From ba84d02a098d236f81be55b83ae3fcadedb8fb48 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Tue, 26 Jul 2022 03:25:01 +0200 Subject: [PATCH] early-access version 2862 --- README.md | 2 +- dist/license.md | 9 + .../colorful/icons/16x16/connected.png | Bin 0 -> 362 bytes .../icons/16x16/connected_notification.png | Bin 0 -> 607 bytes .../colorful/icons/16x16/disconnected.png | Bin 0 -> 784 bytes dist/qt_themes/colorful/style.qrc | 3 + dist/qt_themes/colorful_dark/style.qrc | 4 + dist/qt_themes/default/default.qrc | 4 + .../default/icons/16x16/connected.png | Bin 0 -> 269 bytes .../icons/16x16/connected_notification.png | Bin 0 -> 517 bytes .../default/icons/16x16/disconnected.png | Bin 0 -> 306 bytes .../default/icons/48x48/no_avatar.png | Bin 0 -> 588 bytes .../qdarkstyle/icons/16x16/connected.png | Bin 0 -> 397 bytes .../icons/16x16/connected_notification.png | Bin 0 -> 526 bytes .../qdarkstyle/icons/16x16/disconnected.png | Bin 0 -> 444 bytes .../qdarkstyle/icons/48x48/no_avatar.png | Bin 0 -> 708 bytes dist/qt_themes/qdarkstyle/style.qrc | 4 + externals/CMakeLists.txt | 9 + externals/cpp-jwt/.github/workflows/main.yml | 56 + externals/cpp-jwt/.gitignore | 1 + externals/cpp-jwt/CMakeLists.txt | 115 + externals/cpp-jwt/LICENSE | 21 + externals/cpp-jwt/README.md | 751 + externals/cpp-jwt/cmake/Config.cmake.in | 10 + externals/cpp-jwt/cmake_command | 1 + externals/cpp-jwt/conanfile.txt | 10 + .../simple_ex1.dir/DependInfo.cmake | 22 + .../simple_ex1.dir/cmake_clean.cmake | 10 + .../CMakeFiles/simple_ex1.dir/progress.make | 3 + externals/cpp-jwt/examples/CMakeLists.txt | 24 + .../cpp-jwt/examples/rsa_256/jwtRS256.key | 15 + .../cpp-jwt/examples/rsa_256/jwtRS256.key.pub | 6 + externals/cpp-jwt/examples/simple_ex1.cc | 21 + externals/cpp-jwt/examples/simple_ex2.cc | 48 + externals/cpp-jwt/examples/simple_ex3_rsa.cc | 82 + externals/cpp-jwt/include/jwt/algorithm.hpp | 557 + externals/cpp-jwt/include/jwt/assertions.hpp | 51 + externals/cpp-jwt/include/jwt/base64.hpp | 335 + externals/cpp-jwt/include/jwt/config.hpp | 51 + externals/cpp-jwt/include/jwt/detail/meta.hpp | 236 + externals/cpp-jwt/include/jwt/error_codes.hpp | 136 + externals/cpp-jwt/include/jwt/exceptions.hpp | 305 + .../cpp-jwt/include/jwt/impl/algorithm.ipp | 312 + .../cpp-jwt/include/jwt/impl/error_codes.ipp | 160 + externals/cpp-jwt/include/jwt/impl/jwt.ipp | 882 + .../cpp-jwt/include/jwt/impl/stack_alloc.ipp | 87 + .../cpp-jwt/include/jwt/impl/string_view.ipp | 339 + externals/cpp-jwt/include/jwt/json/json.hpp | 25447 ++++++++++++++++ .../cpp-jwt/include/jwt/json/test_json.cc | 24 + externals/cpp-jwt/include/jwt/jwt.hpp | 1203 + externals/cpp-jwt/include/jwt/parameters.hpp | 451 + .../cpp-jwt/include/jwt/short_string.hpp | 38 + externals/cpp-jwt/include/jwt/stack_alloc.hpp | 200 + externals/cpp-jwt/include/jwt/string_view.hpp | 381 + .../cpp-jwt/include/jwt/test/compile.txt | 1 + .../cpp-jwt/include/jwt/test/test_base64 | Bin 0 -> 42000 bytes .../cpp-jwt/include/jwt/test/test_base64.cc | 48 + externals/cpp-jwt/include/jwt/test/test_evp.c | 45 + externals/cpp-jwt/include/jwt/test/test_hmac | Bin 0 -> 44936 bytes .../cpp-jwt/include/jwt/test/test_hmac.cc | 16 + .../cpp-jwt/include/jwt/test/test_jwt_decode | Bin 0 -> 782988 bytes .../include/jwt/test/test_jwt_decode.cc | 30 + .../cpp-jwt/include/jwt/test/test_jwt_header | Bin 0 -> 727180 bytes .../include/jwt/test/test_jwt_header.cc | 19 + .../cpp-jwt/include/jwt/test/test_jwt_object | Bin 0 -> 912592 bytes .../include/jwt/test/test_jwt_object.cc | 93 + .../cpp-jwt/include/jwt/test/test_jwt_payload | Bin 0 -> 699636 bytes .../include/jwt/test/test_jwt_payload.cc | 34 + .../include/jwt/test/test_jwt_signature | Bin 0 -> 740348 bytes .../include/jwt/test/test_jwt_signature.cc | 25 + externals/cpp-jwt/include/jwt/test/test_rsa | Bin 0 -> 62720 bytes .../cpp-jwt/include/jwt/test/test_rsa.cc | 47 + .../cpp-jwt/include/jwt/test/test_stack_alloc | Bin 0 -> 21516 bytes .../include/jwt/test/test_stack_alloc.cc | 22 + externals/cpp-jwt/include/jwt/test/test_sv | Bin 0 -> 45516 bytes externals/cpp-jwt/include/jwt/test/test_sv.cc | 169 + externals/cpp-jwt/tests/CMakeLists.txt | 69 + .../tests/certs/ec_certs/ec384_priv.pem | 6 + .../tests/certs/ec_certs/ec384_pub.pem | 5 + .../tests/certs/rsa_certs/rsa256_priv.pem | 28 + .../tests/certs/rsa_certs/rsa256_pub.pem | 9 + .../tests/certs/rsa_certs/rsa384_priv.pem | 52 + .../tests/certs/rsa_certs/rsa384_pub.pem | 14 + .../tests/certs/rsa_certs/rsa512_priv.pem | 100 + .../tests/certs/rsa_certs/rsa512_pub.pem | 25 + externals/cpp-jwt/tests/compile.txt | 1 + externals/cpp-jwt/tests/test_jwt_decode.cc | 188 + .../cpp-jwt/tests/test_jwt_decode_verifiy.cc | 196 + .../test_jwt_decode_verifiy_with_exception.cc | 178 + externals/cpp-jwt/tests/test_jwt_encode.cc | 314 + externals/cpp-jwt/tests/test_jwt_es.cc | 149 + externals/cpp-jwt/tests/test_jwt_object.cc | 30 + externals/cpp-jwt/tests/test_jwt_rsa.cc | 145 + externals/cpp-jwt/vcpkg.json | 11 + externals/enet/CMakeLists.txt | 94 + externals/enet/ChangeLog | 179 + externals/enet/Doxyfile | 2303 ++ externals/enet/DoxygenLayout.xml | 191 + externals/enet/LICENSE | 7 + externals/enet/Makefile.am | 22 + externals/enet/README | 15 + externals/enet/callbacks.c | 53 + externals/enet/compress.c | 654 + externals/enet/configure.ac | 28 + externals/enet/docs/FAQ.dox | 24 + externals/enet/docs/design.dox | 126 + externals/enet/docs/install.dox | 63 + externals/enet/docs/license.dox | 26 + externals/enet/docs/mainpage.dox | 59 + externals/enet/docs/tutorial.dox | 366 + externals/enet/enet.dsp | 168 + externals/enet/enet_dll.cbp | 86 + externals/enet/host.c | 492 + externals/enet/include/enet/callbacks.h | 27 + externals/enet/include/enet/enet.h | 607 + externals/enet/include/enet/list.h | 43 + externals/enet/include/enet/protocol.h | 198 + externals/enet/include/enet/time.h | 18 + externals/enet/include/enet/types.h | 13 + externals/enet/include/enet/unix.h | 47 + externals/enet/include/enet/utility.h | 12 + externals/enet/include/enet/win32.h | 57 + externals/enet/libenet.pc.in | 10 + externals/enet/list.c | 75 + externals/enet/m4/.keep | 0 externals/enet/packet.c | 165 + externals/enet/peer.c | 1004 + externals/enet/premake4.lua | 59 + externals/enet/protocol.c | 1913 ++ externals/enet/unix.c | 616 + externals/enet/win32.c | 442 + src/CMakeLists.txt | 1 + src/common/CMakeLists.txt | 1 + src/common/announce_multiplayer_room.h | 143 + src/core/CMakeLists.txt | 14 +- src/core/announce_multiplayer_session.cpp | 164 + src/core/announce_multiplayer_session.h | 98 + src/core/core.cpp | 31 +- src/core/core.h | 10 + .../hid/irsensor/clustering_processor.cpp | 11 +- src/core/hle/service/nifm/nifm.cpp | 4 +- src/core/hle/service/sockets/bsd.cpp | 4 +- src/core/hle/service/sockets/bsd.h | 2 +- .../hle/service/sockets/sockets_translate.cpp | 2 +- .../hle/service/sockets/sockets_translate.h | 2 +- src/core/internal_network/network.cpp | 637 + src/core/internal_network/network.h | 117 + .../internal_network/network_interface.cpp | 209 + src/core/internal_network/network_interface.h | 28 + src/core/internal_network/sockets.h | 95 + src/input_common/helpers/udp_protocol.h | 2 +- src/network/CMakeLists.txt | 16 + src/network/network.cpp | 50 + src/network/network.h | 33 + src/network/packet.cpp | 262 + src/network/packet.h | 165 + src/network/room.cpp | 1110 + src/network/room.h | 151 + src/network/room_member.cpp | 696 + src/network/room_member.h | 318 + src/network/verify_user.cpp | 17 + src/network/verify_user.h | 45 + src/tests/CMakeLists.txt | 2 +- src/tests/core/internal_network/network.cpp | 27 + src/web_service/CMakeLists.txt | 6 +- src/web_service/announce_room_json.cpp | 145 + src/web_service/announce_room_json.h | 41 + src/web_service/verify_user_jwt.cpp | 67 + src/web_service/verify_user_jwt.h | 26 + src/yuzu/CMakeLists.txt | 32 +- src/yuzu/configuration/config.cpp | 75 + src/yuzu/configuration/config.h | 2 + src/yuzu/configuration/configure_dialog.cpp | 8 +- src/yuzu/configuration/configure_dialog.h | 3 +- src/yuzu/configuration/configure_network.cpp | 2 +- src/yuzu/configuration/configure_web.cpp | 5 + src/yuzu/configuration/configure_web.h | 1 + src/yuzu/configuration/configure_web.ui | 10 + src/yuzu/game_list.cpp | 6 + src/yuzu/game_list.h | 8 + src/yuzu/main.cpp | 41 +- src/yuzu/main.h | 5 + src/yuzu/main.ui | 38 + src/yuzu/multiplayer/chat_room.cpp | 491 + src/yuzu/multiplayer/chat_room.h | 75 + src/yuzu/multiplayer/chat_room.ui | 59 + src/yuzu/multiplayer/client_room.cpp | 115 + src/yuzu/multiplayer/client_room.h | 39 + src/yuzu/multiplayer/client_room.ui | 80 + src/yuzu/multiplayer/direct_connect.cpp | 130 + src/yuzu/multiplayer/direct_connect.h | 43 + src/yuzu/multiplayer/direct_connect.ui | 168 + src/yuzu/multiplayer/host_room.cpp | 246 + src/yuzu/multiplayer/host_room.h | 75 + src/yuzu/multiplayer/host_room.ui | 207 + src/yuzu/multiplayer/lobby.cpp | 367 + src/yuzu/multiplayer/lobby.h | 128 + src/yuzu/multiplayer/lobby.ui | 123 + src/yuzu/multiplayer/lobby_p.h | 238 + src/yuzu/multiplayer/message.cpp | 78 + src/yuzu/multiplayer/message.h | 64 + src/yuzu/multiplayer/moderation_dialog.cpp | 112 + src/yuzu/multiplayer/moderation_dialog.h | 43 + src/yuzu/multiplayer/moderation_dialog.ui | 84 + src/yuzu/multiplayer/state.cpp | 308 + src/yuzu/multiplayer/state.h | 92 + src/yuzu/multiplayer/validation.h | 48 + src/yuzu/uisettings.h | 13 + src/yuzu/util/clickable_label.cpp | 11 + src/yuzu/util/clickable_label.h | 21 + src/yuzu_cmd/yuzu.cpp | 158 + 211 files changed, 53330 insertions(+), 31 deletions(-) create mode 100755 dist/qt_themes/colorful/icons/16x16/connected.png create mode 100755 dist/qt_themes/colorful/icons/16x16/connected_notification.png create mode 100755 dist/qt_themes/colorful/icons/16x16/disconnected.png create mode 100755 dist/qt_themes/default/icons/16x16/connected.png create mode 100755 dist/qt_themes/default/icons/16x16/connected_notification.png create mode 100755 dist/qt_themes/default/icons/16x16/disconnected.png create mode 100755 dist/qt_themes/default/icons/48x48/no_avatar.png create mode 100755 dist/qt_themes/qdarkstyle/icons/16x16/connected.png create mode 100755 dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png create mode 100755 dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png create mode 100755 dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.png create mode 100755 externals/cpp-jwt/.github/workflows/main.yml create mode 100755 externals/cpp-jwt/.gitignore create mode 100755 externals/cpp-jwt/CMakeLists.txt create mode 100755 externals/cpp-jwt/LICENSE create mode 100755 externals/cpp-jwt/README.md create mode 100755 externals/cpp-jwt/cmake/Config.cmake.in create mode 100755 externals/cpp-jwt/cmake_command create mode 100755 externals/cpp-jwt/conanfile.txt create mode 100755 externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/DependInfo.cmake create mode 100755 externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/cmake_clean.cmake create mode 100755 externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/progress.make create mode 100755 externals/cpp-jwt/examples/CMakeLists.txt create mode 100755 externals/cpp-jwt/examples/rsa_256/jwtRS256.key create mode 100755 externals/cpp-jwt/examples/rsa_256/jwtRS256.key.pub create mode 100755 externals/cpp-jwt/examples/simple_ex1.cc create mode 100755 externals/cpp-jwt/examples/simple_ex2.cc create mode 100755 externals/cpp-jwt/examples/simple_ex3_rsa.cc create mode 100755 externals/cpp-jwt/include/jwt/algorithm.hpp create mode 100755 externals/cpp-jwt/include/jwt/assertions.hpp create mode 100755 externals/cpp-jwt/include/jwt/base64.hpp create mode 100755 externals/cpp-jwt/include/jwt/config.hpp create mode 100755 externals/cpp-jwt/include/jwt/detail/meta.hpp create mode 100755 externals/cpp-jwt/include/jwt/error_codes.hpp create mode 100755 externals/cpp-jwt/include/jwt/exceptions.hpp create mode 100755 externals/cpp-jwt/include/jwt/impl/algorithm.ipp create mode 100755 externals/cpp-jwt/include/jwt/impl/error_codes.ipp create mode 100755 externals/cpp-jwt/include/jwt/impl/jwt.ipp create mode 100755 externals/cpp-jwt/include/jwt/impl/stack_alloc.ipp create mode 100755 externals/cpp-jwt/include/jwt/impl/string_view.ipp create mode 100755 externals/cpp-jwt/include/jwt/json/json.hpp create mode 100755 externals/cpp-jwt/include/jwt/json/test_json.cc create mode 100755 externals/cpp-jwt/include/jwt/jwt.hpp create mode 100755 externals/cpp-jwt/include/jwt/parameters.hpp create mode 100755 externals/cpp-jwt/include/jwt/short_string.hpp create mode 100755 externals/cpp-jwt/include/jwt/stack_alloc.hpp create mode 100755 externals/cpp-jwt/include/jwt/string_view.hpp create mode 100755 externals/cpp-jwt/include/jwt/test/compile.txt create mode 100755 externals/cpp-jwt/include/jwt/test/test_base64 create mode 100755 externals/cpp-jwt/include/jwt/test/test_base64.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_evp.c create mode 100755 externals/cpp-jwt/include/jwt/test/test_hmac create mode 100755 externals/cpp-jwt/include/jwt/test/test_hmac.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_decode create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_decode.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_header create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_header.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_object create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_object.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_payload create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_payload.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_signature create mode 100755 externals/cpp-jwt/include/jwt/test/test_jwt_signature.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_rsa create mode 100755 externals/cpp-jwt/include/jwt/test/test_rsa.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_stack_alloc create mode 100755 externals/cpp-jwt/include/jwt/test/test_stack_alloc.cc create mode 100755 externals/cpp-jwt/include/jwt/test/test_sv create mode 100755 externals/cpp-jwt/include/jwt/test/test_sv.cc create mode 100755 externals/cpp-jwt/tests/CMakeLists.txt create mode 100755 externals/cpp-jwt/tests/certs/ec_certs/ec384_priv.pem create mode 100755 externals/cpp-jwt/tests/certs/ec_certs/ec384_pub.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa256_priv.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa256_pub.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa384_priv.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa384_pub.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa512_priv.pem create mode 100755 externals/cpp-jwt/tests/certs/rsa_certs/rsa512_pub.pem create mode 100755 externals/cpp-jwt/tests/compile.txt create mode 100755 externals/cpp-jwt/tests/test_jwt_decode.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_decode_verifiy.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_decode_verifiy_with_exception.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_encode.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_es.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_object.cc create mode 100755 externals/cpp-jwt/tests/test_jwt_rsa.cc create mode 100755 externals/cpp-jwt/vcpkg.json create mode 100755 externals/enet/CMakeLists.txt create mode 100755 externals/enet/ChangeLog create mode 100755 externals/enet/Doxyfile create mode 100755 externals/enet/DoxygenLayout.xml create mode 100755 externals/enet/LICENSE create mode 100755 externals/enet/Makefile.am create mode 100755 externals/enet/README create mode 100755 externals/enet/callbacks.c create mode 100755 externals/enet/compress.c create mode 100755 externals/enet/configure.ac create mode 100755 externals/enet/docs/FAQ.dox create mode 100755 externals/enet/docs/design.dox create mode 100755 externals/enet/docs/install.dox create mode 100755 externals/enet/docs/license.dox create mode 100755 externals/enet/docs/mainpage.dox create mode 100755 externals/enet/docs/tutorial.dox create mode 100755 externals/enet/enet.dsp create mode 100755 externals/enet/enet_dll.cbp create mode 100755 externals/enet/host.c create mode 100755 externals/enet/include/enet/callbacks.h create mode 100755 externals/enet/include/enet/enet.h create mode 100755 externals/enet/include/enet/list.h create mode 100755 externals/enet/include/enet/protocol.h create mode 100755 externals/enet/include/enet/time.h create mode 100755 externals/enet/include/enet/types.h create mode 100755 externals/enet/include/enet/unix.h create mode 100755 externals/enet/include/enet/utility.h create mode 100755 externals/enet/include/enet/win32.h create mode 100755 externals/enet/libenet.pc.in create mode 100755 externals/enet/list.c create mode 100755 externals/enet/m4/.keep create mode 100755 externals/enet/packet.c create mode 100755 externals/enet/peer.c create mode 100755 externals/enet/premake4.lua create mode 100755 externals/enet/protocol.c create mode 100755 externals/enet/unix.c create mode 100755 externals/enet/win32.c create mode 100755 src/common/announce_multiplayer_room.h create mode 100755 src/core/announce_multiplayer_session.cpp create mode 100755 src/core/announce_multiplayer_session.h create mode 100755 src/core/internal_network/network.cpp create mode 100755 src/core/internal_network/network.h create mode 100755 src/core/internal_network/network_interface.cpp create mode 100755 src/core/internal_network/network_interface.h create mode 100755 src/core/internal_network/sockets.h create mode 100755 src/network/CMakeLists.txt create mode 100755 src/network/network.cpp create mode 100755 src/network/network.h create mode 100755 src/network/packet.cpp create mode 100755 src/network/packet.h create mode 100755 src/network/room.cpp create mode 100755 src/network/room.h create mode 100755 src/network/room_member.cpp create mode 100755 src/network/room_member.h create mode 100755 src/network/verify_user.cpp create mode 100755 src/network/verify_user.h create mode 100755 src/tests/core/internal_network/network.cpp create mode 100755 src/web_service/announce_room_json.cpp create mode 100755 src/web_service/announce_room_json.h create mode 100755 src/web_service/verify_user_jwt.cpp create mode 100755 src/web_service/verify_user_jwt.h create mode 100755 src/yuzu/multiplayer/chat_room.cpp create mode 100755 src/yuzu/multiplayer/chat_room.h create mode 100755 src/yuzu/multiplayer/chat_room.ui create mode 100755 src/yuzu/multiplayer/client_room.cpp create mode 100755 src/yuzu/multiplayer/client_room.h create mode 100755 src/yuzu/multiplayer/client_room.ui create mode 100755 src/yuzu/multiplayer/direct_connect.cpp create mode 100755 src/yuzu/multiplayer/direct_connect.h create mode 100755 src/yuzu/multiplayer/direct_connect.ui create mode 100755 src/yuzu/multiplayer/host_room.cpp create mode 100755 src/yuzu/multiplayer/host_room.h create mode 100755 src/yuzu/multiplayer/host_room.ui create mode 100755 src/yuzu/multiplayer/lobby.cpp create mode 100755 src/yuzu/multiplayer/lobby.h create mode 100755 src/yuzu/multiplayer/lobby.ui create mode 100755 src/yuzu/multiplayer/lobby_p.h create mode 100755 src/yuzu/multiplayer/message.cpp create mode 100755 src/yuzu/multiplayer/message.h create mode 100755 src/yuzu/multiplayer/moderation_dialog.cpp create mode 100755 src/yuzu/multiplayer/moderation_dialog.h create mode 100755 src/yuzu/multiplayer/moderation_dialog.ui create mode 100755 src/yuzu/multiplayer/state.cpp create mode 100755 src/yuzu/multiplayer/state.h create mode 100755 src/yuzu/multiplayer/validation.h create mode 100755 src/yuzu/util/clickable_label.cpp create mode 100755 src/yuzu/util/clickable_label.h diff --git a/README.md b/README.md index e00a12793..ab2f30bf5 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2859. +This is the source code for early-access 2862. ## Legal Notice diff --git a/dist/license.md b/dist/license.md index 7bdebfec1..c5ebe9c00 100755 --- a/dist/license.md +++ b/dist/license.md @@ -3,6 +3,9 @@ The icons in this folder and its subfolders have the following licenses: Icon Name | License | Origin/Author --- | --- | --- qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/16x16/disconnected.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io @@ -10,18 +13,24 @@ qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8. qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/48x48/no_avatar.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/48x48/no_avatar.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/colorful/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/colorful/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com diff --git a/dist/qt_themes/colorful/icons/16x16/connected.png b/dist/qt_themes/colorful/icons/16x16/connected.png new file mode 100755 index 0000000000000000000000000000000000000000..d6052f1a09a9a828bf8d43ad1827d4d9d9cbae0f GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!T+8vjv*HQ$r4owhbC=p{o8&0tK-fV$N!7I zG75{Wk2Xy_x$CCz^fmhIm;O7pIV>$~UU0|jc-Os|kq&3=6=xmz@v22N`_uxjg657l zK`STRITf1dYARnXzHVnz%7Op?j%KGjO-*Ef=z4Y2!JVP;O`o&4nm=o8Um(r&<$utM zpf?GRCYAmx4Sx2X_vGqd?TH*0tQ9I(1o>RpUa?m2%z2>1TX%C75w(D&HyAH45$5j4PH{Kc+44&>bodk5m->}@RC7fLxAcI z#!1f(d3s;Z@QK)^x266fTk#5$LAl}+;~l5Q zvTbMg9^UuieCIpx-&Ifi=FyAs#93blXAAVk6T1cLkfNhvWKhv_s&Zmhz^+*5hT57% zOF_;n8U!$wS#BAtE4F_}x9_uN3h#||R;qIF^}7#eT+d|uz94mKnfhXH31H9huIfo8 zi^zay+&fR64K1E@-JxlR%=;^#xv6PIV?+G_s+UCMg=h4^)8}bd)f^GE)~e{qV!srN z3)gS0wp1Shp|S7zWIB^sFUGWeiInyCIgX))T9mw`_&DPPf>r?`06RRtwXvb@^0G)- zvhTjLk|#o z6b%F40BRWfRG*xU)HfORdVHeMwzg(YsI)*YB#UnSgU(w<`ka z({=BSp$~)N!i*09^m-ixwy0%@^JLsMc);uRIsiZ) zuLI9}Ao~5H-7!{nzHnbt1h`(`n!P@CZF^;XOBDdj2{VlYef>Elg$2zM_TEx)f%gM| zUZ=yD!}+%K$gbM}Kq7aFCTBFu)Nxkps?w_aZPdD~J>>xKvH4(vCB=E_n|RDx~z-v`(u{Ck(>^ z0K?FTjg5ud&4Z?C%4}L$RQtHS76>VRIGUfI`z*_{6mBpK4XsuKH!pyuX^Kna4xOuM z+^h(UdmYV_Q&T;Qn}iUk)hg+yeWcSi;>fQo&hJ(N2*<9-;|zukPUl1ymtfdJ7=}o) zEICHUE|eYJU8xid7X<*9>zeX|uHNTr)5hE=LNY|r-*L8JZ`1$60Pq`<)BU@nD5;_V O0000 icons/index.theme + icons/16x16/connected.png + icons/16x16/connected_notification.png + icons/16x16/disconnected.png icons/16x16/lock.png icons/48x48/bad_folder.png icons/48x48/chip.png diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc index 0abcb4e83..602c71b32 100755 --- a/dist/qt_themes/colorful_dark/style.qrc +++ b/dist/qt_themes/colorful_dark/style.qrc @@ -1,11 +1,15 @@ + ../colorful/icons/16x16/connected.png + ../colorful/icons/16x16/connected_notification.png + ../colorful/icons/16x16/disconnected.png icons/index.theme icons/16x16/lock.png icons/16x16/view-refresh.png ../colorful/icons/48x48/bad_folder.png ../colorful/icons/48x48/chip.png ../colorful/icons/48x48/folder.png + ../qdarkstyle/icons/48x48/no_avatar.png ../colorful/icons/48x48/plus.png ../colorful/icons/48x48/sd_card.png ../colorful/icons/256x256/plus_folder.png diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index b195747a3..41842e5d8 100755 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -4,10 +4,14 @@ icons/16x16/checked.png icons/16x16/failed.png icons/16x16/lock.png + icons/16x16/connected.png + icons/16x16/disconnected.png + icons/16x16/connected_notification.png icons/16x16/view-refresh.png icons/48x48/bad_folder.png icons/48x48/chip.png icons/48x48/folder.png + icons/48x48/no_avatar.png icons/48x48/plus.png icons/48x48/sd_card.png icons/48x48/star.png diff --git a/dist/qt_themes/default/icons/16x16/connected.png b/dist/qt_themes/default/icons/16x16/connected.png new file mode 100755 index 0000000000000000000000000000000000000000..afa7973948c2fd69a5b838ebc993a4a618e20e31 GIT binary patch literal 269 zcmV+o0rLKdP)wVS)a5(TtL3F*6^H+fmi83={^g~HAo7u1ZbyiW#=5CU=?7R{1!Zbe$F9tyMfYy zZs6*qt^z68Vfj6G&{i5N*B8S8soODKE0Ee2()LDxTpa4zWdu+_YwLCwvHtx4JVP#E TIr_f000000NkvXXu0mjf{AX#) literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/16x16/connected_notification.png b/dist/qt_themes/default/icons/16x16/connected_notification.png new file mode 100755 index 0000000000000000000000000000000000000000..e64901378b000db1871d03db88af5f06c454cb01 GIT binary patch literal 517 zcmV+g0{Z=lP)=oH5kv??W6KBf~W^uD5aJbuU z?1_l^zTebLSsabzI+llOtNV%$o|VX}coVWS{*HQ<)uxI9Ck6S4=9S_D*7>#60 zKOeWZFLYif{X3q+e8sGmn$7ygk(FzSW2Em%_GsHe=P~>O_ZNK92NF;*00000NkvXX Hu0mjf^1s=j literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/16x16/disconnected.png b/dist/qt_themes/default/icons/16x16/disconnected.png new file mode 100755 index 0000000000000000000000000000000000000000..835b1f0d6b5ceeb11e9a2b7d68c61521aef7bcb8 GIT binary patch literal 306 zcmV-20nPr2P)au7)Yo#YK5^hRAPlWv+5i9m07*qoM6N<$ Ef=+yWl>h($ literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/48x48/no_avatar.png b/dist/qt_themes/default/icons/48x48/no_avatar.png new file mode 100755 index 0000000000000000000000000000000000000000..d4bf82026a63d3498c57615ed744ef6ad1a11db4 GIT binary patch literal 588 zcmV-S0<-;zP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0pUqRK~!i%?bts^ z)L|UQ@k_%$K?EW+2x>Uh5Tcv9ID|t(AUGrhij56zIR}A+gAO9Jr08b!2O=6m2Zt72 zf~GJKrJ<=1OR>`Tcl&XCxO<+v^S%%9d&6t}-N*C({O&2_<>l49c1+H(cMu9ry_~F2o$%>&5}pz)}{_h$OO) zLp8FLVI))R7uq*mL^55%F4S1%5t8c`j-du;eT4mvPO?u>qqAIZpUbrkH99NlKOp2k za39IG12sD9A(E^gf1yTa*^%P~B-H@+pav_?k;D;W4;B&f%onro4Lgyj`$wvsFyF>I zR5HBScRWES&NX|c9^fm~kZReX?+f&yrr8^A68lgOTx)jR_zGR9Iy?7YgLmcf a6$)pIccPOKD6@6|0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0U}96K~y+TrIbBN z15p%(2ThUA!elK%1O;&sLOKPvAQqN^S%76$5m9U;Tacs`0+KRKh!$yP^1YdppLy@j zBbpBm?{V)rhhg%Q973Mw%`D4aP+z0Jqmp;@i!L;dL#Tnd!+DKbUdg3waWGz?Z^8u% zk9EdN@ZV$lKwY#fE2=PmlHCTw9lAj?WlY$Q6tTfDbS3CER>p3rhz*9WjL|aY6GcK; zx)-~7C$0pUWy~HGtHREG#M9EY#VX_e#&N7@6_#hEEo8S0El^vr5SHi4nW$v_WT|2y zEKe}ain4kx)4O#2)N;bb8 r=102j2jPKV@-HZOM*IDr)RH7C%sED}gz8E800000NkvXXu0mjf2Q8tG literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png b/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png new file mode 100755 index 0000000000000000000000000000000000000000..7cd8b9d2930d99340778360c68bf28923a66609c GIT binary patch literal 526 zcmV+p0`dKcP)2WpUc5F_z--H^xh8V++PPa z33XEKY?gdgT~jBj|8aa2I1da0uYrTWBVZ~bo|TbKsi966PC1e~wUOg4^}G7fa*Xuu zD}ChCz!%^SaA1RG7s}j2{h+R>H`N~{31-R!rn93J^+qAGq+VB#t4(!Zk5kRaL%_XE zZwD|7j010%$4)l2G8(}uq%QEa{|BSMKwqd&%+I&Gt@=}pSAmByUBvjj+&Xa?AhHBA zg=3C-cdUL$odY1E5y>+Kw}I7x%KDK!!OK4KI`Cf7rGGvQYAIF5bAb`SY)*g}wJhau z3ikh)8lzs_F#r7`PS@PYUsN&*fWd_-?rE zoG=u!$6)85fB0bU!s5G7mw@{bF;~)+PNPxVI=FT#;4H+?AU)r`u=oPtH#>3~=2X*b QzyJUM07*qoM6N<$g7iq=m;e9( literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png b/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png new file mode 100755 index 0000000000000000000000000000000000000000..fc5f23894e4aedae7a49981f68ba017a177b065b GIT binary patch literal 444 zcmV;t0YmPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z~arK~y+Tm6X3q z!(bG}BM3T)4oat1>3fJGI5_zRqJs}n!QHVSy7&}|Z{Q;C6&zcsljtHSLgMcv$HI{dexom*ghNEXcBKF-_BL>^@v~d;_m6`5%aP;4{rR@W0^`{D7O5#ex19(LFqb zU&V+JY)3s}}a0Qf@czHi&OA^DY-4`2a_->y&b9cID?Mf&*K<#kR|Tpm5}?1j7X4 z4jjTqBrVk)Us_UtgqE$`6a4DQ_TU|*mQ&iX713e2_G4@Sa=K!0vPRIeRkTiGreO$! zH&MmZ!_eyCMZY6~+NXx$F1sZT1bRk;ysHB8+LJc+0^2+Y(KXbspbuX?;02!GhRfCj m8qQ()KfzyOz?{P=kt9FFYiX8^{jL1~0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0$E8!K~!i%?U+4C z98nlWS0ieKAc&Ach>BHGNYF0YqzD!%gn)&DplF%GF4lqk-Z(wLx~!5;{cLSkWI zlQe-;q9BTeRU~K>b=Nb#TM5I?JlFRgG6yc>;`{D*#@}Uf85tR+rBEo;=kxg?@`?Q5 z^-r#wvA0p3&g6)M?Qe5o~yzyh)l{YEq|GP zp&CCRQH#Wx@ktM}>#Fht4n0Q24sIagsp{Or@@ctPLr0hD+{5w(d00b7zv|q>@^A96 zhK@efxrhCqjfOqkK)34L!}1Av+=Z8_a}PH#Bnc~+KjBiTI`<&ZYEtG4KGTZ(fhEyH-1lVHtS&9|Mh|_zk~Xa@4Rn`LI!F%8s{4gK5zC}e3ii-fP0^yM zb_;utiTz_oI-;NTRa55&_MejuDW!w?0aN1_ZeW|F|4YTtA9Z*KmtJWlgMntv1_3?` qQcDIioeb>+lqIQUWMn)Xa=B}!H(avGT(DUH0000 icons/index.theme + icons/16x16/connected.png + icons/16x16/disconnected.png + icons/16x16/connected_notification.png icons/16x16/lock.png icons/16x16/view-refresh.png icons/48x48/bad_folder.png icons/48x48/chip.png icons/48x48/folder.png + icons/48x48/no_avatar.png icons/48x48/plus.png icons/48x48/sd_card.png icons/48x48/star.png diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bd01f4c4d..5b74a1773 100755 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -73,6 +73,10 @@ if (YUZU_USE_EXTERNAL_SDL2) add_library(SDL2 ALIAS SDL2-static) endif() +# ENet +add_subdirectory(enet) +target_include_directories(enet INTERFACE ./enet/include) + # Cubeb if(ENABLE_CUBEB) set(BUILD_TESTS OFF CACHE BOOL "") @@ -112,6 +116,11 @@ if (ENABLE_WEB_SERVICE) if (WIN32) target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32) endif() + + # cpp-jwt + add_library(cpp-jwt INTERFACE) + target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) + target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) endif() # Opus diff --git a/externals/cpp-jwt/.github/workflows/main.yml b/externals/cpp-jwt/.github/workflows/main.yml new file mode 100755 index 000000000..1fa014ee1 --- /dev/null +++ b/externals/cpp-jwt/.github/workflows/main.yml @@ -0,0 +1,56 @@ +name: CMake + +on: [push, pull_request] + +env: + BUILD_TYPE: Debug + CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Debug -DCPP_JWT_USE_VENDORED_NLOHMANN_JSON=off' + VCPKG_ARGUMENTS: 'nlohmann-json openssl gtest' + VCPKG_VERSION: '6be82cfac67649a31d4c3eba56d2fafa9dc6736a' # May 13, 2022 + +jobs: + build: + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - { + name: 'Windows/2019/MSVC-19.30.30528.0', + os: windows-2019, + triplet: x64-windows, + parallel: 2, + } + - { + name: 'MacOSX/11/AppleClang-12.0.5.12050022', + os: macos-11, + triplet: x64-osx, + parallel: 3, + } + - { + name: 'Ubuntu/20.04/GCC-9.3.0', + os: ubuntu-20.04, + triplet: x64-linux, + parallel: 2, + } + + steps: + - uses: actions/checkout@v1 + + - name: Install vcpkg + uses: lukka/run-vcpkg@v7 + with: + vcpkgDirectory: ${{ runner.workspace }}/vcpkg + vcpkgArguments: ${{ env.VCPKG_ARGUMENTS }} + vcpkgGitCommitId: ${{ env.VCPKG_VERSION }} + vcpkgTriplet: ${{ matrix.config.triplet }} + + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -DCMAKE_TOOLCHAIN_FILE=${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake ${{ env.CMAKE_ARGS }} + + - name: Build + run: cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --parallel ${{ matrix.config.parallel }} + + - name: Test + working-directory: ${{ github.workspace }}/build + run: ctest -C ${{ env.BUILD_TYPE }} -T test --parallel ${{ matrix.config.parallel }} --output-on-failure --timeout 200 diff --git a/externals/cpp-jwt/.gitignore b/externals/cpp-jwt/.gitignore new file mode 100755 index 000000000..f3d6549d8 --- /dev/null +++ b/externals/cpp-jwt/.gitignore @@ -0,0 +1 @@ +/build/ \ No newline at end of file diff --git a/externals/cpp-jwt/CMakeLists.txt b/externals/cpp-jwt/CMakeLists.txt new file mode 100755 index 000000000..5e56f7372 --- /dev/null +++ b/externals/cpp-jwt/CMakeLists.txt @@ -0,0 +1,115 @@ +cmake_minimum_required(VERSION 3.14.0) +project(cpp-jwt VERSION 1.5.0) + +option(CPP_JWT_BUILD_EXAMPLES "build examples" ON) +option(CPP_JWT_BUILD_TESTS "build tests" ON) +option(CPP_JWT_USE_VENDORED_NLOHMANN_JSON "use vendored json header" ON) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +# only set compiler flags if we are the main project, otherwise let the main +# project decide on the flags +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" + MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + endif() + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() + +endif() + +find_package(OpenSSL REQUIRED) + +if(NOT CPP_JWT_USE_VENDORED_NLOHMANN_JSON) + find_package(nlohmann_json REQUIRED) +endif() + +# ############################################################################## +# LIBRARY +# ############################################################################## + +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories( + ${PROJECT_NAME} + INTERFACE $ + $) +target_link_libraries(${PROJECT_NAME} INTERFACE OpenSSL::SSL) +if(NOT CPP_JWT_USE_VENDORED_NLOHMANN_JSON) + target_link_libraries(${PROJECT_NAME} INTERFACE nlohmann_json::nlohmann_json) +else() + target_compile_definitions(${PROJECT_NAME} INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) +endif() +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_14) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# ############################################################################## +# TESTS +# ############################################################################## + +if(CPP_JWT_BUILD_TESTS) + find_package(GTest REQUIRED) + include_directories(${GTEST_INCLUDE_DIRS}) + enable_testing() + # Recurse into the "Hello" and "Demo" subdirectories. This does not actually + # cause another cmake executable to run. The same process will walk through + # the project's entire directory structure. + add_subdirectory(tests) +endif() + +# ############################################################################## +# EXAMPLES +# ############################################################################## + +if(CPP_JWT_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +# ############################################################################## +# INSTALL +# ############################################################################## + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +set(CPP_JWT_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install( + EXPORT ${PROJECT_NAME}Targets + DESTINATION ${CPP_JWT_CONFIG_INSTALL_DIR} + NAMESPACE ${PROJECT_NAME}:: + COMPONENT dev) +configure_package_config_file(cmake/Config.cmake.in ${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION ${CPP_JWT_CONFIG_INSTALL_DIR} + NO_SET_AND_CHECK_MACRO) +write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake + COMPATIBILITY SameMajorVersion + ARCH_INDEPENDENT) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${CPP_JWT_CONFIG_INSTALL_DIR} + COMPONENT dev) + +if(NOT CPP_JWT_USE_VENDORED_NLOHMANN_JSON) + set(CPP_JWT_VENDORED_NLOHMANN_JSON_INSTALL_PATTERN PATTERN "json" EXCLUDE) +endif() +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/jwt/ + DESTINATION include/jwt + COMPONENT dev + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.ipp" + PATTERN "test" EXCLUDE + ${CPP_JWT_VENDORED_NLOHMANN_JSON_INSTALL_PATTERN}) diff --git a/externals/cpp-jwt/LICENSE b/externals/cpp-jwt/LICENSE new file mode 100755 index 000000000..59be90a87 --- /dev/null +++ b/externals/cpp-jwt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/externals/cpp-jwt/README.md b/externals/cpp-jwt/README.md new file mode 100755 index 000000000..57692632c --- /dev/null +++ b/externals/cpp-jwt/README.md @@ -0,0 +1,751 @@ +

CPP-JWT

+ +
+ A C++14 library for JSON Web Tokens(JWT) +
+ +
+ +
+ +
+ +
+ +
+ + A little library built with lots of ❤︎ for working with JWT easier. + By Arun Muralidharan. + +
+ +## Table of Contents +- [What is it](#what-is-it) +- [Example](#example) +- [API Philosophy](#api-philosophy) +- [Support](#support) +- [External Dependencies](#external-dependencies) +- [Thanks to...](#thanks-to...) +- [Compiler Support](#compiler-support) +- [Installation](#installation) +- [Parameters](#parameters) +- [Claim Data Types](#claim-data-types) +- [Advanced Examples](#advanced-examples) +- [Error Codes & Exceptions](#error-codes-&-exceptions) +- [Additional Header Data](#additional-header-data) +- [Things for improvement](#things-for-improvement) +- [LICENSE](#license) + + +## What is it ? +For the uninitiated, JSON Web Token(JWT) is a JSON based standard (RFC-7519) for creating assertions or access tokens that consists of some claims (encoded within the assertion). +This assertion can be used in some kind of bearer authentication mechanism that the server will provide to clients, and the clients can make use of the provided assertion for accessing resources. + +Few good resources on this material which I found useful are: + Anatomy of JWT + Learn JWT + RFC 7519 + + +## Example + Lets dive into see a simple example of encoding and decoding in Python. Taking the example of pyjwt module from its docs. + + ```python + >>import jwt + >>key = 'secret' + >> + >>encoded = jwt.encode({'some': 'payload'}, key, algorithm='HS256') + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' + >> + >>decoded = jwt.decode(encoded, key, algorithms='HS256') + {'some': 'payload'} + ``` + + Now, lets look at our C++ code doing the same thing. + ```cpp + #include + #include "jwt/jwt.hpp" + + int main() { + using namespace jwt::params; + + auto key = "secret"; //Secret to use for the algorithm + //Create JWT object + jwt::jwt_object obj{algorithm("HS256"), payload({{"some", "payload"}}), secret(key)}; + + //Get the encoded string/assertion + auto enc_str = obj.signature(); + std::cout << enc_str << std::endl; + + //Decode + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret(key)); + std::cout << dec_obj.header() << std::endl; + std::cout << dec_obj.payload() << std::endl; + + return 0; + } + ``` + + It outputs: + ``` + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg + {"alg":"HS256","typ":"JWT"} + {"some":"payload"} + ``` + + Almost the same API, except for some ugliness here and there. But close enough! + + Lets take another example in which we will see to add payload claim having type other than string. + The payload function used in the above example to create jwt_object object can only take strings. For anything else, it will throw a compilation error. + + For adding claims having values other than string, jwt_object class provides add_claim API. We will also see few other APIs in the next example. Make sure to read the comments :). + + ```cpp + #include + #include + #include + #include "jwt/jwt.hpp" + + int main() { + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"user", "admin"}})}; + + //Use add_claim API to add claim values which are + // _not_ strings. + // For eg: `iat` and `exp` claims below. + // Other claims could have been added in the payload + // function above as they are just stringy things. + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "test") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10}) + ; + + //Use `has_claim` to check if the claim exists or not + assert (obj.has_claim("iss")); + assert (obj.has_claim("exp")); + + //Use `has_claim_with_value` to check if the claim exists + //with a specific value or not. + assert (obj.payload().has_claim_with_value("id", "a-b-c-d-e-f-1-2-3")); + assert (obj.payload().has_claim_with_value("iat", 1513862371)); + + //Remove a claim using `remove_claim` API. + //Most APIs have an overload which takes enum class type as well + //It can be used interchangeably with strings. + obj.remove_claim(jwt::registered_claims::expiration); + assert (!obj.has_claim("exp")); + + //Using `add_claim` with extra features. + //Check return status and overwrite + bool ret = obj.payload().add_claim("sub", "new test", false/*overwrite*/); + assert (!ret); + + // Overwrite an existing claim + ret = obj.payload().add_claim("sub", "new test", true/*overwrite*/); + assert (ret); + + assert (obj.payload().has_claim_with_value("sub", "new test")); + + return 0; + } + ``` + +The jwt_object class is basically a composition of the JWT component classes, which are jwt_header & jwt_payload. For convenience jwt_object exposes only few important APIs to the user, the remaining APIs under jwt_header and jwt_payload can be accessed by calling jwt_object::header() and jwt_object::payload() APIs. + + +## API Philosophy +I wanted to make the code easy to read and at the same time make most of the standard library and the modern features. +It also uses some metaprogramming tricks to enforce type checks and give better error messages. + +The design of `parameters` alleviates the pain of remembering positional arguments. Also makes the APIs more extensible for future enhancements. + +The library has 2 sets of APIs for encoding and decoding: + - API which takes an instance of std::error_code + These APIs will report the errors by setting the `error_code`. This does not mean that these API would not throw. Memory allocation errors would still be thrown instead of setting the error_code. + - API which throws exceptions + All the errors would be thrown as exception. + +## Support +Algorithms and features supported +- [x] HS256 +- [x] HS384 +- [x] HS512 +- [x] RS256 +- [x] RS384 +- [x] RS512 +- [x] ES256 +- [x] ES384 +- [x] ES512 +- [x] Sign +- [x] Verify +- [x] iss (issuer) check +- [x] sub (subject) check +- [x] aud (audience) check +- [x] exp (expiration time) check +- [x] nbf (not before time) check +- [x] iat (issued at) check +- [x] jti (JWT id) check +- [x] JWS header addition support. For eg "kid" support. + +## External Dependencies + - OpenSSL (Version >= 1.0.2j) + Might work with older version as well, but I did not check that. + - Google Test Framework + For running the tests + - nlohmann JSON library + The awesome JSON library :) + +## Thanks to... + - ben-collins JWT library + - Howard Hinnant for the stack allocator + - libstd++ code (I took the hashing code for string_view) + +## Compiler Support + +Tested with clang-5.0 and g++-6.4. +With issue#12, VS2017 is also supported. + +## Building the library + +### using conan + +```shell +mkdir build +cd build +conan install .. --build missing +cmake .. +cmake --build . -j +``` + +### using debian + +```shell +sudo apt install nlohmann-json3-dev +sudo apt install libgtest-dev +sudo apt install libssl-dev +mkdir build +cd build +cmake .. +cmake --build . -j +``` + +## Consuming the library + +This library is uses cmake as a build system. +```cmake +# you can use cmake's `find_package` after installation or `add_subdirectory` when vendoring this repository + +find_package(cpp-jwt REQUIRED) +# or +add_subdirectory(third_party/cpp-jwt) + +add_executable(main main.cpp) +target_link_libraries(main cpp-jwt::cpp-jwt) +``` + +You can also use this library as a conan package, its available in the [conan center](https://conan.io/center/cpp-jwt): +just add `cpp-jwt[>=1.2]` to your conanfile.txt. + +It can also be installed using [vcpkg](https://github.com/microsoft/vcpkg) by adding `"cpp-jwt"` to the dependencies in your `vcpkg.json` file. + +## Parameters +There are two sets of parameters which can be used for creating `jwt_object` and for decoding. +All the parameters are basically a function which returns an instance of a type which are modelled after ParameterConcept (see jwt::detail::meta::is_parameter_concept). + + +- jwt_object creation parameters + - payload + + Used to populate the claims while creating the `jwt_object` instance. + + There are two overloads of this function: + - Takes Initializer list of pair + + Easy to pass claims with string values which are all known at the time of object creation. + Can be used like: + ```cpp + jwt_object obj { + payload({ + {"iss", "some-guy"}, + {"sub", "something"}, + {"X-pld", "data1"} + }), + ... // Add other parameters + }; + ``` + Claim values which are not strings/string_views cannot be used. + + - Takes any type which models MappingConcept (see detail::meta::is_mapping_concept) + + This overload can accept std::map or std::unordered_map like containers. + Can be used like: + ```cpp + map m; + m["iss"] = "some-guy"; + m["sub"] = "something"; + m["X-pld"] = "data1"; + + jwt_object obj{ + payload(std::move(m)), + ... // Add other parameters + }; + //OR + jwt_object obj{ + payload(m), + ... // Add other parameters + }; + ``` + + - secret + + Used to pass the key which could be some random string or the bytes of the PEM encoded public key + file in PEM format (wrapped in -----BEGIN PUBLIC KEY----- block) as string. + The passed string type must be convertible to jwt::string_view + + - algorithm + + Used to pass the type of algorithm to use for encoding. + There are two overloads of this function: + - Takes jwt::string_view + + Can pass the algorithm value in any case. It is case agnostic. + + - Takes value of type enum class jwt::algorithm + + - headers + + Used to populate fields in JWT header. It is very similar to `payload` function parameter. + There are two overloads for this function which are similar to how payload function is. + This parameter can be used to add headers other that alg and typ. + + Same as the case with payload, only string values can be used with this. For adding values of other + data types, use add_header API of jwt_header class. + + For example adding `kid` header with other additional data fields. + ```cpp + jwt_object obj{ + algorithm("HS256"), + headers({ + {"kid", "12-34-56"}, + {"xtra", "header"} + }) + ... // Add other parameters + }; + ``` + + +- Decoding parameters + + - algorithms + + This is a mandatory parameter which takes a sequence of algorithms (as string) which the user would like to permit when validating the JWT. The value in the header for "alg" would be matched against the provided sequence of values. If nothing matches InvalidAlgorithmError exception or InvalidAlgorithm error would be set based upon the API being used. + + There are two overloads for this function: + - Takes initializer-list of string values + - Takes in any type which satifies the SequenceConcept (see idetail::meta::is_sequence_concept) + + ```cpp + jwt::decode(algorithms({"none", "HS256", "RS256"}), ...); + + OR + + std::vector algs{"none", "HS256", "RS256"}; + jwt::decode(algorithms(algs), ...); + ``` + + - secret + + Optional parameter. To be supplied only when the algorithm used is not "none". Else would throw/set KeyNotPresentError / KeyNotPresent exception/error. + + - leeway + + Optional parameter. Used with validation of "Expiration" and "Not Before" claims. + The value passed should be `seconds` to account for clock skew. + Default value is `0` seconds. + + - verify + + Optional parameter. Suggests if verification of claims should be done or not. + Takes a boolean value. + By default verification is turned on. + + - issuer + + Optional parameter. + Takes a string value. + Validates the passed issuer value against the one present in the decoded JWT object. If the values do not match InvalidIssuerError or InvalidIssuer exception or error_code is thrown/set. + + - aud + + Optional parameter. + Takes a string value. + Validates the passed audience value against the one present in the decoded JWT object. If the values do not match InvalidAudienceError or InvalidAudience exception or error_code is thrown/set. + + - sub + + Optional parameter. + Takes a string value. + Validates the passed subject value against the one present in the decoded JWT object. If the values do not match InvalidSubjectError or InvalidSubject exception or error_code is thrown/set. + + - validate_iat + + Optional parameter. + Takes a boolean value. + Validates the IAT claim. Only checks whether the field is present and is of correct type. If not throws/sets InvalidIATError or InvalidIAT. + + Default value is false. + + - validate_jti + + Optional parameter. + Takes a boolean value. + Validates the JTI claim. Only checks for the presence of the claim. If not throws or sets InvalidJTIError or InvalidJTI. + + Default is false. + + +## Claim Data Types +For the registered claim types the library assumes specific data types for the claim values. Using anything else is not supported and would result in runtime JSON parse error. + + Claim | Data Type + ----------------------------------- + Expiration(exp) | uint64_t (Epoch time in seconds) + ----------------------------------- + Not Before(nbf) | uint64_t (Epoch time in seconds) + ----------------------------------- + Issuer(iss) | string + ----------------------------------- + Audience(aud) | string + ----------------------------------- + Issued At(iat) | uint64_t (Epoch time in seconds) + ----------------------------------- + Subject(sub) | string + ----------------------------------- + JTI(jti) | + ----------------------------------- + + +## Advanced Examples +We will see few complete examples which makes use of error code checks and exception handling. +The examples are taken from the "tests" section. Users are requested to checkout the tests to find out more ways to use this library. + +Expiration verification example (uses error_code): +```cpp +#include +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + assert (!ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), verify(true)); + assert (ec); + assert (ec.value() == static_cast(jwt::VerificationErrc::TokenExpired)); + + return 0; +} +``` + +Expiration verification example (uses exception): +```cpp +#include +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + auto enc_str = obj.signature(); + + try { + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), verify(true)); + } catch (const jwt::TokenExpiredError& e) { + //Handle Token expired exception here + //... + } catch (const jwt::SignatureFormatError& e) { + //Handle invalid signature format error + //... + } catch (const jwt::DecodeError& e) { + //Handle all kinds of other decode errors + //... + } catch (const jwt::VerificationError& e) { + // Handle the base verification error. + //NOTE: There are other derived types of verification errors + // which will be discussed in next topic. + } catch (...) { + std::cerr << "Caught unknown exception\n"; + } + + return 0; +} +``` + +Invalid issuer test(uses error_code): +```cpp +#include +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + + std::error_code ec; + auto enc_str = obj.signature(ec); + assert (!ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), issuer("arun.muralidharan")); + assert (ec); + + assert (ec.value() == static_cast(jwt::VerificationErrc::InvalidIssuer)); + + return 0; +} +``` + +## Error Codes & Exceptions +The library as we saw earlier supports error reporting via both exceptions and error_code. + +Error codes: + +The error codes are divided into different categories: +- Algorithm Errors + + Used for reporting errors at the time of encoding / signature creation. + ```cpp + enum class AlgorithmErrc + { + SigningErr = 1, + VerificationErr, + KeyNotFoundErr, + NoneAlgorithmUsed, // Not an actual error! + }; + ``` + + NOTE: NoneAlgorithmUsed will be set in the error_code, but it usually should not be treated as a hard error when NONE algorithm is used intentionally. + +- Decode Errors + + Used for reporting errors at the time of decoding. Different categories of decode errors are: + ```cpp + enum class DecodeErrc + { + // No algorithms provided in decode API + EmptyAlgoList = 1, + // The JWT signature has incorrect format + SignatureFormatError, + // The JSON library failed to parse + JsonParseError, + // Algorithm field in header is missing + AlgHeaderMiss, + // Type field in header is missing + TypHeaderMiss, + // Unexpected type field value + TypMismatch, + // Found duplicate claims + DuplClaims, + // Key/Secret not passed as decode argument + KeyNotPresent, + // Key/secret passed as argument for NONE algorithm. + // Not a hard error. + KeyNotRequiredForNoneAlg, + }; + ``` + +- Verification errors + + Used for reporting verification errors when the verification falg is set to true in decode API. + Different categories of decode errors are: + ```cpp + enum class VerificationErrc + { + //Algorithms provided does not match with header + InvalidAlgorithm = 1, + //Token is expired at the time of decoding + TokenExpired, + //The issuer specified does not match with payload + InvalidIssuer, + //The subject specified does not match with payload + InvalidSubject, + //The field IAT is not present or is of invalid type + InvalidIAT, + //Checks for the existence of JTI + //if validate_jti is passed in decode + InvalidJTI, + //The audience specified dowes not match with payload + InvalidAudience, + //Decoded before nbf time + ImmatureSignature, + //Signature match error + InvalidSignature, + // Invalid value type used for known claims + TypeConversionError, + }; + ``` + +Exceptions: +There are exception types created for almost all the error codes above. + +- MemoryAllocationException + + Derived from std::bad_alloc. Thrown for memory allocation errors in OpenSSL C API. + +- SigningError + + Derived from std::runtime_error. Thrown for failures in OpenSSL APIs while signing. + +- DecodeError + + Derived from std::runtime_error. Base class for all decoding related exceptions. + + - SignatureFormatError + + Thrown if the format of the signature is not as expected. + + - KeyNotPresentError + + Thrown if key/secret is not passed in with the decode API if the algorithm used is something other than "none". + +- VerificationError + + Derived from std::runtime_error. Base class exception for all kinds of verification errors. Verification errors are thrown only when the verify decode parameter is set to true. + + - InvalidAlgorithmError + - TokenExpiredError + - InvalidIssuerError + - InvalidAudienceError + - InvalidSubjectError + - InvalidIATError + - InvalidJTIError + - ImmatureSignatureError + - InvalidSignatureError + - TypeConversionError + + NOTE: See the error code section for explanation on above verification errors or checkout exceptions.hpp header for more details. + + +## Additional Header Data +Generally the header consists only of `type` and `algorithm` fields. But there could be a need to add additional header fields. For example, to provide some kind of hint about what algorithm was used to sign the JWT. Checkout JOSE header section in RFC-7515. + +The library provides APIs to do that as well. + +```cpp +#include +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + jwt::jwt_object obj{ + headers({ + {"alg", "none"}, + {"typ", "jwt"}, + }), + payload({ + {"iss", "arun.muralidharan"}, + {"sub", "nsfw"}, + {"x-pld", "not my ex"} + }) + }; + + bool ret = obj.header().add_header("kid", 1234567); + assert (ret); + + ret = obj.header().add_header("crit", std::array{"exp"}); + assert (ret); + + std::error_code ec; + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"none"}), ec, verify(false)); + + // Should not be a hard error in general + assert (ec.value() == static_cast(jwt::AlgorithmErrc::NoneAlgorithmUsed)); +} +``` + + +## Things for improvement +Many things! +Encoding and decoding JWT is fairly a simple task and could be done in a single source file. I have tried my best to get the APIs and design correct in how much ever time I could give for this project. Still, there are quite a few places (or all the places :( ? ) where things are not correct or may not be the best approach. + +With C++, it is pretty easy to go overboard and create something very difficult or something very straightforward (not worth to be a library). My intention was to make a sane library easier for end users to use while also making the life of someone reading the source have fairly good time debugging some issue. + +Things one may have questions about +- There is a string_view implementation. Why not use boost::string_ref ? + + Sorry, I love boost! But, do not want it to be part of dependency. + If you use C++17 or greater `std::string_view` gets used instead and `jwt::string_view` implementation does not get included. + +- You are not using the stack allocator or the shart string anywhere. Why to include it then ? + + I will be using it in few places where I am sure I need not use `std::string` especially in the signing code. + +- Why the complete `nlohmann JSON` is part of your library ? + + Honestly did not know any better way. I know there are ways to use third party github repositories, but I do not know how to do that. Once I figure that out, I may move it out. + +- Am I bound to use `nlohmann JSON` ? Can I use some other JSON library ? + + As of now, ys. You cannot use any other JSON library unless you change the code. I would have liked to provide some adaptors for JSON interface. Perhaps in future, if required. + +- Error codes and exceptions....heh? + + Yeah, I often wonder if that was the right approach. I could have just stuck with error codes and be happy. But that was a good learning time for me. + +- Where to find more about the usage ? + + Checkout the tests. It has examples for all the algorithms which are supported. + +- Support for C++11 seems trivial based on the changes required. Why not support C+11 then ? + + Its 2018 now! If I ever start getting requests to have support for C++11, then I will surely consider it. + +- The Metaprogramming concept checks for Sequence and Mapping looks sad. + + Yeah I know. Just hacked something very basic. + + +## License + +MIT License + +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/externals/cpp-jwt/cmake/Config.cmake.in b/externals/cpp-jwt/cmake/Config.cmake.in new file mode 100755 index 000000000..81389183d --- /dev/null +++ b/externals/cpp-jwt/cmake/Config.cmake.in @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + +if(NOT @CPP_JWT_USE_VENDORED_NLOHMANN_JSON@) + find_package(nlohmann_json REQUIRED) +endif() + +find_package(OpenSSL REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") \ No newline at end of file diff --git a/externals/cpp-jwt/cmake_command b/externals/cpp-jwt/cmake_command new file mode 100755 index 000000000..507352d30 --- /dev/null +++ b/externals/cpp-jwt/cmake_command @@ -0,0 +1 @@ +cmake -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2j -DGTEST_ROOT=$HOME/googletest \ No newline at end of file diff --git a/externals/cpp-jwt/conanfile.txt b/externals/cpp-jwt/conanfile.txt new file mode 100755 index 000000000..b7af82292 --- /dev/null +++ b/externals/cpp-jwt/conanfile.txt @@ -0,0 +1,10 @@ +[requires] +gtest/1.10.0 +nlohmann_json/3.7.0 +openssl/1.1.1d + +[generators] +cmake_find_package +cmake_paths + +[options] diff --git a/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/DependInfo.cmake b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/DependInfo.cmake new file mode 100755 index 000000000..36fabdc1e --- /dev/null +++ b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/DependInfo.cmake @@ -0,0 +1,22 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/Users/amuralid/dev_test/cpp-jwt/examples/simple_ex1.cc" "/Users/amuralid/dev_test/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/simple_ex1.cc.o" + ) +set(CMAKE_CXX_COMPILER_ID "Clang") + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "include" + "/usr/local/Cellar/openssl/1.0.2j/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/cmake_clean.cmake b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/cmake_clean.cmake new file mode 100755 index 000000000..e75088455 --- /dev/null +++ b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/simple_ex1.dir/simple_ex1.cc.o" + "simple_ex1.pdb" + "simple_ex1" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/simple_ex1.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/progress.make b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/progress.make new file mode 100755 index 000000000..abadeb0c3 --- /dev/null +++ b/externals/cpp-jwt/examples/CMakeFiles/simple_ex1.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 1 +CMAKE_PROGRESS_2 = 2 + diff --git a/externals/cpp-jwt/examples/CMakeLists.txt b/externals/cpp-jwt/examples/CMakeLists.txt new file mode 100755 index 000000000..12245d914 --- /dev/null +++ b/externals/cpp-jwt/examples/CMakeLists.txt @@ -0,0 +1,24 @@ +set(CERT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rsa_256") +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -DCERT_ROOT_DIR=\"\\\"${CERT_ROOT_DIR}\\\"\"") + +add_executable(simple_ex1 simple_ex1.cc) +target_link_libraries(simple_ex1 ${PROJECT_NAME}) +add_test( + NAME simple_ex1 + COMMAND ./simple_ex1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(simple_ex2 simple_ex2.cc) +target_link_libraries(simple_ex2 ${PROJECT_NAME}) +add_test( + NAME simple_ex2 + COMMAND ./simple_ex2 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(simple_ex3_rsa simple_ex3_rsa.cc) +target_link_libraries(simple_ex3_rsa ${PROJECT_NAME}) +add_test( + NAME simple_ex3_rsa + COMMAND ./simple_ex3_rsa + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/externals/cpp-jwt/examples/rsa_256/jwtRS256.key b/externals/cpp-jwt/examples/rsa_256/jwtRS256.key new file mode 100755 index 000000000..95538a06b --- /dev/null +++ b/externals/cpp-jwt/examples/rsa_256/jwtRS256.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC4fkg/JYyN3Skr6RYLiAd/Yhl02TE3/HzHSNPnCaRdUakGp9og +7oXBMcoadFDjnoSq1sz+gUHnpoO7s2fwkD5Q4OnCBGD3oKP2A4PlOOWD2B2cVmMq +X/vf1nAA/343496jsbfgkh1Q7LTzR0IXfdii0o1UCbvrVCuaBoyiv4TxWQIDAQAB +AoGAWA5uDTWu0Ecuz3aAvyA9896up8bCZyZrp/JqsWs4uBGxyytyQSWXUY6iF95M +fVe7mo7LaO3ottgTKBOJGJjAJKnfwXRn8/NV/Q5oHx48sPGDoUUtyMrRbZpeLM1L +gpFX715XWrtALInWPlVG1OfkQQLv4K7mwveM0cez0bWPUsECQQDuPK9IL7WuO2WR +s6pGEHBc3/MMk6I+vqJ+rJMgJjCC/Wjeyo6U3xTNipJRJL5L/Y8iMqpWCrYOjpo8 ++1p4FXqDAkEAxj/FcVhXl3NMco6D9u0LxTAmqavMzmXDmODVW2m1K3+rQWQDqXqr +FQ9WQq0LSsqiwRul6hrd0EmCkNJqpCMN8wJBAIz06uDTGbPVAOuMWhrKbzEEcFHo +p/5n3M0GXqaO8fUO6pWnU2VR+IUEkD3id5WOmLmrMI1oGP/T7/5U2dpjGvECQEBq +0k4tJXEJvupuUoT2q19scPOq5kaenHrde5ZTd9HljxEVXXdBa7vRGvdZYRTxWQck +Y7n49uBKMom6RXqGBW8CQQCrnub4stg6dwdpnmZmEtAE4VqNSZeV5UWz7+l7+R+B +ENtNlIgyQfE6NpOc3Fr/uy3IQjaHcOOwIKI0GMJww9sC +-----END RSA PRIVATE KEY----- diff --git a/externals/cpp-jwt/examples/rsa_256/jwtRS256.key.pub b/externals/cpp-jwt/examples/rsa_256/jwtRS256.key.pub new file mode 100755 index 000000000..c04571680 --- /dev/null +++ b/externals/cpp-jwt/examples/rsa_256/jwtRS256.key.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4fkg/JYyN3Skr6RYLiAd/Yhl0 +2TE3/HzHSNPnCaRdUakGp9og7oXBMcoadFDjnoSq1sz+gUHnpoO7s2fwkD5Q4OnC +BGD3oKP2A4PlOOWD2B2cVmMqX/vf1nAA/343496jsbfgkh1Q7LTzR0IXfdii0o1U +CbvrVCuaBoyiv4TxWQIDAQAB +-----END PUBLIC KEY----- diff --git a/externals/cpp-jwt/examples/simple_ex1.cc b/externals/cpp-jwt/examples/simple_ex1.cc new file mode 100755 index 000000000..951b9b6bf --- /dev/null +++ b/externals/cpp-jwt/examples/simple_ex1.cc @@ -0,0 +1,21 @@ +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + auto key = "secret"; //Secret to use for the algorithm + //Create JWT object + jwt::jwt_object obj{algorithm("HS256"), payload({{"some", "payload"}}), secret(key)}; + + //Get the encoded string/assertion + auto enc_str = obj.signature(); + std::cout << enc_str << std::endl; + + //Decode + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret(key)); + std::cout << dec_obj.header() << std::endl; + std::cout << dec_obj.payload() << std::endl; + + return 0; +} diff --git a/externals/cpp-jwt/examples/simple_ex2.cc b/externals/cpp-jwt/examples/simple_ex2.cc new file mode 100755 index 000000000..d906a62db --- /dev/null +++ b/externals/cpp-jwt/examples/simple_ex2.cc @@ -0,0 +1,48 @@ +#include +#include +#include +#include "jwt/jwt.hpp" + +int main() { + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"user", "admin"}})}; + + //Use add_claim API to add claim values which are + // _not_ strings. + // For eg: `iat` and `exp` claims below. + // Other claims could have been added in the payload + // function above as they are just stringy things. + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "test") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10}) + ; + + //Use `has_claim` to check if the claim exists or not + assert (obj.has_claim("iss")); + assert (obj.has_claim("exp")); + + //Use `has_claim_with_value` to check if the claim exists + //with a specific value or not. + assert (obj.payload().has_claim_with_value("id", "a-b-c-d-e-f-1-2-3")); + assert (obj.payload().has_claim_with_value("iat", 1513862371)); + + //Remove a claim using `remove_claim` API. + //Most APIs have an overload which takes enum class type as well + //It can be used interchangeably with strings. + obj.remove_claim(jwt::registered_claims::expiration); + assert (!obj.has_claim("exp")); + + //Using `add_claim` with extra features. + //Check return status and overwrite + assert (!obj.payload().add_claim("sub", "new test", false/*overwrite*/)); + + // Overwrite an existing claim + assert (obj.payload().add_claim("sub", "new test", true/*overwrite*/)); + + assert (obj.payload().has_claim_with_value("sub", "new test")); + + return 0; +} diff --git a/externals/cpp-jwt/examples/simple_ex3_rsa.cc b/externals/cpp-jwt/examples/simple_ex3_rsa.cc new file mode 100755 index 000000000..2934a9230 --- /dev/null +++ b/externals/cpp-jwt/examples/simple_ex3_rsa.cc @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include "jwt/jwt.hpp" + +/*** + * STEPS TO GENERATE RSA PRIVATE PUBLIC KEYPAIR. + * + * 1. openssl genrsa -out jwtRS256.key 1024 + * 2. openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub + */ + +std::string read_from_file(const std::string& path) +{ + std::string contents; + std::ifstream is{path, std::ifstream::binary}; + + if (is) { + // get length of file: + is.seekg (0, is.end); + auto length = is.tellg(); + is.seekg (0, is.beg); + contents.resize(length); + + is.read(&contents[0], length); + if (!is) { + is.close(); + return {}; + } + } else { + std::cerr << "FILE not FOUND!!" << std::endl; + } + + is.close(); + return contents; +} + +int main() { + using namespace jwt::params; + const std::string priv_key_path = std::string{CERT_ROOT_DIR} + "/jwtRS256.key"; + const std::string pub_key_path = std::string{CERT_ROOT_DIR} + "/jwtRS256.key.pub"; + + auto priv_key = read_from_file(priv_key_path); + + jwt::jwt_object obj{algorithm("RS256"), secret(priv_key), payload({{"user", "admin"}})}; + + //Use add_claim API to add claim values which are + // _not_ strings. + // For eg: `iat` and `exp` claims below. + // Other claims could have been added in the payload + // function above as they are just stringy things. + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "test") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10}) + ; + + //Use `has_claim` to check if the claim exists or not + assert (obj.has_claim("iss")); + assert (obj.has_claim("exp")); + + //Use `has_claim_with_value` to check if the claim exists + //with a specific value or not. + assert (obj.payload().has_claim_with_value("id", "a-b-c-d-e-f-1-2-3")); + assert (obj.payload().has_claim_with_value("iat", 1513862371)); + + auto pub_key = read_from_file(pub_key_path); + + std::error_code ec{}; + auto sign = obj.signature(ec); + if (ec) { + std::cerr << ec.message() << std::endl; + return 1; + } + + auto dec_obj = jwt::decode(sign, algorithms({"RS256"}), verify(false), secret(pub_key)); + + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/algorithm.hpp b/externals/cpp-jwt/include/jwt/algorithm.hpp new file mode 100755 index 000000000..0e3b8437d --- /dev/null +++ b/externals/cpp-jwt/include/jwt/algorithm.hpp @@ -0,0 +1,557 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_ALGORITHM_HPP +#define CPP_JWT_ALGORITHM_HPP + +/*! + * Most of the signing and verification code has been taken + * and modified for C++ specific use from the C implementation + * JWT library, libjwt. + * https://github.com/benmcollins/libjwt/tree/master/libjwt + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jwt/assertions.hpp" +#include "jwt/exceptions.hpp" +#include "jwt/string_view.hpp" +#include "jwt/error_codes.hpp" +#include "jwt/base64.hpp" +#include "jwt/config.hpp" + +namespace jwt { + +/// The result type of the signing function +using sign_result_t = std::pair; +/// The result type of verification function +using verify_result_t = std::pair; +/// The function pointer type for the signing function +using sign_func_t = sign_result_t (*) (const jwt::string_view key, + const jwt::string_view data); +/// The function pointer type for the verifying function +using verify_func_t = verify_result_t (*) (const jwt::string_view key, + const jwt::string_view head, + const jwt::string_view jwt_sign); + +namespace algo { + +//Me: TODO: All these can be done using code generaion. +//Me: NO. NEVER. I hate Macros. +//Me: You can use templates too. +//Me: No. I would rather prefer explicit. +//Me: Ok. You win. +//Me: Same to you. + +/** + * HS256 algorithm. + */ +struct HS256 +{ + const EVP_MD* operator()() noexcept + { + return EVP_sha256(); + } +}; + +/** + * HS384 algorithm. + */ +struct HS384 +{ + const EVP_MD* operator()() noexcept + { + return EVP_sha384(); + } +}; + +/** + * HS512 algorithm. + */ +struct HS512 +{ + const EVP_MD* operator()() noexcept + { + return EVP_sha512(); + } +}; + +/** + * NONE algorithm. + */ +struct NONE +{ + void operator()() noexcept + { + return; + } +}; + +/** + * RS256 algorithm. + */ +struct RS256 +{ + static const int type = EVP_PKEY_RSA; + + const EVP_MD* operator()() noexcept + { + return EVP_sha256(); + } +}; + +/** + * RS384 algorithm. + */ +struct RS384 +{ + static const int type = EVP_PKEY_RSA; + + const EVP_MD* operator()() noexcept + { + return EVP_sha384(); + } +}; + +/** + * RS512 algorithm. + */ +struct RS512 +{ + static const int type = EVP_PKEY_RSA; + + const EVP_MD* operator()() noexcept + { + return EVP_sha512(); + } +}; + +/** + * ES256 algorithm. + */ +struct ES256 +{ + static const int type = EVP_PKEY_EC; + + const EVP_MD* operator()() noexcept + { + return EVP_sha256(); + } +}; + +/** + * ES384 algorithm. + */ +struct ES384 +{ + static const int type = EVP_PKEY_EC; + + const EVP_MD* operator()() noexcept + { + return EVP_sha384(); + } +}; + +/** + * ES512 algorithm. + */ +struct ES512 +{ + static const int type = EVP_PKEY_EC; + + const EVP_MD* operator()() noexcept + { + return EVP_sha512(); + } +}; + +} //END Namespace algo + + +/** + * JWT signing algorithm types. + */ +enum class algorithm +{ + NONE = 0, + HS256, + HS384, + HS512, + RS256, + RS384, + RS512, + ES256, + ES384, + ES512, + UNKN, + TERM, +}; + + +/** + * Convert the algorithm enum class type to + * its stringified form. + */ +inline jwt::string_view alg_to_str(SCOPED_ENUM algorithm alg) noexcept +{ + switch (alg) { + case algorithm::HS256: return "HS256"; + case algorithm::HS384: return "HS384"; + case algorithm::HS512: return "HS512"; + case algorithm::RS256: return "RS256"; + case algorithm::RS384: return "RS384"; + case algorithm::RS512: return "RS512"; + case algorithm::ES256: return "ES256"; + case algorithm::ES384: return "ES384"; + case algorithm::ES512: return "ES512"; + case algorithm::TERM: return "TERM"; + case algorithm::NONE: return "NONE"; + case algorithm::UNKN: return "UNKN"; + default: assert (0 && "Unknown Algorithm"); + }; + return "UNKN"; + JWT_NOT_REACHED("Code not reached"); +} + +/** + * Convert stringified algorithm to enum class. + * The string comparison is case insesitive. + */ +inline SCOPED_ENUM algorithm str_to_alg(const jwt::string_view alg) noexcept +{ + if (!alg.length()) return algorithm::UNKN; + + if (!strcasecmp(alg.data(), "NONE")) return algorithm::NONE; + if (!strcasecmp(alg.data(), "HS256")) return algorithm::HS256; + if (!strcasecmp(alg.data(), "HS384")) return algorithm::HS384; + if (!strcasecmp(alg.data(), "HS512")) return algorithm::HS512; + if (!strcasecmp(alg.data(), "RS256")) return algorithm::RS256; + if (!strcasecmp(alg.data(), "RS384")) return algorithm::RS384; + if (!strcasecmp(alg.data(), "RS512")) return algorithm::RS512; + if (!strcasecmp(alg.data(), "ES256")) return algorithm::ES256; + if (!strcasecmp(alg.data(), "ES384")) return algorithm::ES384; + if (!strcasecmp(alg.data(), "ES512")) return algorithm::ES512; + + return algorithm::UNKN; + + JWT_NOT_REACHED("Code not reached"); +} + +/** + */ +inline void bio_deletor(BIO* ptr) +{ + if (ptr) BIO_free_all(ptr); +} + +/** + */ +inline void evp_md_ctx_deletor(EVP_MD_CTX* ptr) +{ + if (ptr) EVP_MD_CTX_destroy(ptr); +} + +/** + */ +inline void ec_key_deletor(EC_KEY* ptr) +{ + if (ptr) EC_KEY_free(ptr); +} + +/** + */ +inline void ec_sig_deletor(ECDSA_SIG* ptr) +{ + if (ptr) ECDSA_SIG_free(ptr); +} + +/** + */ +inline void ev_pkey_deletor(EVP_PKEY* ptr) +{ + if (ptr) EVP_PKEY_free(ptr); +} + +/// Useful typedefs +using bio_deletor_t = decltype(&bio_deletor); +using BIO_uptr = std::unique_ptr; + +using evp_mdctx_deletor_t = decltype(&evp_md_ctx_deletor); +using EVP_MDCTX_uptr = std::unique_ptr; + +using eckey_deletor_t = decltype(&ec_key_deletor); +using EC_KEY_uptr = std::unique_ptr; + +using ecsig_deletor_t = decltype(&ec_sig_deletor); +using EC_SIG_uptr = std::unique_ptr; + +using evpkey_deletor_t = decltype(&ev_pkey_deletor); +using EC_PKEY_uptr = std::unique_ptr; + + + +/** + * OpenSSL HMAC based signature and verfication. + * + * The template type `Hasher` takes the type representing + * the HMAC algorithm type from the `jwt::algo` namespace. + * + * The struct is specialized for NONE algorithm. See the + * details of that class as well. + */ +template +struct HMACSign +{ + /// The type of Hashing algorithm + using hasher_type = Hasher; + + /** + * Signs the input using the HMAC algorithm using the + * provided key. + * + * Arguments: + * @key : The secret/key to use for the signing. + * Cannot be empty string. + * @data : The data to be signed. + * + * Exceptions: + * Any allocation failure will result in jwt::MemoryAllocationException + * being thrown. + */ + static sign_result_t sign(const jwt::string_view key, const jwt::string_view data) + { + std::string sign; + sign.resize(EVP_MAX_MD_SIZE); + std::error_code ec{}; + + uint32_t len = 0; + + unsigned char* res = HMAC(Hasher{}(), + key.data(), + static_cast(key.length()), + reinterpret_cast(data.data()), + data.length(), + reinterpret_cast(&sign[0]), + &len); + if (!res) { + ec = AlgorithmErrc::SigningErr; + } + + sign.resize(len); + return { std::move(sign), ec }; + } + + /** + * Verifies the JWT string against the signature using + * the provided key. + * + * Arguments: + * @key : The secret/key to use for the signing. + * Cannot be empty string. + * @head : The part of JWT encoded string representing header + * and the payload claims. + * @sign : The signature part of the JWT encoded string. + * + * Returns: + * verify_result_t + * verify_result_t::first set to true if verification succeeds. + * false otherwise. + * verify_result_t::second set to relevant error if verification fails. + * + * Exceptions: + * Any allocation failure will result in jwt::MemoryAllocationException + * being thrown. + */ + static verify_result_t + verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign); + +}; + +/** + * Specialization of `HMACSign` class + * for NONE algorithm. + * + * This specialization is selected for even + * PEM based algorithms. + * + * The signing and verification APIs are + * basically no-op except that they would + * set the relevant error code. + * + * NOTE: error_code would be set in the case + * of usage of NONE algorithm. + * Users of this API are expected to check for + * the case explicitly. + */ +template <> +struct HMACSign +{ + using hasher_type = algo::NONE; + + /** + * Basically a no-op. Sets the error code to NoneAlgorithmUsed. + */ + static sign_result_t sign(const jwt::string_view key, const jwt::string_view data) + { + (void)key; + (void)data; + std::error_code ec{}; + ec = AlgorithmErrc::NoneAlgorithmUsed; + + return { std::string{}, ec }; + } + + /** + * Basically a no-op. Sets the error code to NoneAlgorithmUsed. + */ + static verify_result_t + verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign) + { + (void)key; + (void)head; + (void)sign; + std::error_code ec{}; + ec = AlgorithmErrc::NoneAlgorithmUsed; + + return { true, ec }; + } + +}; + + + +/** + * OpenSSL PEM based signature and verfication. + * + * The template type `Hasher` takes the type representing + * the PEM algorithm type from the `jwt::algo` namespace. + * + * For NONE algorithm, HMACSign<> specialization is used. + * See that for more details. + */ +template +struct PEMSign +{ +public: + /// The type of Hashing algorithm + using hasher_type = Hasher; + + /** + * Signs the input data using PEM encryption algorithm. + * + * Arguments: + * @key : The key/secret to be used for signing. + * Cannot be an empty string. + * @data: The data to be signed. + * + * Exceptions: + * Any allocation failure would be thrown out as + * jwt::MemoryAllocationException. + */ + static sign_result_t sign(const jwt::string_view key, const jwt::string_view data) + { + std::error_code ec{}; + + std::string ii{data.data(), data.length()}; + + EC_PKEY_uptr pkey{load_key(key, ec), ev_pkey_deletor}; + if (ec) return { std::string{}, ec }; + + //TODO: Use stack string here ? + std::string sign = evp_digest(pkey.get(), data, ec); + + if (ec) return { std::string{}, ec }; + + if (Hasher::type == EVP_PKEY_EC) { + sign = public_key_ser(pkey.get(), sign, ec); + } + + return { std::move(sign), ec }; + } + + /** + */ + static verify_result_t + verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign); + +private: + + /*! + */ + static EVP_PKEY* load_key(const jwt::string_view key, std::error_code& ec); + + /*! + */ + static std::string evp_digest(EVP_PKEY* pkey, const jwt::string_view data, std::error_code& ec); + + /*! + */ + static std::string public_key_ser(EVP_PKEY* pkey, jwt::string_view sign, std::error_code& ec); + +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L + + + //ATTN: Below 2 functions + //are Taken from https://github.com/nginnever/zogminer/issues/39 + + /** + */ + static void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps) + { + if (pr != nullptr) *pr = sig->r; + if (ps != nullptr) *ps = sig->s; + }; + + /** + */ + static int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s) + { + if (r == nullptr || s == nullptr) return 0; + + BN_clear_free(sig->r); + BN_clear_free(sig->s); + + sig->r = r; + sig->s = s; + return 1; + } + +#endif +}; + +} // END namespace jwt + +#include "jwt/impl/algorithm.ipp" + + +#endif diff --git a/externals/cpp-jwt/include/jwt/assertions.hpp b/externals/cpp-jwt/include/jwt/assertions.hpp new file mode 100755 index 000000000..e253ba810 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/assertions.hpp @@ -0,0 +1,51 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_ASSERTIONS_HPP +#define CPP_JWT_ASSERTIONS_HPP + +#include + +namespace jwt { + +#if defined(__clang__) +# define JWT_NOT_REACHED_MARKER() __builtin_unreachable() +#elif defined(__GNUC__) +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define JWT_NOT_REACHED_MARKER() __builtin_unreachable() +# endif +#elif defined(_MSC_VER) +# define JWT_NOT_REACHED_MARKER() __assume(0) +#endif + +#if defined(DEBUG) +# define JWT_NOT_REACHED(reason) do { \ + assert (0 && reason); \ + JWT_NOT_REACHED_MARKER(); \ + } while (0) +#else +# define JWT_NOT_REACHED(reason) JWT_NOT_REACHED_MARKER() +#endif + +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/base64.hpp b/externals/cpp-jwt/include/jwt/base64.hpp new file mode 100755 index 000000000..248b10c2a --- /dev/null +++ b/externals/cpp-jwt/include/jwt/base64.hpp @@ -0,0 +1,335 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_BASE64_HPP +#define CPP_JWT_BASE64_HPP + +#include +#include +#include +#include +#include "jwt/config.hpp" +#include "jwt/string_view.hpp" + +namespace jwt { + +// Returns the maximum number of bytes required to +// encode an input byte string of length `n` to base64. +inline constexpr +size_t encoding_size(size_t n) +{ + return 4 * ((n + 2) / 3); +} + + +// Returns the maximum number of bytes required +// to store a decoded base64 byte string. +inline constexpr +size_t decoding_size(size_t n) +{ + return n / 4 * 3; +} + +/** + * Encoding map. + * A constexpr helper class for performing base64 + * encoding on the input byte string. + */ +class EMap +{ +public: + constexpr EMap() = default; + +public: + constexpr char at(size_t pos) const noexcept + { + return X_ASSERT(pos < chars_.size()), chars_.at(pos); + } + +private: + std::array chars_ = {{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '+','/', + }}; +}; + +/** + * Encodes a sequence of octet into base64 string. + * Returns std::string resized to contain only the + * encoded data (as usual without null terminator). + * + * The encoded string is atleast `encoding_size(input len)` + * in size. + * + * Arguments: + * @in : Input byte string to be encoded. + * @len : Length of the input byte string. + */ +inline std::string base64_encode(const char* in, size_t len) +{ + std::string result; + const auto encoded_siz = encoding_size(len); + result.resize(encoded_siz); + + constexpr static const EMap emap{}; + + int i = 0; + int j = 0; + for (; i < static_cast(len) - 2; i += 3) { + const auto first = in[i]; + const auto second = in[i+1]; + const auto third = in[i+2]; + + result[j++] = emap.at( (first >> 2) & 0x3F ); + result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4)); + result[j++] = emap.at(((second & 0x0F) << 2) | ((third & 0xC0) >> 6)); + result[j++] = emap.at( (third & 0x3F) ); + } + + switch (len % 3) { + case 2: + { + const auto first = in[i]; + const auto second = in[i+1]; + + result[j++] = emap.at( (first >> 2) & 0x3F ); + result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4)); + result[j++] = emap.at( (second & 0x0F) << 2 ); + result[j++] = '='; + break; + } + case 1: + { + const auto first = in[i]; + + result[j++] = emap.at((first >> 2) & 0x3F); + result[j++] = emap.at((first & 0x03) << 4); + result[j++] = '='; + result[j++] = '='; + break; + } + case 0: + break; + }; + + result.resize(j); + + return result; +} + + + +//======================= Decoder ========================== + +/** + * Decoding map. + * A helper constexpr class for providing interface + * to the decoding map for base64. + */ +class DMap +{ +public: + constexpr DMap() = default; + +public: + constexpr signed char at(size_t pos) const noexcept + { + return X_ASSERT(pos < map_.size()), map_[pos]; + } + +private: + std::array map_ = {{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 + }}; +}; + +/** + * Decodes octet of base64 encoded byte string. + * + * Returns a std::string with the decoded byte string. + * + * Arguments: + * @in : Encoded base64 byte string. + * @len : Length of the encoded input byte string. + */ +inline std::string base64_decode(const char* in, size_t len) +{ + std::string result; + const auto decoded_siz = decoding_size(len); + result.resize(decoded_siz); + + int i = 0; + size_t bytes_rem = len; + size_t bytes_wr = 0; + + constexpr static const DMap dmap{}; + + while (bytes_rem > 0 && dmap.at(in[bytes_rem - 1]) == -1) { bytes_rem--; } + + while (bytes_rem > 4) + { + // Error case in input + if (dmap.at(*in) == -1) return result; + + const auto first = dmap.at(in[0]); + const auto second = dmap.at(in[1]); + const auto third = dmap.at(in[2]); + const auto fourth = dmap.at(in[3]); + + result[i] = (first << 2) | (second >> 4); + result[i + 1] = (second << 4) | (third >> 2); + result[i + 2] = (third << 6) | fourth; + + bytes_rem -= 4; + i += 3; + in += 4; + } + bytes_wr = i; + + switch(bytes_rem) { + case 4: + { + const auto third = dmap.at(in[2]); + const auto fourth = dmap.at(in[3]); + result[i + 2] = (third << 6) | fourth; + bytes_wr++; + } + //FALLTHROUGH + case 3: + { + const auto second = dmap.at(in[1]); + const auto third = dmap.at(in[2]); + result[i + 1] = (second << 4) | (third >> 2); + bytes_wr++; + } + //FALLTHROUGH + case 2: + { + const auto first = dmap.at(in[0]); + const auto second = dmap.at(in[1]); + result[i] = (first << 2) | (second >> 4); + bytes_wr++; + } + }; + + result.resize(bytes_wr); + + return result; +} + +/** + * Makes the base64 encoded byte string URL safe. + * Overwrites/skips few URL unsafe characters + * from the input sequence. + * + * Arguments: + * @data : Base64 encoded byte string. + * @len : Length of the base64 byte string. + * + * Returns: + * Length of the URL safe base64 encoded byte string. + */ +inline size_t base64_uri_encode(char* data, size_t len) noexcept +{ + size_t i = 0; + size_t j = 0; + + for (; i < len; ++i) { + switch (data[i]) { + case '+': + data[j++] = '-'; + break; + case '/': + data[j++] = '_'; + break; + case '=': + break; + default: + data[j++] = data[i]; + }; + } + + return j; +} + +/** + * Decodes an input URL safe base64 encoded byte string. + * + * NOTE: To be used only for decoding URL safe base64 encoded + * byte string. + * + * Arguments: + * @data : URL safe base64 encoded byte string. + * @len : Length of the input byte string. + */ +inline std::string base64_uri_decode(const char* data, size_t len) +{ + std::string uri_dec; + uri_dec.resize(len + 4); + + size_t i = 0; + + for (; i < len; ++i) + { + switch (data[i]) { + case '-': + uri_dec[i] = '+'; + break; + case '_': + uri_dec[i] = '/'; + break; + default: + uri_dec[i] = data[i]; + }; + } + + size_t trailer = 4 - (i % 4); + if (trailer && trailer < 4) { + while (trailer--) { + uri_dec[i++] = '='; + } + } + + return base64_decode(uri_dec.c_str(), uri_dec.length()); +} + +} // END namespace jwt + + +#endif diff --git a/externals/cpp-jwt/include/jwt/config.hpp b/externals/cpp-jwt/include/jwt/config.hpp new file mode 100755 index 000000000..5d94a0c62 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/config.hpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2018 Arun Muralidharan + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef CPP_JWT_CONFIG_HPP +#define CPP_JWT_CONFIG_HPP + +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +// To hack around Visual Studio error: +// error C3431: 'algorithm': a scoped enumeration cannot be redeclared as an unscoped enumeration +#if defined(_MSC_VER) && !defined(__clang__) +#define SCOPED_ENUM enum class +#else +#define SCOPED_ENUM enum +#endif + +// To hack around Visual Studio error +// error C3249: illegal statement or sub-expression for 'constexpr' function +// Doesn't allow assert to be part of constexpr functions. +// Copied the solution as described in: +// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ +#if defined NDEBUG +# define X_ASSERT(CHECK) void(0) +#else +# define X_ASSERT(CHECK) \ + ( (CHECK) ? void(0) : []{assert(!#CHECK);}() ) +#endif + + +#endif diff --git a/externals/cpp-jwt/include/jwt/detail/meta.hpp b/externals/cpp-jwt/include/jwt/detail/meta.hpp new file mode 100755 index 000000000..39df56fc9 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/detail/meta.hpp @@ -0,0 +1,236 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ +#ifndef CPP_JWT_META_HPP +#define CPP_JWT_META_HPP + +#include +#include +#include "jwt/string_view.hpp" + +namespace jwt { +namespace detail { +namespace meta { + +/** + * The famous void_t trick. + */ +template +struct make_void +{ + using type = void; +}; + +template +using void_t = typename make_void::type; + +/** + * A type tag representing an empty tag. + * To be used to represent a `result-not-found` + * situation. + */ +struct empty_type {}; + +/** + * A type list. + */ +template struct list{}; + + +/** + */ +template +struct has_create_json_obj_member: std::false_type +{ +}; + +template +struct has_create_json_obj_member().create_json_obj(), + (void)0 + ) + > + >: std::true_type +{ +}; + +/** + * Checks if the type `T` models MappingConcept. + * + * Requirements on type `T` for matching the requirements: + * a. Must be able to construct jwt::string_view from the + * `key_type` of the map. + * b. Must be able to construct jwt::string_view from the + * `mapped_type` of the map. + * c. The type `T` must have an access operator i.e. operator[]. + * d. The type `T` must have `begin` and `end` member functions + * for iteration. + * + * NOTE: Requirements `a` and `b` means that the concept + * type can only hold values that are string or constructible + * to form a string_view (basically C strings and std::string) + */ +template +struct is_mapping_concept: std::false_type +{ +}; + +template +struct is_mapping_concept::key_type>::value, + void + >::type, + + typename std::enable_if< + std::is_constructible::mapped_type>::value, + void + >::type, + + decltype( + std::declval().operator[](std::declval::key_type>()), + std::declval().begin(), + std::declval().end(), + (void)0 + ) + > + >: std::true_type +{ +}; + + +/** + * Checks if the type `T` models the ParameterConcept. + * + * Requirements on type `T` for matching the requirements: + * a. The type must have a `get` method. + */ +template +struct is_parameter_concept: std::false_type +{ +}; + +template +struct is_parameter_concept().get(), + (void)0 + ) + > + >: std::true_type +{ +}; + +/** + * Models SequenceConcept + */ +template +struct is_sequence_concept: std::false_type +{ +}; + +/// For array types +template +struct is_sequence_concept>::value>, + + std::enable_if_t< + std::is_constructible()))>>::value + > + > + >: std::true_type +{ +}; + +template +struct is_sequence_concept::iterator::iterator_category + >::value>, + + std::enable_if_t< + std::is_constructible::value_type>::value + >, + + decltype( + std::declval().begin(), + std::declval().end(), + (void)0 + ) + > + >: std::true_type +{ +}; + + +/** + * Find if a type is present in the typelist. + * Eg: has_type>{} == true + * has_type>{} == false + */ +template struct has_type; + +template +struct has_type>: std::false_type +{ +}; + +template +struct has_type>: std::true_type +{ +}; + +template +struct has_type>: has_type> +{ +}; + +/** + * A pack of bools for the bool trick. + */ +template +struct bool_pack {}; + +/** + */ +template +using all_true = std::is_same, bool_pack>; + +/** + */ +template +using are_all_params = all_true::value...>; + + +} // END namespace meta +} // END namespace detail +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/error_codes.hpp b/externals/cpp-jwt/include/jwt/error_codes.hpp new file mode 100755 index 000000000..6c6aaa6a4 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/error_codes.hpp @@ -0,0 +1,136 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_ERROR_CODES_HPP +#define CPP_JWT_ERROR_CODES_HPP + +#include + +namespace jwt { +/** + * All the algorithm errors + */ +enum class AlgorithmErrc +{ + SigningErr = 1, + VerificationErr, + KeyNotFoundErr, + InvalidKeyErr, + NoneAlgorithmUsed, // Not an actual error! +}; + +/** + * Algorithm error conditions + * TODO: Remove it or use it! + */ +enum class AlgorithmFailureSource +{ +}; + +/** + * Decode error conditions + */ +enum class DecodeErrc +{ + // No algorithms provided in decode API + EmptyAlgoList = 1, + // The JWT signature has incorrect format + SignatureFormatError, + // The JSON library failed to parse + JsonParseError, + // Algorithm field in header is missing + AlgHeaderMiss, + // Type field in header is missing + TypHeaderMiss, + // Unexpected type field value + TypMismatch, + // Found duplicate claims + DuplClaims, + // Key/Secret not passed as decode argument + KeyNotPresent, + // Key/secret passed as argument for NONE algorithm. + // Not a hard error. + KeyNotRequiredForNoneAlg, +}; + +/** + * Errors handled during verification process. + */ +enum class VerificationErrc +{ + //Algorithms provided does not match with header + InvalidAlgorithm = 1, + //Token is expired at the time of decoding + TokenExpired, + //The issuer specified does not match with payload + InvalidIssuer, + //The subject specified does not match with payload + InvalidSubject, + //The field IAT is not present or is of invalid type + InvalidIAT, + //Checks for the existence of JTI + //if validate_jti is passed in decode + InvalidJTI, + //The audience specified does not match with payload + InvalidAudience, + //Decoded before nbf time + ImmatureSignature, + //Signature match error + InvalidSignature, + // Invalid value type used for known claims + TypeConversionError, +}; + +/** + */ +std::error_code make_error_code(AlgorithmErrc err); + +/** + */ +std::error_code make_error_code(DecodeErrc err); + +/** + */ +std::error_code make_error_code(VerificationErrc err); + +} // END namespace jwt + + +/** + * Make the custom enum classes as error code + * adaptable. + */ +namespace std +{ + template <> + struct is_error_code_enum : true_type {}; + + template <> + struct is_error_code_enum: true_type {}; + + template <> + struct is_error_code_enum: true_type {}; +} + +#include "jwt/impl/error_codes.ipp" + +#endif diff --git a/externals/cpp-jwt/include/jwt/exceptions.hpp b/externals/cpp-jwt/include/jwt/exceptions.hpp new file mode 100755 index 000000000..e844c11f5 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/exceptions.hpp @@ -0,0 +1,305 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_EXCEPTIONS_HPP +#define CPP_JWT_EXCEPTIONS_HPP + +#include +#include + +namespace jwt { + +/** + * Exception for allocation related failures in the + * OpenSSL C APIs. + */ +class MemoryAllocationException final: public std::bad_alloc +{ +public: + /** + * Construct MemoryAllocationException from a + * string literal. + */ + template + MemoryAllocationException(const char(&msg)[N]) + : msg_(&msg[0]) + { + } + + virtual const char* what() const noexcept override + { + return msg_; + } + +private: + const char* msg_ = nullptr; +}; + +/** + * Exception thrown for failures in OpenSSL + * APIs while signing. + */ +class SigningError : public std::runtime_error +{ +public: + /** + */ + SigningError(std::string msg) + : std::runtime_error(std::move(msg)) + { + } +}; + +/** + * Exception thrown for decode related errors. + */ +class DecodeError: public std::runtime_error +{ +public: + /** + */ + DecodeError(std::string msg) + : std::runtime_error(std::move(msg)) + { + } +}; + +/** + * A derived decode error for signature format + * error. + */ +class SignatureFormatError final : public DecodeError +{ +public: + /** + */ + SignatureFormatError(std::string msg) + : DecodeError(std::move(msg)) + { + } +}; + +/** + * A derived decode error for Key argument not present + * error. Only thrown if the algorithm set is not NONE. + */ +class KeyNotPresentError final : public DecodeError +{ +public: + /** + */ + KeyNotPresentError(std::string msg) + : DecodeError(std::move(msg)) + { + } +}; + + +/** + * Base class exception for all kinds of verification errors. + * Verification errors are thrown only when the verify + * decode parameter is set to true. + */ +class VerificationError : public std::runtime_error +{ +public: + /** + */ + VerificationError(std::string msg) + : std::runtime_error(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the algorithm decoded in the header + * is incorrect. + */ +class InvalidAlgorithmError final: public VerificationError +{ +public: + /** + */ + InvalidAlgorithmError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the token is expired at the + * time of decoding. + */ +class TokenExpiredError final: public VerificationError +{ +public: + /** + */ + TokenExpiredError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the issuer claim does not match + * with the one provided as part of decode argument. + */ +class InvalidIssuerError final: public VerificationError +{ +public: + /** + */ + InvalidIssuerError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the audience claim does not match + * with the one provided as part of decode argument. + */ +class InvalidAudienceError final: public VerificationError +{ +public: + /** + */ + InvalidAudienceError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the subject claim does not match + * with the one provided as part of decode argument. + */ +class InvalidSubjectError final: public VerificationError +{ +public: + /** + */ + InvalidSubjectError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when verify_iat parameter is passed to + * decode and IAT is not present. + */ +class InvalidIATError final: public VerificationError +{ +public: + /** + */ + InvalidIATError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when validate_jti is asked for + * in decode and jti claim is not present. + */ +class InvalidJTIError final: public VerificationError +{ +public: + /** + */ + InvalidJTIError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the token is decoded at a time before + * as specified in the `nbf` claim. + */ +class ImmatureSignatureError final: public VerificationError +{ +public: + /** + */ + ImmatureSignatureError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when the signature does not match in the verification process. + */ +class InvalidSignatureError final: public VerificationError +{ +public: + /** + */ + InvalidSignatureError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +class InvalidKeyError final: public VerificationError +{ +public: + /** + */ + InvalidKeyError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +/** + * Derived from VerificationError. + * Thrown when there type expectation mismatch + * while verifying the values of registered claim names. + */ +class TypeConversionError final: public VerificationError +{ +public: + /** + */ + TypeConversionError(std::string msg) + : VerificationError(std::move(msg)) + { + } +}; + +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/impl/algorithm.ipp b/externals/cpp-jwt/include/jwt/impl/algorithm.ipp new file mode 100755 index 000000000..c778533e7 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/impl/algorithm.ipp @@ -0,0 +1,312 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_ALGORITHM_IPP +#define CPP_JWT_ALGORITHM_IPP + +namespace jwt { + +template +verify_result_t HMACSign::verify( + const jwt::string_view key, + const jwt::string_view head, + const jwt::string_view jwt_sign) +{ + std::error_code ec{}; + + unsigned char enc_buf[EVP_MAX_MD_SIZE]; + uint32_t enc_buf_len = 0; + + unsigned char* res = HMAC(Hasher{}(), + key.data(), + static_cast(key.length()), + reinterpret_cast(head.data()), + head.length(), + enc_buf, + &enc_buf_len); + if (!res) { + ec = AlgorithmErrc::VerificationErr; + return {false, ec}; + } + if (enc_buf_len == 0) { + ec = AlgorithmErrc::VerificationErr; + return {false, ec}; + } + + std::string b64_enc_str = jwt::base64_encode((const char*)&enc_buf[0], enc_buf_len); + + if (!b64_enc_str.length()) { + ec = AlgorithmErrc::VerificationErr; + return {false, ec}; + } + + // Make the base64 string url safe + auto new_len = jwt::base64_uri_encode(&b64_enc_str[0], b64_enc_str.length()); + b64_enc_str.resize(new_len); + + bool ret = (jwt::string_view{b64_enc_str} == jwt_sign); + + return { ret, ec }; +} + + +template +verify_result_t PEMSign::verify( + const jwt::string_view key, + const jwt::string_view head, + const jwt::string_view jwt_sign) +{ + std::error_code ec{}; + std::string dec_sig = base64_uri_decode(jwt_sign.data(), jwt_sign.length()); + + BIO_uptr bufkey{ + BIO_new_mem_buf((void*)key.data(), static_cast(key.length())), + bio_deletor}; + + if (!bufkey) { + throw MemoryAllocationException("BIO_new_mem_buf failed"); + } + + EC_PKEY_uptr pkey{ + PEM_read_bio_PUBKEY(bufkey.get(), nullptr, nullptr, nullptr), + ev_pkey_deletor}; + + if (!pkey) { + ec = AlgorithmErrc::InvalidKeyErr; + return { false, ec }; + } + + int pkey_type = EVP_PKEY_id(pkey.get()); + + if (pkey_type != Hasher::type) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + //Convert EC signature back to ASN1 + if (Hasher::type == EVP_PKEY_EC) { + EC_SIG_uptr ec_sig{ECDSA_SIG_new(), ec_sig_deletor}; + if (!ec_sig) { + throw MemoryAllocationException("ECDSA_SIG_new failed"); + } + + //Get the actual ec_key + EC_KEY_uptr ec_key{EVP_PKEY_get1_EC_KEY(pkey.get()), ec_key_deletor}; + if (!ec_key) { + throw MemoryAllocationException("EVP_PKEY_get1_EC_KEY failed"); + } + + unsigned int degree = EC_GROUP_get_degree( + EC_KEY_get0_group(ec_key.get())); + + unsigned int bn_len = (degree + 7) / 8; + + if ((bn_len * 2) != dec_sig.length()) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + BIGNUM* ec_sig_r = BN_bin2bn((unsigned char*)dec_sig.data(), bn_len, nullptr); + BIGNUM* ec_sig_s = BN_bin2bn((unsigned char*)dec_sig.data() + bn_len, bn_len, nullptr); + + if (!ec_sig_r || !ec_sig_s) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + ECDSA_SIG_set0(ec_sig.get(), ec_sig_r, ec_sig_s); + + size_t nlen = i2d_ECDSA_SIG(ec_sig.get(), nullptr); + dec_sig.resize(nlen); + + auto data = reinterpret_cast(&dec_sig[0]); + nlen = i2d_ECDSA_SIG(ec_sig.get(), &data); + + if (nlen == 0) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + } + + EVP_MDCTX_uptr mdctx_ptr{EVP_MD_CTX_create(), evp_md_ctx_deletor}; + if (!mdctx_ptr) { + throw MemoryAllocationException("EVP_MD_CTX_create failed"); + } + + if (EVP_DigestVerifyInit( + mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey.get()) != 1) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + if (EVP_DigestVerifyUpdate(mdctx_ptr.get(), head.data(), head.length()) != 1) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + if (EVP_DigestVerifyFinal( + mdctx_ptr.get(), (unsigned char*)&dec_sig[0], dec_sig.length()) != 1) { + ec = AlgorithmErrc::VerificationErr; + return { false, ec }; + } + + return { true, ec }; +} + +template +EVP_PKEY* PEMSign::load_key( + const jwt::string_view key, + std::error_code& ec) +{ + ec.clear(); + + BIO_uptr bio_ptr{ + BIO_new_mem_buf((void*)key.data(), static_cast(key.length())), + bio_deletor}; + + if (!bio_ptr) { + throw MemoryAllocationException("BIO_new_mem_buf failed"); + } + + EVP_PKEY* pkey = PEM_read_bio_PrivateKey( + bio_ptr.get(), nullptr, nullptr, nullptr); + + if (!pkey) { + ec = AlgorithmErrc::SigningErr; + return nullptr; + } + + auto pkey_type = EVP_PKEY_id(pkey); + if (pkey_type != Hasher::type) { + ec = AlgorithmErrc::SigningErr; + return nullptr; + } + + return pkey; +} + +template +std::string PEMSign::evp_digest( + EVP_PKEY* pkey, + const jwt::string_view data, + std::error_code& ec) +{ + ec.clear(); + + EVP_MDCTX_uptr mdctx_ptr{EVP_MD_CTX_create(), evp_md_ctx_deletor}; + + if (!mdctx_ptr) { + throw MemoryAllocationException("EVP_MD_CTX_create failed"); + } + + //Initialiaze the digest algorithm + if (EVP_DigestSignInit( + mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey) != 1) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + //Update the digest with the input data + if (EVP_DigestSignUpdate(mdctx_ptr.get(), data.data(), data.length()) != 1) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + size_t len = 0; + + if (EVP_DigestSignFinal(mdctx_ptr.get(), nullptr, &len) != 1) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + std::string sign; + sign.resize(len); + + //Get the signature + if (EVP_DigestSignFinal(mdctx_ptr.get(), (unsigned char*)&sign[0], &len) != 1) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + return sign; +} + +template +std::string PEMSign::public_key_ser( + EVP_PKEY* pkey, + jwt::string_view sign, + std::error_code& ec) +{ + // Get the EC_KEY representing a public key and + // (optionaly) an associated private key + std::string new_sign; + ec.clear(); + + EC_KEY_uptr ec_key{EVP_PKEY_get1_EC_KEY(pkey), ec_key_deletor}; + + if (!ec_key) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + uint32_t degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key.get())); + + ec_key.reset(nullptr); + + auto char_ptr = &sign[0]; + + EC_SIG_uptr ec_sig{d2i_ECDSA_SIG(nullptr, + (const unsigned char**)&char_ptr, + static_cast(sign.length())), + ec_sig_deletor}; + + if (!ec_sig) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + const BIGNUM* ec_sig_r = nullptr; + const BIGNUM* ec_sig_s = nullptr; + + ECDSA_SIG_get0(ec_sig.get(), &ec_sig_r, &ec_sig_s); + + int r_len = BN_num_bytes(ec_sig_r); + int s_len = BN_num_bytes(ec_sig_s); + int bn_len = static_cast((degree + 7) / 8); + + if ((r_len > bn_len) || (s_len > bn_len)) { + ec = AlgorithmErrc::SigningErr; + return {}; + } + + auto buf_len = 2 * bn_len; + new_sign.resize(buf_len); + + BN_bn2bin(ec_sig_r, (unsigned char*)&new_sign[0] + bn_len - r_len); + BN_bn2bin(ec_sig_s, (unsigned char*)&new_sign[0] + buf_len - s_len); + + return new_sign; +} + +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/impl/error_codes.ipp b/externals/cpp-jwt/include/jwt/impl/error_codes.ipp new file mode 100755 index 000000000..30906b655 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/impl/error_codes.ipp @@ -0,0 +1,160 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_ERROR_CODES_IPP +#define CPP_JWT_ERROR_CODES_IPP + +namespace jwt { +// Anonymous namespace +namespace { + +/** + */ +struct AlgorithmErrCategory: std::error_category +{ + const char* name() const noexcept override + { + return "algorithms"; + } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case AlgorithmErrc::SigningErr: + return "signing failed"; + case AlgorithmErrc::VerificationErr: + return "verification failed"; + case AlgorithmErrc::KeyNotFoundErr: + return "key not provided"; + case AlgorithmErrc::NoneAlgorithmUsed: + return "none algorithm used"; + case AlgorithmErrc::InvalidKeyErr: + return "invalid key"; + }; + return "unknown algorithm error"; + } +}; + +/** + */ +struct DecodeErrorCategory: std::error_category +{ + const char* name() const noexcept override + { + return "decode"; + } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case DecodeErrc::EmptyAlgoList: + return "empty algorithm list"; + case DecodeErrc::SignatureFormatError: + return "signature format is incorrect"; + case DecodeErrc::AlgHeaderMiss: + return "missing algorithm header"; + case DecodeErrc::TypHeaderMiss: + return "missing type header"; + case DecodeErrc::TypMismatch: + return "type mismatch"; + case DecodeErrc::JsonParseError: + return "json parse failed"; + case DecodeErrc::DuplClaims: + return "duplicate claims"; + case DecodeErrc::KeyNotPresent: + return "key not present"; + case DecodeErrc::KeyNotRequiredForNoneAlg: + return "key not required for NONE algorithm"; + }; + return "unknown decode error"; + } +}; + +/** + */ +struct VerificationErrorCategory: std::error_category +{ + const char* name() const noexcept override + { + return "verification"; + } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case VerificationErrc::InvalidAlgorithm: + return "invalid algorithm"; + case VerificationErrc::TokenExpired: + return "token expired"; + case VerificationErrc::InvalidIssuer: + return "invalid issuer"; + case VerificationErrc::InvalidSubject: + return "invalid subject"; + case VerificationErrc::InvalidAudience: + return "invalid audience"; + case VerificationErrc::InvalidIAT: + return "invalid iat"; + case VerificationErrc::InvalidJTI: + return "invalid jti"; + case VerificationErrc::ImmatureSignature: + return "immature signature"; + case VerificationErrc::InvalidSignature: + return "invalid signature"; + case VerificationErrc::TypeConversionError: + return "type conversion error"; + }; + return "unknown verification error"; + } +}; + +// Create global object for the error categories +const AlgorithmErrCategory theAlgorithmErrCategory {}; + +const DecodeErrorCategory theDecodeErrorCategory {}; + +const VerificationErrorCategory theVerificationErrorCategory {}; + +} + + +// Create the AlgorithmErrc error code +inline std::error_code make_error_code(AlgorithmErrc err) +{ + return { static_cast(err), theAlgorithmErrCategory }; +} + +inline std::error_code make_error_code(DecodeErrc err) +{ + return { static_cast(err), theDecodeErrorCategory }; +} + +inline std::error_code make_error_code(VerificationErrc err) +{ + return { static_cast(err), theVerificationErrorCategory }; +} + +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/impl/jwt.ipp b/externals/cpp-jwt/include/jwt/impl/jwt.ipp new file mode 100755 index 000000000..6173d2c40 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/impl/jwt.ipp @@ -0,0 +1,882 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef JWT_IPP +#define JWT_IPP + +#include "jwt/config.hpp" +#include "jwt/detail/meta.hpp" +#include +#include + +namespace jwt { + +/** + */ +static inline void jwt_throw_exception(const std::error_code& ec); + +template +std::string to_json_str(const T& obj, bool pretty) +{ + return pretty ? obj.create_json_obj().dump(2) + : obj.create_json_obj().dump() + ; +} + + +template +std::ostream& write(std::ostream& os, const T& obj, bool pretty) +{ + pretty ? (os << std::setw(2) << obj.create_json_obj()) + : (os << obj.create_json_obj()) + ; + + return os; +} + + +template +std::ostream& operator<< (std::ostream& os, const T& obj) +{ + os << obj.create_json_obj(); + return os; +} + +//======================================================================== + +inline void jwt_header::decode(const jwt::string_view enc_str, std::error_code& ec) +{ + ec.clear(); + std::string json_str = base64_decode(enc_str); + + try { + payload_ = json_t::parse(std::move(json_str)); + } catch(const std::exception&) { + ec = DecodeErrc::JsonParseError; + return; + } + + //Look for the algorithm field + auto alg_itr = payload_.find("alg"); + if (alg_itr == payload_.end()) { + ec = DecodeErrc::AlgHeaderMiss; + return; + } + + alg_ = str_to_alg(alg_itr.value().get()); + + if (alg_ != algorithm::NONE) + { + auto itr = payload_.find("typ"); + + if (itr != payload_.end()) { + const auto& typ = itr.value().get(); + if (strcasecmp(typ.c_str(), "JWT")) { + ec = DecodeErrc::TypMismatch; + return; + } + + typ_ = str_to_type(typ); + } + } else { + //TODO: + } + + // Populate header + for (auto it = payload_.begin(); it != payload_.end(); ++it) { + auto ret = headers_.insert(it.key()); + if (!ret.second) { + ec = DecodeErrc::DuplClaims; + //ATTN: Dont stop the decode here + //Not a hard error. + } + } + + return; +} + +inline void jwt_header::decode(const jwt::string_view enc_str) +{ + std::error_code ec; + decode(enc_str, ec); + if (ec) { + throw DecodeError(ec.message()); + } + return; +} + +inline void jwt_payload::decode(const jwt::string_view enc_str, std::error_code& ec) +{ + ec.clear(); + std::string json_str = base64_decode(enc_str); + try { + payload_ = json_t::parse(std::move(json_str)); + } catch(const std::exception&) { + ec = DecodeErrc::JsonParseError; + return; + } + //populate the claims set + for (auto it = payload_.begin(); it != payload_.end(); ++it) { + auto ret = claim_names_.insert(it.key()); + if (!ret.second) { + ec = DecodeErrc::DuplClaims; + break; + } + } + + return; +} + +inline void jwt_payload::decode(const jwt::string_view enc_str) +{ + std::error_code ec; + decode(enc_str, ec); + if (ec) { + throw DecodeError(ec.message()); + } + return; +} + +inline std::string jwt_signature::encode(const jwt_header& header, + const jwt_payload& payload, + std::error_code& ec) +{ + std::string jwt_msg; + ec.clear(); + //TODO: Optimize allocations + + sign_func_t sign_fn = get_sign_algorithm_impl(header); + + std::string hdr_sign = header.base64_encode(); + std::string pld_sign = payload.base64_encode(); + std::string data = hdr_sign + '.' + pld_sign; + + auto res = sign_fn(key_, data); + + if (res.second && res.second != AlgorithmErrc::NoneAlgorithmUsed) { + ec = res.second; + return {}; + } + + std::string b64hash; + + if (!res.second) { + b64hash = base64_encode(res.first.c_str(), res.first.length()); + } + + auto new_len = base64_uri_encode(&b64hash[0], b64hash.length()); + b64hash.resize(new_len); + + jwt_msg = data + '.' + b64hash; + + return jwt_msg; +} + +inline verify_result_t jwt_signature::verify(const jwt_header& header, + const jwt::string_view hdr_pld_sign, + const jwt::string_view jwt_sign) +{ + verify_func_t verify_fn = get_verify_algorithm_impl(header); + return verify_fn(key_, hdr_pld_sign, jwt_sign); +} + + +inline sign_func_t +jwt_signature::get_sign_algorithm_impl(const jwt_header& hdr) const noexcept +{ + sign_func_t ret = nullptr; + + switch (hdr.algo()) { + case algorithm::HS256: + ret = HMACSign::sign; + break; + case algorithm::HS384: + ret = HMACSign::sign; + break; + case algorithm::HS512: + ret = HMACSign::sign; + break; + case algorithm::NONE: + ret = HMACSign::sign; + break; + case algorithm::RS256: + ret = PEMSign::sign; + break; + case algorithm::RS384: + ret = PEMSign::sign; + break; + case algorithm::RS512: + ret = PEMSign::sign; + break; + case algorithm::ES256: + ret = PEMSign::sign; + break; + case algorithm::ES384: + ret = PEMSign::sign; + break; + case algorithm::ES512: + ret = PEMSign::sign; + break; + default: + assert (0 && "Code not reached"); + }; + + return ret; +} + + + +inline verify_func_t +jwt_signature::get_verify_algorithm_impl(const jwt_header& hdr) const noexcept +{ + verify_func_t ret = nullptr; + + switch (hdr.algo()) { + case algorithm::HS256: + ret = HMACSign::verify; + break; + case algorithm::HS384: + ret = HMACSign::verify; + break; + case algorithm::HS512: + ret = HMACSign::verify; + break; + case algorithm::NONE: + ret = HMACSign::verify; + break; + case algorithm::RS256: + ret = PEMSign::verify; + break; + case algorithm::RS384: + ret = PEMSign::verify; + break; + case algorithm::RS512: + ret = PEMSign::verify; + break; + case algorithm::ES256: + ret = PEMSign::verify; + break; + case algorithm::ES384: + ret = PEMSign::verify; + break; + case algorithm::ES512: + ret = PEMSign::verify; + break; + default: + assert (0 && "Code not reached"); + }; + + return ret; +} + + +// +template +jwt_object::jwt_object( + First&& first, Rest&&... rest) +{ + static_assert (detail::meta::is_parameter_concept::value && + detail::meta::are_all_params::value, + "All constructor argument types must model ParameterConcept"); + + set_parameters(std::forward(first), std::forward(rest)...); +} + +template +void jwt_object::set_parameters( + params::detail::payload_param&& payload, Rest&&... rargs) +{ + for (const auto& elem : payload.get()) { + payload_.add_claim(std::move(elem.first), std::move(elem.second)); + } + set_parameters(std::forward(rargs)...); +} + +template +void jwt_object::set_parameters( + params::detail::secret_param secret, Rest&&... rargs) +{ + secret_.assign(secret.get().data(), secret.get().length()); + set_parameters(std::forward(rargs)...); +} + +template +void jwt_object::set_parameters( + params::detail::algorithm_param alg, Rest&&... rargs) +{ + header_.algo(alg.get()); + set_parameters(std::forward(rargs)...); +} + +template +void jwt_object::set_parameters( + params::detail::headers_param&& header, Rest&&... rargs) +{ + for (const auto& elem : header.get()) { + header_.add_header(std::move(elem.first), std::move(elem.second)); + } + + set_parameters(std::forward(rargs)...); +} + +inline void jwt_object::set_parameters() +{ + //sentinel call + return; +} + +inline jwt_object& jwt_object::add_claim(const jwt::string_view name, system_time_t tp) +{ + return add_claim( + name, + std::chrono::duration_cast< + std::chrono::seconds>(tp.time_since_epoch()).count() + ); +} + +inline jwt_object& jwt_object::remove_claim(const jwt::string_view name) +{ + payload_.remove_claim(name); + return *this; +} + +inline std::string jwt_object::signature(std::error_code& ec) const +{ + ec.clear(); + + //key/secret should be set for any algorithm except NONE + if (header().algo() != jwt::algorithm::NONE) { + if (secret_.length() == 0) { + ec = AlgorithmErrc::KeyNotFoundErr; + return {}; + } + } + + jwt_signature jws{secret_}; + return jws.encode(header_, payload_, ec); +} + +inline std::string jwt_object::signature() const +{ + std::error_code ec; + std::string res = signature(ec); + if (ec) { + throw SigningError(ec.message()); + } + return res; +} + +template +std::error_code jwt_object::verify( + const Params& dparams, + const params::detail::algorithms_param& algos) const +{ + std::error_code ec{}; + + //Verify if the algorithm set in the header + //is any of the one expected by the client. + auto fitr = std::find_if(algos.get().begin(), + algos.get().end(), + [this](const auto& elem) + { + return jwt::str_to_alg(elem) == this->header().algo(); + }); + + if (fitr == algos.get().end()) { + ec = VerificationErrc::InvalidAlgorithm; + return ec; + } + + //Check for the expiry timings + if (has_claim(registered_claims::expiration)) { + auto curr_time = + std::chrono::duration_cast< + std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + + auto p_exp = payload() + .get_claim_value(registered_claims::expiration); + + if (static_cast(curr_time) > static_cast(p_exp + dparams.leeway)) { + ec = VerificationErrc::TokenExpired; + return ec; + } + } + + //Check for issuer + if (dparams.has_issuer) + { + if (has_claim(registered_claims::issuer)) + { + const std::string& p_issuer = payload() + .get_claim_value(registered_claims::issuer); + + if (p_issuer != dparams.issuer) { + ec = VerificationErrc::InvalidIssuer; + return ec; + } + } else { + ec = VerificationErrc::InvalidIssuer; + return ec; + } + } + + //Check for audience + if (dparams.has_aud) + { + if (has_claim(registered_claims::audience)) + { + const std::string& p_aud = payload() + .get_claim_value(registered_claims::audience); + + if (p_aud != dparams.aud) { + ec = VerificationErrc::InvalidAudience; + return ec; + } + } else { + ec = VerificationErrc::InvalidAudience; + return ec; + } + } + + //Check the subject + if (dparams.has_sub) + { + if (has_claim(registered_claims::subject)) + { + const std::string& p_sub = payload() + .get_claim_value(registered_claims::subject); + if (p_sub != dparams.sub) { + ec = VerificationErrc::InvalidSubject; + return ec; + } + } else { + ec = VerificationErrc::InvalidSubject; + return ec; + } + } + + //Check for NBF + if (has_claim(registered_claims::not_before)) + { + auto curr_time = + std::chrono::duration_cast< + std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + + auto p_exp = payload() + .get_claim_value(registered_claims::not_before); + + if (static_cast(p_exp - dparams.leeway) > static_cast(curr_time)) { + ec = VerificationErrc::ImmatureSignature; + return ec; + } + } + + //Check IAT validation + if (dparams.validate_iat) { + if (!has_claim(registered_claims::issued_at)) { + ec = VerificationErrc::InvalidIAT; + return ec; + } else { + // Will throw type conversion error + auto val = payload() + .get_claim_value(registered_claims::issued_at); + (void)val; + } + } + + //Check JTI validation + if (dparams.validate_jti) { + if (!has_claim("jti")) { + ec = VerificationErrc::InvalidJTI; + return ec; + } + } + + return ec; +} + + +inline std::array +jwt_object::three_parts(const jwt::string_view enc_str) +{ + std::array result; + + size_t fpos = enc_str.find_first_of('.'); + assert (fpos != jwt::string_view::npos); + + result[0] = jwt::string_view{&enc_str[0], fpos}; + + size_t spos = enc_str.find_first_of('.', fpos + 1); + + result[1] = jwt::string_view{&enc_str[fpos + 1], spos - fpos - 1}; + + if (spos + 1 != enc_str.length()) { + result[2] = jwt::string_view{&enc_str[spos + 1], enc_str.length() - spos - 1}; + } + + return result; +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::secret_param s, Rest&&... args) +{ + dparams.secret.assign(s.get().data(), s.get().length()); + dparams.has_secret = true; + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::secret_function_param&& s, Rest&&... args) +{ + dparams.secret = s.get(*dparams.payload_ptr); + dparams.has_secret = true; + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::leeway_param l, Rest&&... args) +{ + dparams.leeway = l.get(); + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::verify_param v, Rest&&... args) +{ + dparams.verify = v.get(); + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::issuer_param i, Rest&&... args) +{ + dparams.issuer = std::move(i).get(); + dparams.has_issuer = true; + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::audience_param a, Rest&&... args) +{ + dparams.aud = std::move(a).get(); + dparams.has_aud = true; + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::subject_param s, Rest&&... args) +{ + dparams.sub = std::move(s).get(); + dparams.has_sub = true; + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_iat_param v, Rest&&... args) +{ + dparams.validate_iat = v.get(); + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_jti_param v, Rest&&... args) +{ + dparams.validate_jti = v.get(); + jwt_object::set_decode_params(dparams, std::forward(args)...); +} + +template +void jwt_object::set_decode_params(DecodeParams& dparams) +{ + (void) dparams; // prevent -Wunused-parameter with gcc + return; +} + +//================================================================== + +template +jwt_object decode(const jwt::string_view enc_str, + const params::detail::algorithms_param& algos, + std::error_code& ec, + Args&&... args) +{ + ec.clear(); + jwt_object obj; + + if (algos.get().size() == 0) { + ec = DecodeErrc::EmptyAlgoList; + return obj; + } + + struct decode_params + { + /// key to decode the JWS + bool has_secret = false; + std::string secret; + + /// Verify parameter. Defaulted to true. + bool verify = true; + + /// Leeway parameter. Defaulted to zero seconds. + uint32_t leeway = 0; + + ///The issuer + //TODO: optional type + bool has_issuer = false; + std::string issuer; + + ///The audience + //TODO: optional type + bool has_aud = false; + std::string aud; + + //The subject + //TODO: optional type + bool has_sub = false; + std::string sub; + + //Validate IAT + bool validate_iat = false; + + //Validate JTI + bool validate_jti = false; + const jwt_payload* payload_ptr = 0; + }; + + decode_params dparams{}; + + + //Signature must have atleast 2 dots + auto dot_cnt = std::count_if(std::begin(enc_str), std::end(enc_str), + [](char ch) { return ch == '.'; }); + if (dot_cnt < 2) { + ec = DecodeErrc::SignatureFormatError; + return obj; + } + + auto parts = jwt_object::three_parts(enc_str); + + //throws decode error + jwt_header hdr{}; + hdr.decode(parts[0], ec); + if (ec) { + return obj; + } + //obj.header(jwt_header{parts[0]}); + obj.header(std::move(hdr)); + + //If the algorithm is not NONE, it must not + //have more than two dots ('.') and the split + //must result in three strings with some length. + if (obj.header().algo() != jwt::algorithm::NONE) { + if (dot_cnt > 2) { + ec = DecodeErrc::SignatureFormatError; + return obj; + } + if (parts[2].length() == 0) { + ec = DecodeErrc::SignatureFormatError; + return obj; + } + } + + //throws decode error + jwt_payload payload{}; + payload.decode(parts[1], ec); + if (ec) { + return obj; + } + obj.payload(std::move(payload)); + dparams.payload_ptr = & obj.payload(); + jwt_object::set_decode_params(dparams, std::forward(args)...); + if (dparams.verify) { + try { + ec = obj.verify(dparams, algos); + } catch (const json_ns::detail::type_error&) { + ec = VerificationErrc::TypeConversionError; + } + + if (ec) return obj; + + //Verify the signature only if some algorithm was used + if (obj.header().algo() != algorithm::NONE) + { + if (!dparams.has_secret) { + ec = DecodeErrc::KeyNotPresent; + return obj; + } + jwt_signature jsign{dparams.secret}; + + // Length of the encoded header and payload only. + // Addition of '1' to account for the '.' character. + auto l = parts[0].length() + 1 + parts[1].length(); + + //MemoryAllocationError is not caught + verify_result_t res = jsign.verify(obj.header(), enc_str.substr(0, l), parts[2]); + if (res.second) { + ec = res.second; + return obj; + } + + if (!res.first) { + ec = VerificationErrc::InvalidSignature; + return obj; + } + } else { + ec = AlgorithmErrc::NoneAlgorithmUsed; + } + } + + return obj; +} + + + +template +jwt_object decode(const jwt::string_view enc_str, + const params::detail::algorithms_param& algos, + Args&&... args) +{ + std::error_code ec{}; + auto jwt_obj = decode(enc_str, + algos, + ec, + std::forward(args)...); + + if (ec) { + jwt_throw_exception(ec); + } + + return jwt_obj; +} + + +void jwt_throw_exception(const std::error_code& ec) +{ + const auto& cat = ec.category(); + + if (&cat == &theVerificationErrorCategory || + std::string(cat.name()) == std::string(theVerificationErrorCategory.name())) + { + switch (static_cast(ec.value())) + { + case VerificationErrc::InvalidAlgorithm: + { + throw InvalidAlgorithmError(ec.message()); + } + case VerificationErrc::TokenExpired: + { + throw TokenExpiredError(ec.message()); + } + case VerificationErrc::InvalidIssuer: + { + throw InvalidIssuerError(ec.message()); + } + case VerificationErrc::InvalidAudience: + { + throw InvalidAudienceError(ec.message()); + } + case VerificationErrc::InvalidSubject: + { + throw InvalidSubjectError(ec.message()); + } + case VerificationErrc::InvalidIAT: + { + throw InvalidIATError(ec.message()); + } + case VerificationErrc::InvalidJTI: + { + throw InvalidJTIError(ec.message()); + } + case VerificationErrc::ImmatureSignature: + { + throw ImmatureSignatureError(ec.message()); + } + case VerificationErrc::InvalidSignature: + { + throw InvalidSignatureError(ec.message()); + } + case VerificationErrc::TypeConversionError: + { + throw TypeConversionError(ec.message()); + } + default: + assert (0 && "Unknown error code"); + }; + } + + if (&cat == &theDecodeErrorCategory || + std::string(cat.name()) == std::string(theDecodeErrorCategory.name())) + { + switch (static_cast(ec.value())) + { + case DecodeErrc::SignatureFormatError: + { + throw SignatureFormatError(ec.message()); + } + case DecodeErrc::KeyNotPresent: + { + throw KeyNotPresentError(ec.message()); + } + case DecodeErrc::KeyNotRequiredForNoneAlg: + { + // Not an error. Just to be ignored. + break; + } + default: + { + throw DecodeError(ec.message()); + } + }; + + assert (0 && "Unknown error code"); + } + + if (&cat == &theAlgorithmErrCategory || + std::string(cat.name()) == std::string(theAlgorithmErrCategory.name())) + { + switch (static_cast(ec.value())) + { + case AlgorithmErrc::InvalidKeyErr: + { + throw InvalidKeyError(ec.message()); + } + case AlgorithmErrc::VerificationErr: + { + throw InvalidSignatureError(ec.message()); + } + case AlgorithmErrc::NoneAlgorithmUsed: + { + //Not an error actually. + break; + } + default: + assert (0 && "Unknown error code or not to be treated as an error"); + }; + } + return; +} + +} // END namespace jwt + + +#endif diff --git a/externals/cpp-jwt/include/jwt/impl/stack_alloc.ipp b/externals/cpp-jwt/include/jwt/impl/stack_alloc.ipp new file mode 100755 index 000000000..8e2ceb90e --- /dev/null +++ b/externals/cpp-jwt/include/jwt/impl/stack_alloc.ipp @@ -0,0 +1,87 @@ +/* +// The MIT License (MIT) +// +// Copyright (c) 2015 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + */ + +#ifndef STACK_ALLOC_IPP +#define STACK_ALLOC_IPP + +namespace jwt { + +template +template +char* Arena::allocate(size_t n) noexcept +{ + static_assert (reqested_alignment <= alignment, + "Requested alignment is too small for this arena"); + + assert (pointer_in_storage(ptr_) && + "No more space in the arena or it has outgrown its capacity"); + + n = align_up(n); + + if ((ptr_ + n) <= (buf_ + N)) { + char* ret = ptr_; + ptr_ += n; + + return ret; + } + + assert (0 && "Code should not reach here"); + + return nullptr; +} + +template +void Arena::deallocate(char* p, size_t n) noexcept +{ + assert (pointer_in_storage(p) && + "The address to de deleted does not lie inside the storage"); + + n = align_up(n); + + if ((p + n) == ptr_) { + ptr_ = p; + } + + return; +} + +template +T* stack_alloc::allocate(size_t n) noexcept +{ + return reinterpret_cast( + arena_.template allocate(n * sizeof(T)) + ); +} + +template +void stack_alloc::deallocate(T* p, size_t n) noexcept +{ + arena_.deallocate(reinterpret_cast(p), n); + return; +} + +} + + +#endif diff --git a/externals/cpp-jwt/include/jwt/impl/string_view.ipp b/externals/cpp-jwt/include/jwt/impl/string_view.ipp new file mode 100755 index 000000000..8368bef91 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/impl/string_view.ipp @@ -0,0 +1,339 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef JWT_STRING_VIEW_IPP +#define JWT_STRING_VIEW_IPP + +namespace jwt { + +template +auto basic_string_view::find( + const CharT* str, + size_type pos, + size_type n) const noexcept -> size_type +{ + assert (str); + assert (n < (len_ - pos) && "Comparison size out of bounds"); + + if (n == 0) { + return pos <= len_ ? pos : npos; + } + if (n <= len_) { + for (; pos <= (len_ - n); ++pos) { + if (traits_type::eq(data_[pos], str[0]) && + traits_type::compare(data_ + pos + 1, str + 1, n - 1) == 0) { + return pos; + } + } + } + + return npos; +} + +template +auto basic_string_view::rfind( + const CharT* str, + size_type pos, + size_type n) const noexcept -> size_type +{ + assert (str); + assert (pos < len_ && "Position out of bounds"); + + if (n <= len_) { + pos = std::min(len_ - n, pos); + do { + if (traits_type::eq(data_[pos], str[0]) && + traits_type::compare(data_ + pos + 1, str + 1, n - 1) == 0) { + return pos; + } + } while (pos-- != 0); + } + + return npos; +} + +template +auto basic_string_view::find( + const CharT ch, + size_type pos) const noexcept -> size_type +{ + if (pos < len_) { + for (size_type i = pos; i < len_; ++i) { + if (traits_type::eq(data_[i], ch)) return i; + } + } + return npos; +} + +template +auto basic_string_view::rfind( + const CharT ch, + size_type pos) const noexcept -> size_type +{ + if (pos < len_) { + do { + if (traits_type::eq(data_[pos], ch)) { + return pos; + } + } while (pos-- != 0); + } + + return npos; +} + +template +auto basic_string_view::find_first_of( + const CharT* str, + size_type pos, + size_type count) const noexcept -> size_type +{ + assert (str); + + for (size_type i = pos; i < len_; ++i) { + auto p = traits_type::find(str, count, data_[i]); + if (p) { + return i; + } + } + + return npos; +} + +template +auto basic_string_view::find_last_of( + const CharT* str, + size_type pos, + size_type count) const noexcept -> size_type +{ + assert (str); + assert (pos < len_ && "Position must be within the bounds of the view"); + size_type siz = len_; + + if (siz && count) { + siz = std::min(pos, siz); + + do { + auto p = traits_type::find(str, count, data_[siz]); + if (p) { + return siz; + } + } while (siz-- != 0); + } + + return npos; +} + +template +auto basic_string_view::find_first_not_of( + const CharT* str, + size_type pos, + size_type n) const noexcept -> size_type +{ + assert (str); + assert (pos < len_&& "Position must be within the bounds of the view"); + + for (size_type i = pos; i < len_; ++i) + { + auto p = traits_type::find(str, n, data_[i]); + if (!p) return i; + } + + return npos; +} + +template +auto basic_string_view::find_last_not_of( + const CharT* str, + size_type pos, + size_type n) const noexcept -> size_type +{ + assert (str); + assert (pos < len_ && "Position must be within the bounds of the view"); + + do { + for (size_type i = 0; i < n; ++i) { + if (!traits_type::eq(data_[pos], str[i])) return pos; + } + } while (pos-- != 0); + + return npos; +} + +template +auto basic_string_view::find_first_not_of( + CharT ch, + size_type pos) const noexcept -> size_type +{ + assert (pos < len_&& "Position must be within the bounds of the view"); + + for (size_type i = pos; i < len_; ++i) { + if (!traits_type::eq(data_[i], ch)) return i; + } + + return npos; +} + +template +auto basic_string_view::find_last_not_of( + CharT ch, + size_type pos) const noexcept -> size_type +{ + assert (pos < len_ && "Position must be within the bounds of the view"); + + do { + if (!traits_type::eq(data_[pos], ch)) return pos; + } while (pos-- != 0); + + return npos; +} + +// Comparison Operators + +template +bool operator== (basic_string_view a, + basic_string_view b) noexcept +{ + if (a.length() != b.length()) return false; + using traits_type = typename basic_string_view::traits_type; + using size_type = typename basic_string_view::size_type; + + for (size_type i = 0; i < a.length(); ++i) { + if (!traits_type::eq(a[i], b[i])) return false; + } + + return true; +} + +template +bool operator!= (basic_string_view a, + basic_string_view b) noexcept +{ + return !( a == b ); +} + +template +bool operator< (basic_string_view a, + basic_string_view b) noexcept +{ + return a.compare(b) < 0; +} + +template +bool operator> (basic_string_view a, + basic_string_view b) noexcept +{ + return a.compare(b) > 0; +} + +template +bool operator<= (basic_string_view a, + basic_string_view b) noexcept +{ + return a.compare(b) <= 0; +} + +template +bool operator>= (basic_string_view a, + basic_string_view b) noexcept +{ + return a.compare(b) >= 0; +} + +template +std::ostream& operator<< (std::ostream& os, basic_string_view sv) +{ + os.write(sv.data(), sv.length()); + return os; +} + +namespace { + +/* + * Copy of gcc implementation of murmurhash + * hash_bytes.cc + */ + +inline size_t +unaligned_load(const char* p) noexcept +{ + std::size_t result; + std::memcpy(&result, p, sizeof(result)); + return result; +} + +inline size_t +hash_bytes(const void* ptr, size_t len, size_t seed) noexcept +{ + const size_t m = 0x5bd1e995; + size_t hash = seed ^ len; + const char* buf = static_cast(ptr); + + // Mix 4 bytes at a time into the hash. + while(len >= 4) + { + size_t k = unaligned_load(buf); + k *= m; + k ^= k >> 24; + k *= m; + hash *= m; + hash ^= k; + buf += 4; + len -= 4; + } + + // Handle the last few bytes of the input array. + switch(len) + { + case 3: + hash ^= static_cast(buf[2]) << 16; + //FALLTHROUGH + case 2: + hash ^= static_cast(buf[1]) << 8; + //FALLTHROUGH + case 1: + hash ^= static_cast(buf[0]); + hash *= m; + }; + + // Do a few final mixes of the hash. + hash ^= hash >> 13; + hash *= m; + hash ^= hash >> 15; + return hash; +} +} + +} // END namespace jwt + +/// Provide a hash specialization +namespace std { + template <> + struct hash + { + size_t operator()(const jwt::string_view& sv) const noexcept + { + return jwt::hash_bytes((void*)sv.data(), sv.length(), static_cast(0xc70f6907UL)); + } + }; +} + +#endif diff --git a/externals/cpp-jwt/include/jwt/json/json.hpp b/externals/cpp-jwt/include/jwt/json/json.hpp new file mode 100755 index 000000000..a70aaf8cb --- /dev/null +++ b/externals/cpp-jwt/include/jwt/json/json.hpp @@ -0,0 +1,25447 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.9.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 9 +#define NLOHMANN_JSON_VERSION_PATCH 1 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // pair +// #include +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 13 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP \ +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include + +// #include + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; + +template +struct ordered_map; + +/*! +@brief ordered JSON class + +This type preserves the insertion order of object keys. + +@since version 3.9.0 +*/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using iterator_t = typename T::iterator; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, + enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +// source: https://stackoverflow.com/a/37193089/4116453 + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + std::is_constructible::value && + std::is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (std::is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (std::is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type_impl : std::false_type {}; + +template +struct is_compatible_string_type_impl < + BasicJsonType, CompatibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + std::is_constructible::value; +}; + +template +struct is_compatible_string_type + : is_compatible_string_type_impl {}; + +template +struct is_constructible_string_type_impl : std::false_type {}; + +template +struct is_constructible_string_type_impl < + BasicJsonType, ConstructibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + std::is_constructible::value; +}; + +template +struct is_constructible_string_type + : is_constructible_string_type_impl {}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < is_detected::value&& + is_detected::value&& +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + !is_iterator_traits < + iterator_traits>::value >> +{ + static constexpr bool value = + std::is_constructible::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + std::is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_detected::value&& +is_complete_type < +detected_t>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a + // base class `iterator`... Therefore it is detected as a + // ConstructibleArrayType. The real fix would be to have an Iterable + // concept. + !is_iterator_traits>::value && + + (std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{b}; + j.m_value = value; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{std::move(b)}; + j.m_value = value; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann + + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + +} // namespace nlohmann + +// #include + + +#include // uint8_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // size_t, uint8_t +#include // hash + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, j.get_binary().subtype()); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move + +// #include + +// #include + + +#include // array +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == EOF)) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); + } + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } + +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +template +auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) +{ + // Enable ADL + using std::begin; + using std::end; + + return input_adapter(begin(container), end(container)); +} + +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() && !callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (!keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{'t', 'r', 'u', 'e'}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{'n', 'u', 'l', 'l'}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore ///< ignore tags +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in, out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + + case cbor_tag_handler_t::ignore: + { + switch (current) + { + case 0xD8: + { + std::uint8_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xD9: + { + std::uint16_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xDA: + { + std::uint32_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xDB: + { + std::uint64_t len{}; + get_number(input_format_t::cbor, len); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + auto number_ia = detail::input_adapter(std::forward(number_vector)); + auto number_lexer = detail::lexer(std::move(number_ia), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + }; + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) + { + return json_pointer(ptr) /= array_idx; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + s + + "' must not begin with '0'")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)) + , is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue = true; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // isnan, isinf + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // streamsize +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_ostream +#include // basic_string +#include // vector +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(adapter) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + write_number(static_cast(0xd8)); + write_number(j.m_value.binary->subtype()); + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, + "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const std::uint64_t value) + { + if (value <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(value)); + } + else if (value <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(value)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + default: + JSON_ASSERT(false); + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + @return The size of the BSON entry + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j.m_value.number_unsigned); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + default: + JSON_ASSERT(false); + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianess, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + + private: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } + else + { + (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(3, '\0'); + (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(3, '\0'); + (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + } + + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'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'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); + + const bool is_negative = std::is_same::value && !(x >= 0); // see issue #755 + number_unsigned_t abs_value; + + unsigned int n_chars; + + if (is_negative) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + char* begin = number_buffer.data(); + char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 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, // 00..1F + 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, // 20..3F + 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, // 40..5F + 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, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 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, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // allocator +#include // pair +#include // vector + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using typename Container::iterator; + using typename Container::const_iterator; + using typename Container::size_type; + using typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + auto it = pos; + + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return pos; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } +}; + +} // namespace nlohmann + + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam BinaryType type for packed binary data for compatibility with binary +serialization formats (`std::vector` by default; will be used in +@ref binary_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): + JSON values have + [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](https://en.cppreference.com/w/cpp/named_req/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @complexity Constant. + + @since 2.1.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2020 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /*! + @brief a type for a packed binary type + + This type is a type designed to carry binary data that appears in various + serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is + simply defined as an ordered sequence of zero or more byte values. + + Additionally, as an implementation detail, the subtype of the binary data is + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). + + [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type + as: + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + None of these impose any limitations on the internal representation other + than the basic unit of storage be some type of array whose parts are + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + Binary Arrays are stored as pointers in a @ref basic_json type. That is, + for any access to array values, a pointer of the type `binary_t*` must be + dereferenced. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = nlohmann::byte_container_with_subtype; + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * object) + { + AllocatorTraits::deallocate(alloc, object, 1); + }; + std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); + JSON_ASSERT(object != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for rvalue strings + json_value(string_t&& value) + { + string = create(std::move(value)); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for rvalue objects + json_value(object_t&& value) + { + object = create(std::move(value)); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + + /// constructor for rvalue arrays + json_value(array_t&& value) + { + array = create(std::move(value)); + } + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) + { + binary = create(std::move(value)); + } + + void destroy(value_t t) noexcept + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else if (t == value_t::object) + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), + std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + default: + { + break; + } + } + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa @ref parser_callback_t for more information and examples + */ + using parse_event_t = detail::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + binary | empty array + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); + } + + /*! + @brief create a JSON value from an existing one + + This is a constructor for existing @ref basic_json types. + It does not hijack copy/move constructors, since the parameter has different + template arguments than the current ones. + + The constructor tries to convert the internal @ref m_value of the parameter. + + @tparam BasicJsonType a type such that: + - @a BasicJsonType is a @ref basic_json type. + - @a BasicJsonType has different template arguments than @ref basic_json_t. + + @param[in] val the @ref basic_json value to be converted. + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + assert_invariant(); + } + + /*! + @brief explicitly create a binary array (without subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See https://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } + + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + std::string(first.m_object->type_name()))); + } + + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + default: + break; + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] error_handler how to react on decoding errors; there are three + possible values: `strict` (throws and exception in case a decoding error + occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). + + @return string containing the serialization of the JSON value + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype + + @complexity Linear. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0; indentation character @a indent_char, option + @a ensure_ascii and exceptions added in version 3.0.0; error + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. + */ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + binary | value_t::binary + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + @sa @ref is_binary() -- returns whether JSON value is a binary array + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa @ref type() -- return the type of the JSON value (explicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::type, basic_json_t>::value, + int> = 0> + basic_json get() const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @tparam BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, detail::enable_if_t < + !std::is_same::value&& + detail::is_basic_json::value, int > = 0 > + BasicJsonType get() const + { + return *this; + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && + detail::has_from_json::value && + !detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + detail::enable_if_t < !std::is_same::value && + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 + */ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + !std::is_pointer::value&& + !std::is_same>::value&& + !std::is_same::value&& + !detail::is_basic_json::value + && !std::is_same>::value +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + && !std::is_same::value +#endif + && detail::is_detected::value + , int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a key + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + // using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a ptr + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @sa @ref contains(KeyT&&) const -- checks whether a key exists + + @since version 1.0.0 + */ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /*! + @brief check the existence of an element in a JSON object + + Check whether an element exists in a JSON object with key equivalent to + @a key. If the element is not found or the JSON value is not an object, + false is returned. + + @note This method always returns false when executed on a JSON type + that is not an object. + + @param[in] key key value to check its existence. + + @return true if an element with specified @a key exists. If no such + element with such key is found or the JSON value is not an object, + false is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains} + + @sa @ref find(KeyT&&) -- returns an iterator to an object element + @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer + + @since version 3.6.0 + */ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /*! + @brief check the existence of an element in a JSON object given a JSON pointer + + Check whether the given JSON pointer @a ptr can be resolved in the current + JSON value. + + @note This method can be executed on any JSON value type. + + @param[in] ptr JSON pointer to check its existence. + + @return true if the JSON pointer can be resolved to a stored value, false + otherwise. + + @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} + + @sa @ref contains(KeyT &&) const -- checks the existence of a key + + @since version 3.7.0 + */ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto& el : j_object.items()) + { + std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; + } + @endcode + + The `items()` function also allows to use + [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) + (C++17): + + @code{cpp} + for (auto& [key, val] : j_object.items()) + { + std::cout << "key: " << key << ", value:" << val << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). For primitive types (e.g., numbers), + `key()` returns an empty string. + + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.1.0, structured bindings support since 3.5.0. + */ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + binary | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + binary | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + binary | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + binary | An empty byte vector + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // if val is moved from, basic_json move constructor marks it null so we do not call the destructor + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return reference to the inserted element + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8, returns reference since 3.7.0 + */ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) +#ifdef JSON_HAS_CPP_17 + return m_value.array->emplace_back(std::forward(args)...); +#else + m_value.array->emplace_back(std::forward(args)...); + return m_value.array->back(); +#endif + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + return result; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)`. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. + + @liveexample{The example shows how `insert()` is used.,insert__range_object} + + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from JSON object @a j and overwrites existing keys. + + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_reference j) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from from range `[first, last)` and overwrites existing + keys. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used__range.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() + || !last.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note that two NaN values are always treated as unequal. + - Two JSON null values are equal. + + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode + + @note NaN values never compare equal to themselves or to other NaN values. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } + + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from a compatible input + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb or reading from the input @a i has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief deserialize from a pair of character iterators + + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief check if the input is valid JSON + + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return Whether the input read from @a i is valid JSON. + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /*! + @brief generate SAX events + + The SAX event lister must follow the interface of @ref json_sax. + + This function reads from a compatible input. Examples are: + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in,out] sax SAX event listener + @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) + @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. + + @return return value of the last processed SAX event + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the SAX consumer @a sax has + a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `sax_parse()` function + reading from string and processing the events with a user-defined SAX + event consumer.,sax_parse} + + @since version 3.2.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } + + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + binary | `"binary"` + discarded | `"discarded"` + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @sa @ref type() -- return the type of the JSON value + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + } + + + private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /*! + @brief create a CBOR serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): + + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 + boolean | `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | 0xA0..0xB7 + object | *size*: 23..255 | map (1 byte follow) | 0xB8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + binary | *size*: 0..23 | byte string | 0x40..0x57 + binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 + binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 + binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A + binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The following CBOR types are not used in the conversion: + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half-precision floats (0xF9) + - break (0xFF) + + @param[in] j JSON value to serialize + @return CBOR serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: + + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. + + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack for the analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /*! + @brief create a UBJSON serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. + + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} + + @sa http://ubjson.org + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + + @since version 3.1.0 + */ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + + /*! + @brief Serializes the given JSON object `j` to BSON and returns a vector + containing the corresponding BSON-representation. + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 9223372036854775808..18446744073709551615| -- | -- + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + Also, integers larger than 9223372036854775807 cannot be serialized to BSON, + and the keys may not contain U+0000, since they are serialized a + zero-terminated c-strings. + + @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > 9223372036854775807` + @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) + @throw type_error.317 if `!j.is_object()` + + @pre The input `j` is required to be an object: `j.is_object() == true`. + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in BSON format.,to_bson} + + @sa http://bsonspec.org/spec.html + @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the + analogous deserialization + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + @sa @ref to_cbor(const basic_json&) for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + */ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /*! + @brief Serializes the given JSON object `j` to BSON and forwards the + corresponding BSON-representation to the given output_adapter `o`. + @param j The JSON object to convert to BSON. + @param o The output adapter that receives the binary BSON representation. + @pre The input `j` shall be an object: `j.is_object() == true` + @sa @ref to_bson(const basic_json&) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /*! + @copydoc to_bson(const basic_json&, detail::output_adapter) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + + /*! + @brief create a JSON value from an input in CBOR format + + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. + + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Null | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] tag_handler how to treat CBOR tags (optional, error by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the + related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0; added @a tag_handler parameter since 3.9.0. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @brief create a JSON value from an input in MessagePack format + + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. + + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 + negative fixint | number_integer | 0xE0-0xFF + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for + the related UBJSON format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief create a JSON value from an input in UBJSON format + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} + + @sa http://ubjson.org + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format + + @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief Create a JSON value from an input in BSON format + + Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) + serialization format. + + The library maps BSON record types to JSON value types as follows: + + BSON type | BSON marker byte | JSON value type + --------------- | ---------------- | --------------------------- + double | 0x01 | number_float + string | 0x02 | string + document | 0x03 | object + array | 0x04 | array + binary | 0x05 | still unsupported + undefined | 0x06 | still unsupported + ObjectId | 0x07 | still unsupported + boolean | 0x08 | boolean + UTC Date-Time | 0x09 | still unsupported + null | 0x0A | null + Regular Expr. | 0x0B | still unsupported + DB Pointer | 0x0C | still unsupported + JavaScript Code | 0x0D | still unsupported + Symbol | 0x0E | still unsupported + JavaScript Code | 0x0F | still unsupported + int32 | 0x10 | number_integer + Timestamp | 0x11 | still unsupported + 128-bit decimal float | 0x13 | still unsupported + Max Key | 0x7F | still unsupported + Min Key | 0xFF | still unsupported + + @warning The mapping is **incomplete**. The unsupported mappings + are indicated in the table above. + + @param[in] i an input in BSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.114 if an unsupported BSON record type is encountered + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + BSON format to a JSON value.,from_bson} + + @sa http://bsonspec.org/spec.html + @sa @ref to_bson(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_bson(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer} + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + } + + break; + } + + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + @sa @ref merge_patch -- apply a JSON Merge Patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path + "/" + key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path + "/" + key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] apply_patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/*! +@brief user-defined to_string function for JSON values + +This function implements a user-defined to_string for JSON objects. + +@param[in] j a JSON object +@return a std::string object +*/ + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ + +/// hash value for JSON objects +template<> +struct hash +{ + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + return nlohmann::detail::hash(j); + } +}; + +/// specialization for std::less +/// @note: do not remove the space after '<', +/// see https://github.com/nlohmann/json/pull/679 +template<> +struct less<::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( + is_nothrow_move_constructible::value&& + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@param[in] n the length of string @a s +@return a JSON object + +@since version 1.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT + +// #include +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/externals/cpp-jwt/include/jwt/json/test_json.cc b/externals/cpp-jwt/include/jwt/json/test_json.cc new file mode 100755 index 000000000..d1ea50248 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/json/test_json.cc @@ -0,0 +1,24 @@ +#include +#include +#if defined( CPP_JWT_USE_VENDORED_NLOHMANN_JSON) +#include "./json.hpp" +#else +#include "nlohmann/json.hpp" +#endif +using json = nlohmann::json; + +void basic_json_test() +{ + json obj = json::object(); + obj["test"] = "value-test"; + obj["test-int"] = 42; + + std::string jstr = obj.dump(0); + std::cout << jstr << std::endl; +} + +int main() { + basic_json_test(); + + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/jwt.hpp b/externals/cpp-jwt/include/jwt/jwt.hpp new file mode 100755 index 000000000..f9fabd442 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/jwt.hpp @@ -0,0 +1,1203 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef JWT_HPP +#define JWT_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "jwt/assertions.hpp" +#include "jwt/base64.hpp" +#include "jwt/config.hpp" +#include "jwt/algorithm.hpp" +#include "jwt/string_view.hpp" +#include "jwt/parameters.hpp" +#include "jwt/exceptions.hpp" +#if defined(CPP_JWT_USE_VENDORED_NLOHMANN_JSON) +#include "jwt/json/json.hpp" +#else +#include "nlohmann/json.hpp" +#endif +// For convenience +using json_t = nlohmann::json; +using system_time_t = std::chrono::time_point; +namespace json_ns = nlohmann; + +namespace jwt { + +/** + * The type of header. + * NOTE: Only JWT is supported currently. + */ +enum class type +{ + NONE = 0, + JWT = 1, +}; + +/** + * Converts a string representing a value of type + * `enum class type` into its actual type. + */ +inline enum type str_to_type(const jwt::string_view typ) noexcept +{ + assert (typ.length() && "Empty type string"); + + if (!strcasecmp(typ.data(), "jwt")) return type::JWT; + else if(!strcasecmp(typ.data(), "none")) return type::NONE; + + JWT_NOT_REACHED("Code not reached"); +} + + +/** + * Converts an instance of type `enum class type` + * to its string equivalent. + */ +inline jwt::string_view type_to_str(SCOPED_ENUM type typ) +{ + switch (typ) { + case type::JWT: return "JWT"; + default: assert (0 && "Unknown type"); + }; + + JWT_NOT_REACHED("Code not reached"); +} + + +/** + * Registered claim names. + */ +enum class registered_claims +{ + // Expiration Time claim + expiration = 0, + // Not Before Time claim + not_before, + // Issuer name claim + issuer, + // Audience claim + audience, + // Issued At Time claim + issued_at, + // Subject claim + subject, + // JWT ID claim + jti, +}; + + +/** + * Converts an instance of type `enum class registered_claims` + * to its string equivalent representation. + */ +inline jwt::string_view reg_claims_to_str(SCOPED_ENUM registered_claims claim) noexcept +{ + switch (claim) { + case registered_claims::expiration: return "exp"; + case registered_claims::not_before: return "nbf"; + case registered_claims::issuer: return "iss"; + case registered_claims::audience: return "aud"; + case registered_claims::issued_at: return "iat"; + case registered_claims::subject: return "sub"; + case registered_claims::jti: return "jti"; + default: assert (0 && "Not a registered claim"); + }; + JWT_NOT_REACHED("Code not reached"); +} + +/** + * A helper class that enables reuse of the + * std::set container with custom comparator. + */ +struct jwt_set +{ + /** + * Transparent comparator. + * @note: C++14 only. + */ + struct case_compare + { + using is_transparent = std::true_type; + + bool operator()(const jwt::string_view lhs, const jwt::string_view rhs) const noexcept + { + int ret = lhs.compare(rhs); + return (ret < 0); + } + }; + + using header_claim_set_t = std::set; +}; + +// Fwd declaration for friend functions to specify the +// default arguments +// See: https://stackoverflow.com/a/23336823/434233 +template {}>::type> +std::string to_json_str(const T& obj, bool pretty=false); + +template +std::ostream& write(std::ostream& os, const T& obj, bool pretty=false); + +template {}>::type + > +std::ostream& operator<< (std::ostream& os, const T& obj); + + +/** + * A helper class providing the necessary functionalities + * for: + * a) converting an object into JSON string. + * b) writing to a standard output stream in JSON format. + * c) writing to standard console in JSON format using + * overloaded '<<' operator. + * + * @note: The JWT component classes inherits from this + * class to get the above functionalities. + */ +struct write_interface +{ + /** + * Converts an object of type `T` to its JSON + * string format. + * @note: Type `T` must have a member function named + * `create_json_obj`. + * The check is made at compile time. Check + * `meta::has_create_json_obj_member` for more details. + * This check is done in `Cond` template parameter. + * + * For pretty print, pass second parameter as `true`. + */ + template + friend std::string to_json_str(const T& obj, bool pretty); + + /** + * Writes the object of instance `T` in JSON format + * to standard output stream. + * The requirements on type `T` is same as that for + * `to_json_str` API. + */ + template + friend std::ostream& write( + std::ostream& os, const T& obj, bool pretty); + + /** + * An overloaded operator for writing to standard + * ostream in JSON format. + * The requirements on type `T` is same as that for + * `to_json_str` API. + * + * This API is same in functionality as that of + * `write` API. Only difference is that, there is no + * option to write the JSON representation in pretty + * format. + */ + template + friend std::ostream& operator<< (std::ostream& os, const T& obj); +}; + +/** + * Provides the functionality for doing + * base64 encoding and decoding from the + * json string. + * + * @note: The JWT component classes inherits from this + * class to get the base64 related encoding and decoding + * functionalities. + */ +template +struct base64_enc_dec +{ + /** + * Does URL safe base64 encoding + */ + std::string base64_encode(bool with_pretty = false) const + { + std::string jstr = to_json_str(*static_cast(this), with_pretty); + std::string b64_str = jwt::base64_encode(jstr.c_str(), jstr.length()); + // Do the URI safe encoding + auto new_len = jwt::base64_uri_encode(&b64_str[0], b64_str.length()); + b64_str.resize(new_len); + + return b64_str; + } + + /** + * Does URL safe base64 decoding. + */ + std::string base64_decode(const jwt::string_view encoded_str) + { + return jwt::base64_uri_decode(encoded_str.data(), encoded_str.length()); + } + +}; + + +/** + * Component class representing JWT Header. + */ +struct jwt_header: write_interface + , base64_enc_dec +{ +public: // 'tors + /* + * Default constructor. + */ + jwt_header() + { + payload_["alg"] = "none"; + payload_["typ"] = "JWT"; + } + + /** + * Constructor taking specified algorithm type + * and JWT type. + */ + jwt_header(SCOPED_ENUM algorithm alg, SCOPED_ENUM type typ = type::JWT) + : alg_(alg) + , typ_(typ) + { + payload_["typ"] = std::string(type_to_str(typ_)); + payload_["alg"] = std::string(alg_to_str(alg_)); + } + + /** + * Construct the header from an encoded string. + */ + jwt_header(const jwt::string_view enc_str) + { + this->decode(enc_str); + } + + /// Default Copy and assignment + jwt_header(const jwt_header&) = default; + jwt_header& operator=(const jwt_header&) = default; + + ~jwt_header() = default; + +public: // Exposed APIs + /** + * NOTE: Any previously saved json dump or the encoding of the + * header would not be valid after modifying the algorithm. + */ + /** + * Set the algorithm. + */ + void algo(SCOPED_ENUM algorithm alg) + { + alg_ = alg; + payload_["alg"] = std::string(alg_to_str(alg_)); + } + + /** + * Set the algorithm. String overload. + */ + void algo(const jwt::string_view sv) + { + alg_ = str_to_alg(sv.data()); + payload_["alg"] = std::string(alg_to_str(alg_)); + } + + /** + * Get the algorithm. + */ + SCOPED_ENUM algorithm algo() const noexcept + { + return alg_; + } + + /** + * NOTE: Any previously saved json dump or the encoding of the + * header would not be valid after modifying the type. + */ + /** + * Set the JWT type. + */ + void typ(SCOPED_ENUM type typ) noexcept + { + typ_ = typ; + payload_["typ"] = std::string(type_to_str(typ_)); + } + + /** + * Set the JWT type header. String overload. + */ + void typ(const jwt::string_view sv) + { + typ_ = str_to_type(sv.data()); + payload_["typ"] = std::string(type_to_str(typ_)); + } + + /** + * Get the JWT type. + */ + SCOPED_ENUM type typ() const noexcept + { + return typ_; + } + + /** + * Add a header to the JWT header. + */ + template >::value + > + > + bool add_header(const jwt::string_view hname, T&& hvalue, bool overwrite=false) + { + auto itr = headers_.find(hname); + if (itr != std::end(headers_) && !overwrite) { + return false; + } + + headers_.emplace(hname.data(), hname.length()); + payload_[hname.data()] = std::forward(hvalue); + + return true; + } + + /** + * Add a header to the JWT header. + * Overload which takes the header value as `jwt::string_view` + */ + bool add_header(const jwt::string_view cname, const jwt::string_view cvalue, bool overwrite=false) + { + return add_header(cname, + std::string{cvalue.data(), cvalue.length()}, + overwrite); + } + + /** + * Remove the header from JWT. + * NOTE: Special handling for removing type field + * from header. The typ_ is set to NONE when removed. + */ + bool remove_header(const jwt::string_view hname) + { + if (!strcasecmp(hname.data(), "typ")) { + typ_ = type::NONE; + payload_.erase(hname.data()); + return true; + } + + auto itr = headers_.find(hname); + if (itr == std::end(headers_)) { + return false; + } + payload_.erase(hname.data()); + headers_.erase(hname.data()); + + return true; + } + + /** + * Checks if header with the given name + * is present or not. + */ + bool has_header(const jwt::string_view hname) + { + if (!strcasecmp(hname.data(), "typ")) return typ_ != type::NONE; + return headers_.find(hname) != std::end(headers_); + } + + + /** + * Get the URL safe base64 encoded string + * of the header. + */ + //TODO: error code ? + std::string encode(bool pprint = false) + { + return base64_encode(pprint); + } + + /** + * Decodes the base64 encoded string to JWT header. + * @note: Overwrites the data member of this instance + * with the decoded values. + * + * This API takes an error_code to set the error instead + * of throwing an exception. + * + * @note: Exceptions related to memory allocation failures + * are not translated to an error_code. The API would + * still throw an exception in those cases. + */ + void decode(const jwt::string_view enc_str, std::error_code& ec); + + /** + * Exception throwing API version of decode. + * Throws `DecodeError` exception. + * Could also throw memory allocation failure + * exceptions. + */ + void decode(const jwt::string_view enc_str); + + /** + * Creates a `json_t` object this class instance. + * @note: Presence of this member function is a requirement + * for some interfaces (Eg: `write_interface`). + */ + const json_t& create_json_obj() const + { + return payload_; + } + +private: // Data members + /// The Algorithm to use for signature creation + SCOPED_ENUM algorithm alg_ = algorithm::NONE; + + /// The type of header + SCOPED_ENUM type typ_ = type::JWT; + + // The JSON payload object + json_t payload_; + + //Extra headers for JWS + jwt_set::header_claim_set_t headers_; +}; + + +/** + * Component class representing JWT Payload. + * The payload is nothing but a set of claims + * which are directly written into a JSON object. + */ +struct jwt_payload: write_interface + , base64_enc_dec +{ +public: // 'tors + /** + * Default constructor. + */ + jwt_payload() = default; + + /** + * Construct the payload from an encoded string. + * TODO: Throw an exception in case of error. + */ + jwt_payload(const jwt::string_view enc_str) + { + this->decode(enc_str); + } + + /// Default copy and assignment operations + jwt_payload(const jwt_payload&) = default; + jwt_payload& operator=(const jwt_payload&) = default; + + ~jwt_payload() = default; + +public: // Exposed APIs + /** + * Add a claim to the set. + * Parameters: + * cname - The name of the claim. + * cvalue - Value of the claim. + * overwrite - Over write the value if already present. + * + * @note: This overload works for all value types which + * are: + * a) _not_ an instance of type system_time_t. + * b) _not_ an instance of type jwt::string_view. + * c) can be written to `json_t` object. + */ + template >::value || + !std::is_same>::value + > + > + bool add_claim(const jwt::string_view cname, T&& cvalue, bool overwrite=false) + { + // Duplicate claim names not allowed + // if overwrite flag is set to true. + auto itr = claim_names_.find(cname); + if (itr != claim_names_.end() && !overwrite) { + return false; + } + + // Add it to the known set of claims + claim_names_.emplace(cname.data(), cname.length()); + + //Add it to the json payload + payload_[cname.data()] = std::forward(cvalue); + + return true; + } + + /** + * Adds a claim. + * This overload takes string claim value. + */ + bool add_claim(const jwt::string_view cname, const jwt::string_view cvalue, bool overwrite=false) + { + return add_claim(cname, std::string{cvalue.data(), cvalue.length()}, overwrite); + } + + /** + * Adds a claim. + * This overload takes system_time_t claim value. + * @note: Useful for providing timestamp as the claim value. + */ + bool add_claim(const jwt::string_view cname, system_time_t tp, bool overwrite=false) + { + return add_claim( + cname, + std::chrono::duration_cast< + std::chrono::seconds>(tp.time_since_epoch()).count(), + overwrite + ); + } + + /** + * Adds a claim. + * This overload takes `registered_claims` as the claim name. + */ + template , system_time_t>::value || + !std::is_same, jwt::string_view>::value + >> + bool add_claim(SCOPED_ENUM registered_claims cname, T&& cvalue, bool overwrite=false) + { + return add_claim( + reg_claims_to_str(cname), + std::forward(cvalue), + overwrite + ); + } + + /** + * Adds a claim. + * This overload takes `registered_claims` as the claim name and + * `system_time_t` as the claim value type. + */ + bool add_claim(SCOPED_ENUM registered_claims cname, system_time_t tp, bool overwrite=false) + { + return add_claim( + reg_claims_to_str(cname), + std::chrono::duration_cast< + std::chrono::seconds>(tp.time_since_epoch()).count(), + overwrite + ); + } + + /** + * Adds a claim. + * This overload takes `registered_claims` as the claim name and + * `jwt::string_view` as the claim value type. + */ + bool add_claim(SCOPED_ENUM registered_claims cname, jwt::string_view cvalue, bool overwrite=false) + { + return add_claim( + reg_claims_to_str(cname), + std::string{cvalue.data(), cvalue.length()}, + overwrite + ); + } + + /** + * Gets the claim value provided the claim value name. + * @note: The claim name used here is Case Sensitive. + * + * The template type `T` is what the user expects the value + * type to be. If the type provided is incompatible the underlying + * JSON library will throw an exception. + */ + template + decltype(auto) get_claim_value(const jwt::string_view cname) const + { + return payload_[cname.data()].get(); + } + + /** + * Gets the claim value provided the claim value name. + * This overload takes the claim name as an instance of + * type `registered_claims`. + * + * The template type `T` is what the user expects the value + * type to be. If the type provided is incompatible the underlying + * JSON library will throw an exception. + */ + template + decltype(auto) get_claim_value(SCOPED_ENUM registered_claims cname) const + { + return get_claim_value(reg_claims_to_str(cname)); + } + + /** + * Remove a claim identified by a claim name. + */ + bool remove_claim(const jwt::string_view cname) + { + auto itr = claim_names_.find(cname); + if (itr == claim_names_.end()) return false; + + claim_names_.erase(itr); + payload_.erase(cname.data()); + + return true; + } + + /** + * Remove a claim. + * Overload which takes the claim name as an instance + * of `registered_claims` type. + */ + bool remove_claim(SCOPED_ENUM registered_claims cname) + { + return remove_claim(reg_claims_to_str(cname)); + } + + /** + * Checks whether a claim is present in the payload + * or not. + * @note: Claim name is case sensitive for this API. + */ + //TODO: Not all libc++ version agrees with this + //because count() is not made const for is_transparent + //based overload + bool has_claim(const jwt::string_view cname) const noexcept + { + return claim_names_.find(cname) != std::end(claim_names_); + } + + /** + * Checks whether a claim is present in the payload or + * not. + * Overload which takes the claim name as an instance + * of `registered_claims` type. + */ + bool has_claim(SCOPED_ENUM registered_claims cname) const noexcept + { + return has_claim(reg_claims_to_str(cname)); + } + + /** + * Checks whether there is claim with a specific + * value in the payload. + */ + template + bool has_claim_with_value(const jwt::string_view cname, T&& cvalue) const + { + auto itr = claim_names_.find(cname); + if (itr == claim_names_.end()) return false; + + return (cvalue == payload_[cname.data()]); + } + + /** + * Checks whether there is claim with a specific + * value in the payload. + * Overload which takes the claim name as an instance of + * type `registered_claims`. + */ + template + bool has_claim_with_value(const SCOPED_ENUM registered_claims cname, T&& value) const + { + return has_claim_with_value(reg_claims_to_str(cname), std::forward(value)); + } + + /** + * Encodes the payload as URL safe Base64 encoded + * string. + */ + std::string encode(bool pprint = false) + { + return base64_encode(pprint); + } + + /** + * Decodes an encoded string and overwrites the payload + * as per the encoded information. + * + * This version of API reports error via std::error_code. + * + * @note: Allocation failures are still thrown out + * as exceptions. + */ + void decode(const jwt::string_view enc_str, std::error_code& ec); + + /** + * Overload of decode API which throws exception + * on any failure. + * + * Throws DecodeError on failure. + */ + void decode(const jwt::string_view enc_str); + + /** + * Creates a JSON object of the payload. + * + * The presence of this API is required for + * making it work with `write_interface`. + */ + const json_t& create_json_obj() const + { + return payload_; + } + +private: + + /// JSON object containing payload + json_t payload_; + /// The set of claim names in the payload + jwt_set::header_claim_set_t claim_names_; +}; + +/** + * Component class for representing JWT signature. + * + * Provides APIs for: + * a) Encoding header and payload to JWS string parts. + * b) Verifying the signature by matching it with header and payload + * signature. + */ +struct jwt_signature +{ +public: // 'tors + /// Default constructor + jwt_signature() = default; + + /** + * Constructor which takes the key. + */ + jwt_signature(const jwt::string_view key) + : key_(key.data(), key.length()) + { + } + + /// Default copy and assignment operator + jwt_signature(const jwt_signature&) = default; + jwt_signature& operator=(const jwt_signature&) = default; + + ~jwt_signature() = default; + +public: // Exposed APIs + /** + * Encodes the header and payload to get the + * three part JWS signature. + */ + std::string encode(const jwt_header& header, + const jwt_payload& payload, + std::error_code& ec); + + /** + * Verifies the JWS signature. + * Returns `verify_result_t` which is a pair + * of bool and error_code. + */ + verify_result_t verify(const jwt_header& header, + const jwt::string_view hdr_pld_sign, + const jwt::string_view jwt_sign); + +private: // Private implementation + /*! + */ + sign_func_t get_sign_algorithm_impl(const jwt_header& hdr) const noexcept; + + /*! + */ + verify_func_t get_verify_algorithm_impl(const jwt_header& hdr) const noexcept; + +private: // Data members; + + /// The key for creating the JWS + std::string key_; +}; + + +/** + * The main class representing the JWT object. + * It is a composition of all JWT composition classes. + * + * @note: This class does not provide all the required + * APIs in its public interface. Instead the class provides + * `header()` and `payload()` APIs. Those can be used to + * access more public APIs specific to those components. + */ +class jwt_object +{ +public: // 'tors + /** + * Default constructor. + */ + jwt_object() = default; + + /** + * Takes a variadic set of parameters. + * Each type must satisfy the + * `ParameterConcept` concept. + * + * The parameters that can be passed: + * 1. payload : Can pass a initializer list of pairs or any associative + * containers which models `MappingConcept` (see `meta::is_mapping_concept`) + * to populate claims. Use `add_claim` for more controlled additions. + * + * 2. secret : The secret to be used for generating and verification + * of JWT signature. Not required for NONE algorithm. + * + * 3. algorithm : The algorithm to be used for signing and decoding. + * + * 4. headers : Can pass a initializer list of pairs or any associative + * containers which models `MappingConcept` (see `meta::is_mapping_concept`) + * to populate header. Not much useful unless JWE is supported. + */ + template ::value>> + jwt_object(First&& first, Rest&&... rest); + +public: // Exposed static APIs + /** + * Splitsa JWT string into its three parts + * using dot('.') as the delimiter. + * + * @note: Instead of actually splitting the API + * simply provides an array of view. + */ + static std::array + three_parts(const jwt::string_view enc_str); + +public: // Exposed APIs + /** + * Returns the payload component object by reference. + */ + jwt_payload& payload() noexcept + { + return payload_; + } + + /** + * Returns the payload component object by const-reference. + */ + const jwt_payload& payload() const noexcept + { + return payload_; + } + + /** + * Sets the payload component object. + */ + void payload(const jwt_payload& p) + { + payload_ = p; + } + + /** + * Sets the payload component object. + * Takes the payload object as rvalue-reference. + */ + void payload(jwt_payload&& p) + { + payload_ = std::move(p); + } + + /** + * Sets the header component object. + */ + void header(const jwt_header& h) + { + header_ = h; + } + + /** + * Sets the header component object. + * Takes the header object as rvalue-reference. + */ + void header(jwt_header&& h) + { + header_ = std::move(h); + } + + /** + * Get the header component object as reference. + */ + jwt_header& header() noexcept + { + return header_; + } + + /** + * Get the header component object as const-reference. + */ + const jwt_header& header() const noexcept + { + return header_; + } + + /** + * Get the secret to be used for signing. + */ + std::string secret() const + { + return secret_; + } + + /** + * Set the secret to be used for signing. + */ + void secret(const jwt::string_view sv) + { + secret_.assign(sv.data(), sv.length()); + } + + /** + * Provides the glue interface for adding claim. + * @note: See `jwt_payload::add_claim` for more details. + */ + template >::value>> + jwt_object& add_claim(const jwt::string_view name, T&& value) + { + payload_.add_claim(name, std::forward(value)); + return *this; + } + + /** + * Provides the glue interface for adding claim. + * + * @note: See `jwt_payload::add_claim` for more details. + * + * Specialization for time points. + * Eg: Users can set `exp` claim to `chrono::system_clock::now()`. + */ + jwt_object& add_claim(const jwt::string_view name, system_time_t time_point); + + /** + * Provides the glue interface for adding claim. + * Overload for taking claim name as `registered_claims` instance. + * + * @note: See `jwt_payload::add_claim` for more details. + */ + template + jwt_object& add_claim(SCOPED_ENUM registered_claims cname, T&& value) + { + return add_claim(reg_claims_to_str(cname), std::forward(value)); + } + + /** + * Provides the glue interface for removing claim. + * + * @note: See `jwt_payload::remove_claim` for more details. + */ + jwt_object& remove_claim(const jwt::string_view name); + + /** + * Provides the glue interface for removing claim. + * + * @note: See `jwt_payload::remove_claim` for more details. + */ + jwt_object& remove_claim(SCOPED_ENUM registered_claims cname) + { + return remove_claim(reg_claims_to_str(cname)); + } + + /** + * Provides the glue interface for checking if a claim is present + * or not. + * + * @note: See `jwt_payload::has_claim` for more details. + */ + bool has_claim(const jwt::string_view cname) const noexcept + { + return payload().has_claim(cname); + } + + /** + * Provides the glue interface for checking if a claim is present + * or not. + * + * @note: See `jwt_payload::has_claim` for more details. + */ + bool has_claim(SCOPED_ENUM registered_claims cname) const noexcept + { + return payload().has_claim(cname); + } + + /** + * Generate the JWS for the header + payload using the secret. + * This version takes the error_code for reporting errors. + * + * @note: The API would still throw for memory allocation exceptions + * (`std::bad_alloc` or `jwt::MemoryAllocationException`) + * or exceptions thrown by user types. + */ + std::string signature(std::error_code& ec) const; + + /** + * Generate the JWS for the header + payload using the secret. + * Exception throwing version. + */ + std::string signature() const; + + /** + * Verify the signature. + * TODO: Returns an error_code instead of taking + * by reference. + */ + template + std::error_code verify( + const Params& dparams, + const params::detail::algorithms_param& algos) const; + +private: // private APIs + /** + */ + template + void set_parameters(Args&&... args); + + /** + */ + template + void set_parameters(params::detail::payload_param&&, Rest&&...); + + /** + */ + template + void set_parameters(params::detail::secret_param, Rest&&...); + + /** + */ + template + void set_parameters(params::detail::algorithm_param, Rest&&...); + + /** + */ + template + void set_parameters(params::detail::headers_param&&, Rest&&...); + + /** + */ + void set_parameters(); + +public: //TODO: Not good + /// Decode parameters + template + static void set_decode_params(DecodeParams& dparams, params::detail::secret_param s, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::secret_function_param&& s, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::leeway_param l, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::verify_param v, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::issuer_param i, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::audience_param a, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::subject_param a, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::validate_iat_param v, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams, params::detail::validate_jti_param v, Rest&&... args); + + template + static void set_decode_params(DecodeParams& dparams); + +private: // Data Members + + /// JWT header section + jwt_header header_; + + /// JWT payload section + jwt_payload payload_; + + /// The secret key + std::string secret_; +}; + +/** + * Decode the JWT signature to create the `jwt_object`. + * This version reports error back using `std::error_code`. + * + * If any of the registered claims are found in wrong format + * then sets TypeConversion error in the error_code. + * + * NOTE: Memory allocation exceptions are not caught. + * + * Optional parameters that can be passed: + * 1. verify : A boolean flag to indicate whether + * the signature should be verified or not. + * Set to `true` by default. + * + * 2. leeway : Number of seconds that can be added (in case of exp) + * or subtracted (in case of nbf) to be more lenient. + * Set to `0` by default. + * + * 3. algorithms : Takes in a sequence of algorithms which the client + * expects the signature to be decoded with. + * + * 4. aud : The audience the client expects to be in the claim. + * NOTE: It is case-sensitive. + * + * 5. issuer: The issuer the client expects to be in the claim. + * NOTE: It is case-sensitive + * + * 6. sub: The subject the client expects to be in the claim. + * + * 7. validate_iat: Checks if IAT claim is present or not + * and the type is uint64_t or not. If claim is not present + * then set InvalidIAT error. + * + * 8. validate_jti: Checks if jti claim is present or not. + */ +template +jwt_object decode(const jwt::string_view enc_str, + const params::detail::algorithms_param& algos, + std::error_code& ec, + Args&&... args); + +/** + * Decode the JWT signature to create the `jwt_object`. + * This version reports error back by throwing exceptions. + */ +template +jwt_object decode(const jwt::string_view enc_str, + const params::detail::algorithms_param& algos, + Args&&... args); + + +} // END namespace jwt + + +#include "jwt/impl/jwt.ipp" + +#endif diff --git a/externals/cpp-jwt/include/jwt/parameters.hpp b/externals/cpp-jwt/include/jwt/parameters.hpp new file mode 100755 index 000000000..9b9417164 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/parameters.hpp @@ -0,0 +1,451 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_PARAMETERS_HPP +#define CPP_JWT_PARAMETERS_HPP + +#include +#include +#include +#include +#include +#include + +#include "jwt/algorithm.hpp" +#include "jwt/detail/meta.hpp" +#include "jwt/string_view.hpp" + +namespace jwt { + +using system_time_t = std::chrono::time_point; + +namespace params { + + +namespace detail { +/** + * Parameter for providing the payload. + * Takes a Mapping concept representing + * key-value pairs. + * + * NOTE: MappingConcept allows only strings + * for both keys and values. Use `add_header` + * API of `jwt_object` otherwise. + * + * Modeled as ParameterConcept. + */ +template +struct payload_param +{ + payload_param(MappingConcept&& mc) + : payload_(std::forward(mc)) + {} + + MappingConcept get() && { return std::move(payload_); } + const MappingConcept& get() const& { return payload_; } + + MappingConcept payload_; +}; + +/** + * Parameter for providing the secret key. + * Stores only the view of the provided string + * as string_view. Later the implementation may or + * may-not copy it. + * + * Modeled as ParameterConcept. + */ +struct secret_param +{ + secret_param(string_view sv) + : secret_(sv) + {} + + string_view get() { return secret_; } + string_view secret_; +}; + +template +struct secret_function_param +{ + T get() const { return fun_; } + template + std::string get(U&& u) const { return fun_(u);} + T fun_; +}; + +/** + * Parameter for providing the algorithm to use. + * The parameter can accept either the string representation + * or the enum class. + * + * Modeled as ParameterConcept. + */ +struct algorithm_param +{ + algorithm_param(const string_view alg) + : alg_(str_to_alg(alg)) + {} + + algorithm_param(jwt::algorithm alg) + : alg_(alg) + {} + + jwt::algorithm get() const noexcept + { + return alg_; + } + + typename jwt::algorithm alg_; +}; + +/** + * Parameter for providing additional headers. + * Takes a mapping concept representing + * key-value pairs. + * + * Modeled as ParameterConcept. + */ +template +struct headers_param +{ + headers_param(MappingConcept&& mc) + : headers_(std::forward(mc)) + {} + + MappingConcept get() && { return std::move(headers_); } + const MappingConcept& get() const& { return headers_; } + + MappingConcept headers_; +}; + +/** + */ +struct verify_param +{ + verify_param(bool v) + : verify_(v) + {} + + bool get() const { return verify_; } + + bool verify_; +}; + +/** + */ +template +struct algorithms_param +{ + algorithms_param(Sequence&& seq) + : seq_(std::forward(seq)) + {} + + Sequence get() && { return std::move(seq_); } + const Sequence& get() const& { return seq_; } + + Sequence seq_; +}; + +/** + */ +struct leeway_param +{ + leeway_param(uint32_t v) + : leeway_(v) + {} + + uint32_t get() const noexcept { return leeway_; } + + uint32_t leeway_; +}; + +/** + */ +struct audience_param +{ + audience_param(std::string aud) + : aud_(std::move(aud)) + {} + + const std::string& get() const& noexcept { return aud_; } + std::string get() && noexcept { return aud_; } + + std::string aud_; +}; + +/** + */ +struct issuer_param +{ + issuer_param(std::string iss) + : iss_(std::move(iss)) + {} + + const std::string& get() const& noexcept { return iss_; } + std::string get() && noexcept { return iss_; } + + std::string iss_; +}; + +/** + */ +struct subject_param +{ + subject_param(std::string sub) + : sub_(std::move(sub)) + {} + + const std::string& get() const& noexcept { return sub_; } + std::string get() && noexcept { return sub_; } + + std::string sub_; +}; + +/** + */ +struct validate_iat_param +{ + validate_iat_param(bool v) + : iat_(v) + {} + + bool get() const noexcept { return iat_; } + + bool iat_; +}; + +/** + */ +struct validate_jti_param +{ + validate_jti_param(bool v) + : jti_(v) + {} + + bool get() const noexcept { return jti_; } + + bool jti_; +}; + +/** + */ +struct nbf_param +{ + nbf_param(const jwt::system_time_t tp) + : duration_(std::chrono::duration_cast< + std::chrono::seconds>(tp.time_since_epoch()).count()) + {} + + nbf_param(const uint64_t epoch) + : duration_(epoch) + {} + + uint64_t get() const noexcept { return duration_; } + + uint64_t duration_; +}; + +} // END namespace detail + +// Useful typedef +using param_init_list_t = std::initializer_list>; +using param_seq_list_t = std::initializer_list; + + +/** + */ +inline detail::payload_param> +payload(const param_init_list_t& kvs) +{ + std::unordered_map m; + + for (const auto& elem : kvs) { + m.emplace(elem.first.data(), elem.second.data()); + } + + return { std::move(m) }; +} + +/** + */ +template +detail::payload_param +payload(MappingConcept&& mc) +{ + static_assert (jwt::detail::meta::is_mapping_concept::value, + "Template parameter does not meet the requirements for MappingConcept."); + + return { std::forward(mc) }; +} + + +/** + */ +inline detail::secret_param secret(const string_view sv) +{ + return { sv }; +} + +template +inline std::enable_if_t::value, detail::secret_function_param> +secret(T&& fun) +{ + return detail::secret_function_param{ fun }; +} + +/** + */ +inline detail::algorithm_param algorithm(const string_view sv) +{ + return { sv }; +} + +/** + */ +inline detail::algorithm_param algorithm(jwt::algorithm alg) +{ + return { alg }; +} + +/** + */ +inline detail::headers_param> +headers(const param_init_list_t& kvs) +{ + std::map m; + + for (const auto& elem : kvs) { + m.emplace(elem.first.data(), elem.second.data()); + } + + return { std::move(m) }; +} + +/** + */ +template +detail::headers_param +headers(MappingConcept&& mc) +{ + static_assert (jwt::detail::meta::is_mapping_concept::value, + "Template parameter does not meet the requirements for MappingConcept."); + + return { std::forward(mc) }; +} + +/** + */ +inline detail::verify_param +verify(bool v) +{ + return { v }; +} + +/** + */ +inline detail::leeway_param +leeway(uint32_t l) +{ + return { l }; +} + +/** + */ +inline detail::algorithms_param> +algorithms(const param_seq_list_t& seq) +{ + std::vector vec; + vec.reserve(seq.size()); + + for (const auto& e: seq) { vec.emplace_back(e.data(), e.length()); } + + return { std::move(vec) }; +} + +template +detail::algorithms_param +algorithms(SequenceConcept&& sc) +{ + return { std::forward(sc) }; +} + +/** + */ +inline detail::audience_param +aud(const jwt::string_view aud) +{ + return { aud.data() }; +} + +/** + */ +inline detail::issuer_param +issuer(const jwt::string_view iss) +{ + return { iss.data() }; +} + +/** + */ +inline detail::subject_param +sub(const jwt::string_view subj) +{ + return { subj.data() }; +} + +/** + */ +inline detail::validate_iat_param +validate_iat(bool v) +{ + return { v }; +} + +/** + */ +inline detail::validate_jti_param +validate_jti(bool v) +{ + return { v }; +} + +/** + */ +inline detail::nbf_param +nbf(const system_time_t tp) +{ + return { tp }; +} + +/** + */ +inline detail::nbf_param +nbf(const uint64_t epoch) +{ + return { epoch }; +} + +} // END namespace params +} // END namespace jwt + +#endif diff --git a/externals/cpp-jwt/include/jwt/short_string.hpp b/externals/cpp-jwt/include/jwt/short_string.hpp new file mode 100755 index 000000000..4a16d2f82 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/short_string.hpp @@ -0,0 +1,38 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef CPP_JWT_SHORT_STRING_HPP +#define CPP_JWT_SHORT_STRING_HPP + +#include +#include "jwt/stack_alloc.hpp" + +namespace jwt { +/* + * A basic_string implementation using stack allocation. + */ +template +using short_string = std::basic_string, stack_alloc>; + +} + +#endif diff --git a/externals/cpp-jwt/include/jwt/stack_alloc.hpp b/externals/cpp-jwt/include/jwt/stack_alloc.hpp new file mode 100755 index 000000000..fb28a833f --- /dev/null +++ b/externals/cpp-jwt/include/jwt/stack_alloc.hpp @@ -0,0 +1,200 @@ +/* +// The MIT License (MIT) +// +// Copyright (c) 2015 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + */ + +#ifndef STACK_ALLOC_HPP +#define STACK_ALLOC_HPP + +/* + * Based on Howard Hinnants awesome allocator boilerplate code + * https://howardhinnant.github.io/short_alloc.h + */ + +#include +#include + +namespace jwt { + +/* + */ +template < + /// Size of the stack allocated byte buffer. + size_t N, + /// The alignment required for the buffer. + size_t alignment = alignof(std::max_align_t) +> +class Arena +{ +public: // 'tors + Arena() noexcept + : ptr_(buf_) + { + static_assert (alignment <= alignof(std::max_align_t), + "Alignment chosen is more than the maximum supported alignment"); + } + + /// Non copyable and assignable + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + + ~Arena() + { + ptr_ = nullptr; + } + +public: // Public APIs + + /* + * Reserves space within the buffer of size atleast 'n' + * bytes. + * More bytes maybe reserved based on the alignment requirements. + * + * Returns: + * 1. The pointer within the storage buffer where the object can be constructed. + * 2. nullptr if space cannot be reserved for requested number of bytes + * (+ alignment padding if applicable) + */ + template < + /// The requested alignment for this allocation. + /// Must be less than or equal to the 'alignment'. + size_t requested_alignment + > + char* allocate(size_t n) noexcept; + + /* + * Free back the space pointed by p within the storage buffer. + */ + void deallocate(char* p, size_t n) noexcept; + + /* + * The size of the internal storage buffer. + */ + constexpr static size_t size() noexcept + { + return N; + } + + /* + * Returns number of remaining bytes within the storage buffer + * that can be used for further allocation requests. + */ + size_t used() const noexcept + { + return static_cast(ptr_ - buf_); + } + +private: // Private member functions + + /* + * A check to determine if the pointer 'p' + * points to a region within storage. + */ + bool pointer_in_storage(char* p) const noexcept + { + return (buf_ <= p) && (p <= (buf_ + N)); + } + + /* + * Rounds up the number to the next closest number + * as per the alignment. + */ + constexpr static size_t align_up(size_t n) noexcept + { + return (n + (alignment - 1)) & ~(alignment - 1); + } + +private: // data members + /// Storage + alignas(alignment) char buf_[N]; + + /// Current allocation pointer within storage + char* ptr_ = nullptr; +}; + + + +/* + */ +template < + /// The allocator for type T + typename T, + /// Number of bytes for the arena + size_t N, + /// Alignment of the arena + size_t align = alignof(std::max_align_t) +> +class stack_alloc +{ +public: // typedefs + using value_type = T; + using arena_type = Arena; + + static auto constexpr alignment = align; + static auto constexpr size = N; + +public: // 'tors + stack_alloc(arena_type& a) + : arena_(a) + { + } + + stack_alloc(const stack_alloc&) = default; + stack_alloc& operator=(const stack_alloc&) = delete; + + template + stack_alloc(const stack_alloc& other) + : arena_(other.arena_) + { + } + + template + struct rebind { + using other = stack_alloc; + }; + +public: // Exposed APIs + + /* + * Allocate memory of 'n' bytes for object + * of type 'T' + */ + T* allocate(size_t n) noexcept; + + /* + * Deallocate the storage reserved for the object + * of type T pointed by pointer 'p' + */ + void deallocate(T* p, size_t n) noexcept; + +private: // Private APIs + +private: // Private data members + /// The arena + arena_type& arena_; +}; + +} // END namespace jwt + +#include "jwt/impl/stack_alloc.ipp" + +#endif diff --git a/externals/cpp-jwt/include/jwt/string_view.hpp b/externals/cpp-jwt/include/jwt/string_view.hpp new file mode 100755 index 000000000..bbe343cbc --- /dev/null +++ b/externals/cpp-jwt/include/jwt/string_view.hpp @@ -0,0 +1,381 @@ +/* +Copyright (c) 2017 Arun Muralidharan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +#ifndef JWT_STRING_VIEW_HPP +#define JWT_STRING_VIEW_HPP + +#if defined(__cpp_lib_string_view) + +#include + +namespace jwt { + using string_view = std::string_view; +} + +#else // defined(__cpp_lib_string_view) + +#include +#include +#include + +namespace jwt { + +/* + * Implements c++17 string_view. + * Could have used boost::string_ref, but wanted to + * keep boost dependency off from this library. + */ + +template < + typename CharT, + typename Traits = std::char_traits +> +class basic_string_view +{ +public: // Member Types + using traits_type = std::char_traits; + using value_type = CharT; + using pointer = const CharT*; + using const_pointer = const CharT*; + using reference = const CharT&; + using const_reference = const CharT&; + using iterator = const CharT*; + using const_iterator = const CharT*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + + static constexpr size_type npos = size_type(-1); + +public: // 'tors + /// The default constructor; + basic_string_view() = default; + + /// Construct from string literal + basic_string_view(const CharT* str) noexcept + : data_(str) + , len_(str ? traits_type::length(str) : 0) + { + } + + /// Construct from CharT pointer and provided length + basic_string_view(const CharT* p, size_type len) noexcept + : data_(p) + , len_(len) + { + } + + /// Construct from std::string + template + basic_string_view( + const std::basic_string& str) noexcept + : data_(str.data()) + , len_(str.length()) + { + } + + /// Copy constructor + basic_string_view(const basic_string_view&) = default; + + /// Assignment operator + basic_string_view& operator=(const basic_string_view&) = default; + + /// Destructor + ~basic_string_view() + { + data_ = nullptr; + len_ = 0; + } + +public: // Exposed APIs + /// Iterator Member Functions + + iterator begin() const noexcept { return data_; } + iterator end() const noexcept { return data_ + len_; } + + iterator rbegin() const noexcept { return reverse_iterator(end()); } + iterator rend() const noexcept { return reverse_iterator(begin()); } + + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_iterator crbegin() const noexcept { return rbegin(); } + const_iterator crend() const noexcept { return rend(); } + + /// Capacity Member Functions + + size_type length() const noexcept { return len_; } + size_type size() const noexcept { return len_; } + + size_type max_size() const noexcept + { + return (npos - sizeof(size_type) - sizeof(void*)) + / sizeof(value_type) / 4; + } + + bool empty() const noexcept { return len_ == 0; } + + /// Element Access Member Functions + const_reference operator[](size_type idx) const noexcept + { + assert(idx < len_ && "string_view subscript out of range"); + return data_[idx]; + } + + // NOTE: 'at' not supported + //CharT at(size_type idx) const; + + const_reference front() const noexcept + { + return data_[0]; + } + + const_reference back() const noexcept + { + return data_[len_ - 1]; + } + + const_pointer data() const noexcept + { + return data_; + } + + /// Modifier Member Functions + void remove_prefix(size_type n) noexcept + { + assert (n < len_ && "Data would point out of bounds"); + data_ += n; + len_ -= n; + } + + void remove_suffix(size_type n) noexcept + { + assert (n < len_ && "Suffix length more than data length"); + len_ -= n; + } + + void swap(basic_string_view& other) + { + std::swap(data_, other.data_); + std::swap(len_, other.len_); + } + + /// String Operation Member Functions + + template + explicit operator std::basic_string() const + { + return {data_, len_}; + } + + // NOTE: Does not throw + size_type copy(CharT* dest, size_type n, size_type pos = 0) const noexcept + { + assert (pos < len_ && n < len_); + size_type to_copy = std::min(n, len_ - pos); + + for (size_type i = 0; i < to_copy; i++) { + dest[i] = data_[i + pos]; + } + + return to_copy; + } + + // NOTE: Does not throw + basic_string_view substr(size_type pos, size_type n = npos) const noexcept + { + assert (pos < len_ && "Start position should be less than length of the view"); + assert (n == npos ? 1 : (n - pos) < len_ && + "Substring length asked for is more than the view length"); + + if (n == npos) n = len_; + + return basic_string_view{data_ + pos, n}; + } + + /// Comparison Member Functions + int compare(const basic_string_view& other) const noexcept + { + int ret = traits_type::compare(data_, other.data_, std::min(len_, other.len_)); + if (ret == 0) { + ret = compare_length(len_, other.len_); + } + return ret; + } + + int compare(size_type pos, size_type n, basic_string_view other) const noexcept + { + return substr(pos, n).compare(other); + } + + int compare(const CharT* str) const noexcept + { + return compare(basic_string_view{str}); + } + + int compare(size_type pos, size_type n, const CharT* str) const noexcept + { + return compare(pos, n, basic_string_view{str}); + } + + int compare(size_type pos, size_type n1, const CharT* str, size_type n2) const noexcept + { + return compare(pos, n1, basic_string_view{str, n2}); + } + + /// Find operations + size_type find(const CharT* str, size_type pos, size_type n) const noexcept; + + size_type find(const CharT ch, size_type pos) const noexcept; + + size_type find(basic_string_view sv, size_type pos = 0) const noexcept + { + return find(sv.data(), pos, sv.length()); + } + + size_type find(const CharT* str, size_type pos = 0) const noexcept + { + return find(str, pos, traits_type::length(str)); + } + + size_type rfind(const CharT* str, size_type pos, size_type n) const noexcept; + + size_type rfind(const CharT ch, size_type pos) const noexcept; + + size_type rfind(basic_string_view sv, size_type pos = 0) const noexcept + { + return rfind(sv.data(), pos, sv.length()); + } + + size_type rfind(const CharT* str, size_type pos = 0) const noexcept + { + return rfind(str, pos, traits_type::length(str)); + } + + size_type find_first_of(const CharT* str, size_type pos, size_type count) const noexcept; + + size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept + { + return find_first_of(str.data(), pos, str.length()); + } + + size_type find_first_of(CharT ch, size_type pos = 0) const noexcept + { + return find(ch, pos); + } + + size_type find_first_of(const CharT* str, size_type pos = 0) const noexcept + { + return find_first_of(str, pos, traits_type::length(str)); + } + + size_type find_last_of(const CharT* str, size_type pos, size_type count) const noexcept; + + size_type find_last_of(basic_string_view str, size_type pos = npos) const noexcept + { + return find_last_of(str.data(), (pos == npos ? len_ - 1 : pos), str.length()); + } + + size_type find_last_of(CharT ch, size_type pos = npos) const noexcept + { + return rfind(ch, pos == npos ? len_ - 1 : pos); + } + + size_type find_last_of(const CharT* str, size_type pos = npos) const noexcept + { + return find_last_of(str, (pos == npos ? len_ - 1 : pos), traits_type::length(str)); + } + + size_type find_first_not_of(const CharT* str, size_type pos, size_type n) const noexcept; + + size_type find_first_not_of(CharT ch, size_type pos) const noexcept; + + size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept + { + return find_first_not_of(str.data(), pos, str.length()); + } + + size_type find_first_not_of(const CharT* str, size_type pos = 0) const noexcept + { + return find_first_not_of(str, pos, traits_type::length(str)); + } + + size_type find_last_not_of(const CharT* str, size_type pos, size_type n) const noexcept; + + size_type find_last_not_of(CharT ch, size_type pos) const noexcept; + + size_type find_last_not_of(basic_string_view str, size_type pos = npos) const noexcept + { + return find_last_not_of(str.data(), (pos == npos ? len_ - 1 : pos), str.length()); + } + + size_type find_last_not_of(const CharT* str, size_type pos = npos) const noexcept + { + return find_last_not_of(str, (pos == npos ? len_ - 1 : pos), traits_type::length(str)); + } + + /// Comparison operators Member Functions + /* + friend bool operator== (basic_string_view a, basic_string_view b) noexcept; + + friend bool operator!= (basic_string_view a, basic_string_view b) noexcept; + + friend bool operator< (basic_string_view a, basic_string_view b) noexcept; + + friend bool operator> (basic_string_view a, basic_string_view b) noexcept; + + friend bool operator<= (basic_string_view a, basic_string_view b) noexcept; + + friend bool operator>= (basic_string_view a, basic_string_view b) noexcept; + */ + +private: // private implementations + + static constexpr int compare_length(size_type n1, size_type n2) noexcept + { + return static_cast(n1 - n2) > std::numeric_limits::max() + ? std::numeric_limits::max() + : static_cast(n1 - n2) < std::numeric_limits::min() + ? std::numeric_limits::min() + : static_cast(n1 - n2) + ; + } + +private: + // This is what view is basically... + const char* data_ = nullptr; + size_type len_ = 0; +}; + + +/// Helper typedef +using string_view = basic_string_view; + + +} // END namespace jwt + +#include "jwt/impl/string_view.ipp" + +#endif // defined(__cpp_lib_string_view) + +#endif diff --git a/externals/cpp-jwt/include/jwt/test/compile.txt b/externals/cpp-jwt/include/jwt/test/compile.txt new file mode 100755 index 000000000..b2cc8eb8c --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/compile.txt @@ -0,0 +1 @@ +g++ -std=c++14 -I /usr/local/Cellar/openssl/1.0.2j/include/ -I /Users/amuralid/dev_test/cpp-jwt/include/ -o test_rsa test_rsa.cc -L /usr/local/Cellar//openssl/1.0.2j/lib/ -lssl -lcrypto diff --git a/externals/cpp-jwt/include/jwt/test/test_base64 b/externals/cpp-jwt/include/jwt/test/test_base64 new file mode 100755 index 0000000000000000000000000000000000000000..25becbdd5c715d750e952d1d892a919b4f3b550e GIT binary patch literal 42000 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 z +#include +#include +#include "jwt/base64.hpp" + +void base64_test_encode() +{ + std::string input = "ArunMu"; + std::string output = jwt::base64_encode(input.c_str(), input.length()); + assert (output == "QXJ1bk11"); + + input = "Something really strange!!"; + output = jwt::base64_encode(input.c_str(), input.length()); + assert (output == "U29tZXRoaW5nIHJlYWxseSBzdHJhbmdlISE="); + + input = "Do you want to know something more stranger ????"; + output = jwt::base64_encode(input.c_str(), input.length()); + assert (output == "RG8geW91IHdhbnQgdG8ga25vdyBzb21ldGhpbmcgbW9yZSBzdHJhbmdlciA/Pz8/"); + + input = R"({"a" : "b", "c" : [1,2,3,4,5]})"; + output = jwt::base64_encode(input.c_str(), input.length()); + assert (output == "eyJhIiA6ICJiIiwgImMiIDogWzEsMiwzLDQsNV19"); +} + +void base64_test_decode() +{ + std::string input = "QXJ1bk11"; + std::string output = jwt::base64_decode(input.c_str(), input.length()); + assert (output == "ArunMu"); + + input = "U29tZXRoaW5nIHJlYWxseSBzdHJhbmdlISE="; + output = jwt::base64_decode(input.c_str(), input.length()); + assert (output == "Something really strange!!"); + + input = "RG8geW91IHdhbnQgdG8ga25vdyBzb21ldGhpbmcgbW9yZSBzdHJhbmdlciA/Pz8/"; + output = jwt::base64_decode(input.c_str(), input.length()); + assert (output == "Do you want to know something more stranger ????"); + + input = "eyJhIiA6ICJiIiwgImMiIDogWzEsMiwzLDQsNV19"; + output = jwt::base64_decode(input.c_str(), input.length()); + assert (output == R"({"a" : "b", "c" : [1,2,3,4,5]})"); +} + +int main() { + base64_test_encode(); + base64_test_decode(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_evp.c b/externals/cpp-jwt/include/jwt/test/test_evp.c new file mode 100755 index 000000000..6c8597c7a --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/test_evp.c @@ -0,0 +1,45 @@ +#include +#include + +int main(int argc, char *argv[]) + { + EVP_MD_CTX *mdctx; + const EVP_MD *md; + char mess1[] = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaXNzIjoiYXJ1bi5jb20iLCJ0aW1lX3N0ciI6Ijg6MThwbSAyNCBOb3YgMjAxNyIsIndoZXJlIjoiYWlycG9ydCJ9"; + unsigned char md_value[EVP_MAX_MD_SIZE]; + int md_len, i; + + //OpenSSL_add_all_digests(); + + if(!argv[1]) { + printf("Usage: mdtest digestname\n"); + exit(1); + } + + md = EVP_sha256(); + + if(!md) { + printf("Unknown message digest %s\n", argv[1]); + exit(1); + } + + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, md, NULL); + EVP_DigestUpdate(mdctx, mess1, strlen(mess1)); + EVP_DigestFinal_ex(mdctx, md_value, &md_len); + EVP_MD_CTX_destroy(mdctx); + + printf("Dig: %s\n", md_value); + printf("Dig: %d\n", md_len); + + printf("Digest is: "); + for(i = 0; i < md_len; i++) + printf("%02x", md_value[i]); + printf("\n"); + + d2i_ECDSA_SIG(NULL, (const unsigned char **)&md_value[0], md_len); + + /* Call this once before exit. */ + EVP_cleanup(); + exit(0); + } diff --git a/externals/cpp-jwt/include/jwt/test/test_hmac b/externals/cpp-jwt/include/jwt/test/test_hmac new file mode 100755 index 0000000000000000000000000000000000000000..314a8193bdd29b11f258424ab6230367d15725c8 GIT binary patch literal 44936 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 +#include "jwt/algorithm.hpp" + +void basic_hmac_test() +{ + jwt::string_view sv = "secret" ; + jwt::string_view d = "Some random data string"; + auto res = jwt::HMACSign::sign(sv, d); + + std::cout << res.first << std::endl; +} + +int main() { + basic_hmac_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_decode b/externals/cpp-jwt/include/jwt/test/test_jwt_decode new file mode 100755 index 0000000000000000000000000000000000000000..a79d91039c6aba3cf3571fd355943e8b990a8313 GIT binary patch literal 782988 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�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 pE8e2t^fs +#include "jwt/jwt.hpp" + +void basic_decode_test() +{ + // Create header + jwt::jwt_header hdr; + hdr = jwt::jwt_header{jwt::algorithm::HS256}; + + // Create payload + jwt::jwt_payload jp; + jp.add_claim("sub", "1234567890"); + jp.add_claim("name", "John Doe"); + jp.add_claim("admin", true); + + jwt::jwt_signature sgn{"secret"}; + std::error_code ec{}; + auto res = sgn.encode(hdr, jp, ec); + std::cout << res << std::endl; + + using namespace jwt::params; + + std::cout << "DECODE: \n"; + jwt::decode(res, algorithms({"none", "HS256"}), ec, verify(false), secret("secret")); +} + +int main() { + basic_decode_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_header b/externals/cpp-jwt/include/jwt/test/test_jwt_header new file mode 100755 index 0000000000000000000000000000000000000000..2320623ef92024f8482f425565d6ea8407fc4777 GIT binary patch literal 727180 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

|_}fd5*5o#)i_p8SxdG&Hcpos(V7 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 Vu*Ijz<6?qu+gq}!l69B<{ts*yPL2Qo literal 0 HcmV?d00001 diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_header.cc b/externals/cpp-jwt/include/jwt/test/test_jwt_header.cc new file mode 100755 index 000000000..64e9792f6 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/test_jwt_header.cc @@ -0,0 +1,19 @@ +#include +#include "jwt/jwt.hpp" + +void test_basic_header() +{ + jwt::jwt_header hdr; + hdr = jwt::jwt_header{jwt::algorithm::HS256}; + std::string jstr = to_json_str(hdr); + std::cout << jstr << std::endl; + + std::string enc_str = hdr.base64_encode(); + std::cout << "Base64: " << enc_str << std::endl; + std::cout << "Decoded: " << hdr.base64_decode(enc_str) << std::endl; +} + +int main() { + test_basic_header(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_object b/externals/cpp-jwt/include/jwt/test/test_jwt_object new file mode 100755 index 0000000000000000000000000000000000000000..d26912e2405444292eece771974c5d1c920062bd GIT binary patch literal 912592 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�_jC%g3wLo!RCJMfmZL&_ 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⪼9nB`*8n6`jc1k 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 literal 0 HcmV?d00001 diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_object.cc b/externals/cpp-jwt/include/jwt/test/test_jwt_object.cc new file mode 100755 index 000000000..a0e6ceea6 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/test_jwt_object.cc @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include "jwt/jwt.hpp" + +void basic_jwt_object_test() +{ + using namespace jwt::params; + jwt::jwt_object obj(payload({ + {"a", "b"}, + {"c", "d"} + })); + + //check with std::map + std::map m; + m["a"] = "b"; + m["c"] = "d"; + + jwt::jwt_object obj1{payload(m)}; + + auto obj2 = std::move(obj1); + + std::cout << obj2.payload() << std::endl; + + //check with unordered map of string_view + std::unordered_map um = { + {"a", "b"}, + {"c", "d"} + }; + jwt::jwt_object obj3{payload(um)}; + + obj3.add_claim("f", true) + .add_claim("time", 176353563) + .add_claim("exp", std::chrono::system_clock::now()) + ; + + std::cout << jwt::to_json_str(obj3.payload(), true) << std::endl; + + obj3.remove_claim(std::string{"a"}); + std::cout << obj3.payload() << std::endl; + + obj3.secret("secret"); + obj3.header().algo("HS256"); + + auto dec_obj = jwt::decode(obj3.signature(), algorithms({"HS256"}), secret("secret")); +} + +void jwt_object_pem_test() +{ + using namespace jwt::params; + + std::string pub_key = + R"(-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEomxC9ycc8AkXSwWQpu1kN5Fmgy/sD/KJ +qN3tlSZmUEZ3w3c6KYJfK97PMOSZQaUdeydBoq/IOglQQOj8zLqubq5IpaaUiDQ5 +0eJg79PvXuLiVUH98cBL/o8sDVB/sGzz +-----END PUBLIC KEY-----)"; + + std::string priv_key = +R"(-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBeLCgapjZmvTatMHaYX3A02+0Ys3Tr8kda+E9DFnmCSiCOEig519fT +13edeU8YdDugBwYFK4EEACKhZANiAASibEL3JxzwCRdLBZCm7WQ3kWaDL+wP8omo +3e2VJmZQRnfDdzopgl8r3s8w5JlBpR17J0Gir8g6CVBA6PzMuq5urkilppSINDnR +4mDv0+9e4uJVQf3xwEv+jywNUH+wbPM= +-----END EC PRIVATE KEY-----)"; + + jwt::jwt_object obj; + obj.secret(priv_key); + obj.header().algo(jwt::algorithm::ES256); + + obj.add_claim("iss", "arun.com") + .add_claim("where", "airport") + .add_claim("time_str", "8:18pm 24 Nov 2017") + .add_claim("id", 1) + .add_claim("exp", std::chrono::system_clock::now()) + ; + + std::cout << "pem sign " << obj.signature() << std::endl; + std::cout << "Get claim value for exp: " << + obj.payload().get_claim_value("exp") << std::endl; + + auto dec_obj = jwt::decode(obj.signature(), algorithms({"ES256"}), secret(pub_key)); + std::cout << dec_obj.payload() << std::endl; +} + +int main() { + basic_jwt_object_test(); + //jwt_object_pem_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_payload b/externals/cpp-jwt/include/jwt/test/test_jwt_payload new file mode 100755 index 0000000000000000000000000000000000000000..e6b8551eb345a20b7f68dd249501cf5bee6c861a GIT binary patch literal 699636 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

@F)7J1v(+}Oba4{$2!J|peQR4 z(k(F3yVQaUI?FtZ;2Dr{A_#gmgIX=jmrGOxGRZg*kx(h!E>( zT_Wm|IYt^uK^8an&fyo6k?oxtotumdF`w5N+5e_qi_(oe;DU>&HMwB(LYOa-`BIp# zX&AT?4ooJ2ST9Z{f%Il{CegJrkz54H9r_aTgXFd&xkG)KFNOJ}+0Gu0 z@nK7_EB6bOFj3ww@Z|%7rDu2j)4$jEuYda25#wU%h@nL> zIOPVr7tk~d;pPrXbb5l>i9zx7$|4e{(|TD*)1d1G(Vs@VZWsAo6E9XVd{R7PjXj1) z-offmI}lZfGd-12@91K)z8*f;gVuoJ-yOE$fpKibMp)Nk?+wX{V*uUg$2~mv_PW9u z)a~b)MQ!~d0dVC=QW1-4uaF3ryE*+Ee5g}Z&0MdJ5|iq*@PH_sYM2i0+V1RHAa~QW zY5Ka`6Wz6m*3$D-VF?km^i=j*Tw18wbZ0buDIPdoW~ca!$0!z!PG-8^Xe%we6y(sw zSfM(!6ULQWIR^WdXVAxFG4Q&ca#B|y;%>CD+`8*#T%juKd6I+$&M#c?6~O5M>WR#Z zD%5cmk5|fY7k)3o!t-@W^qJ%QFVUV&N<;V71G`1bjQs9Jw zBsgB{qMM=HA9=(B{R}DZQBQ-ltpLymL#%iMr28*yD0l0!f&?lt1bLJo#7vSpYZxJ9^CxJ}q3+%EjN@JV5_@K?fTgwG0FgwF||7rr2D6}~8ZN%)G;7rrXo zA$(1^Q~0{@4dI)@cHuk1cZKf>y>j(`;aB*6s8Fe79JuzOgKzp;jO~8!rO&+2=5ZE7v3$rS9qVWMtHyQ0pa(BwZaF54+%F2 zHwqsSJ}TTKY!Ge{ZWV45HVL;2e=dAd*ev{&@EPH=!WQ9k!smrA2wR0O3SSbwBJ_o? z3U>%!6YdngE__4yrm$W3j__UKdqVFD^?%`B!o7tl!u^E%3l9{g2@e(?B0NkuOnA8P zNa0b!uyDBWIN=Gxk-`&&Cksal(}iP&8aK3PXFi*HpSRh;?EEEb zt`goLtP-vk-YC3DxJG!h@D|~%!nMNNg?9+=60R5CExcEFpRh)FzwiO!_l32>2Zav_ zHwZTh9}zw(+$3xeZV_%3ZWA^Mw+nwRd{Wpf{FU$-;j_XP;d8?0g)azOg)a(U622nz zg|7;C2wxNK6uvHeL-?k!UHFdhUEzB|?@IN5;aB*6s8Fe79JuzOgKz< zxbR5fQNplrxbQgP3Br-W6NM)WM+?)1V};{{6NDMUNy5p(DZ)(Q*}`eUbA=J%XN1#* zGljE+vxOH5=L+WuFBV=Z{DN@4aDgySxKLOiTp}zK772@mCBib{6~g7htA*DHuN77Z zuNSTo-XN?Jt`^=Xyh*r5c(d>p;jO~8!rO&+2=5ZE7v3$rS9qVWMtHyQ0pa(BwZaF5 z4+%F2HwqsSJ}TTKY!Ge{ZWV45HVL;2e=dAd*ev{&@EPH=!WQ9k!smrA2wR0O3SSbw zBJ_o?3U>%!6YdngE__4yrm$W3j__UKdqNKfQ&|5C_Y&?cOcCxU++TR0Fim){@DSl) z!ePS0g+~gH5{8Atg~tg`5RMd{C_Gs>T9_^zD;y`BAj}X>5>6IQ5oQX{7ETkMD~t#~ zBb+XrDV!yoExb@TS2$02vG7vi7liYL3xs*Xg~9^i5@DgRNLVZ^5ta$B5H1&9Exbl} zt*}CPy>ONA24R(OweUvaO~N(8n}xRsZxyZ;-Y&dDc$aX!@NVI~!uy0Z!uy2}2){3^ z6+S3@NVq|`QTT}PQQ;%Ayp)Y(@xI_4waHsHf;Tys?h3&$3gzpO96MFbcJNmzHFX7(86ybit{e=e#(}V{L z`Gxn6;|~)K6CN%+Qh1awEF3O8PI!WFr0_)H$->dXbm3UxIN=0ghH#Q_vT%wpQ+T#; zn($m@FC#_;YQ&j!bgRhgbl(i!mYw>!Y1K%;m?In3Y&$$5tv*M)BYrzbu&bb;qhy`baxzvXz|ngSejd2O%Y zD5wA@M_$|WG@U@xuV~`qaow0y@L8HR(T)#7+a96`J7%x#ewv~*t*7bhG~G(m_h`D2 zrpIWyo~B>Y#51;nKhRV})7v!V(R2V#HQO$xiJ#1An?=(kn$D$ZE=`kZ;%okGV`*AN z6VI~?*3)zpO+TUO5SpH$X+N4?r^%x!6^|p^-o#;Q!6=$`&@_#v7iqefCZ3%aTuBqv z=?mgCZKdfhnjWF)hcwmFw3Vj&X!<2he51eM&otdk(*Z+Zs-o#wny#g3B28s9eSxMW zG<}&SJ|8G}o~F4p9gA1t+oscWIZbENbQ?_>G;N}3G)=#w={TA`HWa4AY09H1jV6Ap zv2Aaf?xX2lJWnfVqUm*-j@%n2s=yX}k){`DdYq!RrP`*@6rpJxO*hbVB28Oq3e&_F``ZqqX*dq6+xDkv22CHMX$ej3(6}zR zpQhJn`UOod(X^AM=V%&n08G>eFUX>48%?=1JxbGcG(AYuI+|)|dYGoWXnKmKTWH!z z(`uUb!-J5v3Yvz~bOlXk(ZtW16kI^l0-Bc4G>@k1XqrjWEi_G|>0X*9(Ns@UI!!;L z=>(cuXgZRnKhbnBP4Cc@Lesu@64mw|l*|i;(ewsQqiA}SrpYw%?T3QT()27%7t{1} znikWvg{G@$;;RWA>+bM|9KJt>)yg!_8--5cB_Ke~1=X)(-JwGcm;fXHug8A~Uh(S; zHv&!j6fI9fi_Maw+!1af41AR}j{k1qPJ{oWk=Az^ZZ`VbDWLi(y_4t+_Vb|0aELD* zcIRSDuEKxPenUltH)Py%7;cBb`A6uGUKk?@UBKH8>V=R$GIBWpOqaA+c4&rrXkYIv z{Cgwte;N(^Ss6bG6Wu97z2opdmInUJeGM{sDgG%+yd8iZ@$Ut!kf_E73N;4XuVftGSZJ+Kzv=$30+%90@oA%)?|(2mA$$ekY(N>5Vdd z=q-Q>F!z9Z7ojrgQVu$FJK(p0%d4nex}-oZ4!sO;Qs7bpSxA?K?BM_Fn1jF!JsG`k zZs6??U=YlE5Z%N(55O>sK!^UuG0MRoz~sTj{X#34E#&a?Y^TRj^dGpTr%7|KSY!H2zP+e>xeKnN_4;$GWer^oCrY>@wHT$9DX)CGG%| z>@vLSL0!gIK)}&x26{UabZ7$b9fo1se4SkdbRpsIu9qAEW@sFssO1ezAMILmQL

5D4vI0Hij!+r$e;~5MN!Po{F;|fJCK&dL)nuMR*75FycNdv6kwI4CCNe|NjoCv+ zf`*=i@TIpn1iROY_N5$r4K(yP#QCVe0DkI& zZ7{IEeiC+l^uftN5E>Zs`KVp*cmPLQE}4@7UkBB%YaiS7u$0&Tg$fygxVY%@G68H8 z{j7iA05dbvOuKvVPn9kG`Y+F;m_bfP0sn^o-!R@H0K3JFprPTeG;$dQ=3J|v&U*S+ zhll*FLT;Jk`|zJ^r)MkB)il02mFhIBeyz3{0{T z?*qdKT0y>;KPgN9c0GkD7x=bR zBh{J>ox{6^)?9}1NJGD~VZ&a#hAx)@*lTOa@I+!csD6I|L)9m_taQEh8|<|$=(YIs zZX=xFdhIm!+9{Y?lP>3gNhbO|^k*jeM^LW`z(n_<)Or!j1EBid1RIVuxx5O7g-xMc zn+r+L2;}lzFm!nWH1unXie#8EC^KEo=jhmm1qRFYE6`9K3PWZyXy{%{G-PT)LpNdR zLFRVQP|l&oECmgH3MtZM6lmyZKyv5@W9_55|ETW&f9&^fucFM^332c*kU68A%=t8B z&eX0l=iII`r`E}wS1EIT=48%(lsPYh$z#^}I}bJ#T5_2QCMmQwLhw-LybkI;4WQ6E z7&6EEGnixqpMwnrkX#;y^q~OyH6Zj1MAMlN6>=#|Gndt%p?ZiUZd2vb1}Vfa`%nP= zw-Z1wQvf~g1kh6yK(7Om()BGc$&BV;QwH9lruA}Gu?V3?iTK|?-X6lc_OITL>9atUZ?QMgN($HCC$_n@J9$GUVtL!Um* zm`KBgt`0#H1tD2FS+~nNB!@k{`XP;!|?b>{$Ydk z|DF8fdDGdLKPcn*h*Agr=Y#5J=S>q}<_bQSb`kv3LO*kdr1dZtd7q;l-MYBDGyQ$B z1=WuR9?tMs%R3T~ls+Bnc*azWJW&4gxG=O9lMFql(({vGP69oZ%-di}K=os`yPxf@ zmJNUg9w)v`i|^t8H2nWBnO6LNi3a|x962!YATUa+3j802RP^gu_dsaKcS1ZE#x&-g z!-YV#V{*ytz(0dllHma`CSC6yGLyl45%gg)4}w_&%0-0t8N|+N{~eifFi#9cPRLw| z#RFqo$2G=DSR#-)AJlu!G4GMN0PBTSpwDw9@f*N*LG?>MA31U_XF~(GalEzMI|*>D zb3oZ$8|{kU=|N~3&cpwmv|yEF)4-q3A}7PZWcgeqxzBXz<kXC}~mn>rio%o}Hl|O{N>2@&31@T*7 zSO{xod<*`)a{OyROigl%NL>Ee8j%tk~*`Wm4gj2mtffn0Nm~C zXYOn>#+*yLdH5ei1AiUs?ykUc{BW!v)gl*yhUm&VK9h{qQr9!?!l2@4V8c8?iyHwM zpuZur6>ur2ejV%nfFv%V`6;t81B2@M(aSJ=7Ddo69oc8^zmR2l55Vz}hBAjDC_R~r zK|`|up9Z5JlVn*2kmh8d#nan3Wy) zzdlHZ$L=@4;%%mLk4vYH>HOKH)4_E9)>k@@qu!<>n7SM3ZuUDj{`SaN2>S3ut8^T9 z!oZ@(Xu&6RSD|qFu_Ea*fwdXm+nD=6y>9{*!Bs!W4eF|}i}FT|WS?{So*pDc10&=^ zGj9Q)n1`JFCCB^sjrTkn9>M<~>9+vj2ifWzhhyo+9}SH7dRjyPR|K9nxiXB{2QDjJ z8SY>iz7ALrcuS`LgCI&87$uK~y`KX93aVey?}ss@nmiUtN_)oi)?FkRvrz^xt?Uc z3B#vYT>d_RN?;=$$I76xLntVL$(bL)`U<3_r(o1!8V3vgl4WjYrud^_CXzXyNgZ_} zhK{TOD)Vnp@9ZFVBkg3FU4Qsf;FAVcj@t46VE97dqBz*?X5xPh{*!V1JT&BSR**M~ z1Cl=)3PEq8#Sf7vW#40o0+N zhaog0_#9yr;HCU zVj7s*aF&KM(^OFXR3}qVpKQVfUF#KnjH?``%EXd&_4po=KbH-;WCSW}JmTh6Ntzou z*FH^V4uBb6N``AIDyuE!!2Sy|%6WAqnTG((_#`q7fG%sfKLqp3vvAXo26n8ZJpOx7 zZ*7RZ94@)6p97F9_L8GPLl>d^WEQjOvjLw5qhF7@-am+o2CgYiV_Zz?EySZ=vR(QY zF)-YF5Toah23FN*S{wqHN?ZPt>Gdyv-Z7u_JBa!^C`j|~ zFmOIThDH0CtFPa(z79nJSYIR9l_(>6)t9X;ZbZer4S%PxUZbe~HW>7S1AZOrT0`y$ zTZP$RwFU#*ZwrUTb}$U`EScXR0`?uA@oRm>9`YWUOk{!H4%Si`Hgq|G%t2TxCu6$= z482oN%_{-W+b_xd0P!Zhy$y!m{*Q~fS^&MJvi5%g<}OhE_%{C$Fx^j=IDq{@x&9Uy zE{OE&SQnL!geDm8mB5+?1{}>!J<=8PY%=GenC$Lz$xzMnA-FCflZ|3BFV~W}XCEu` z&15#9taQ1L%o_-eqdV_-mh1r7oP`MV3)XVSBi5VnKY>mCBebXQjN-kt*WZUoTVAINM6l!EHlUp^W1C^pom z5FxwnSTg?uhVf9zP>-j-52~N$gP`t??OJz%+zB$qN@c$u$)0iqnBH=Z`i=V0z?^)E z7E=Mwu$=rUJ$46L9Ebm8TcHhi$$o8U$XAr!FNKJT2B!LPmU0IeX7?mAdjs-7^;-gl z9Z4=vfMK$K1P#qZ(U>o}+yRCzkAQ}Lf%bwA@4rDqx4;jX^`N0!0Vjdc&vMeSF1fyV zEXW%T%-h?vcn-k4QBlBq1I$WL{mNm(*yQp%FwDWlp>=+X!pdKaT)0iypCq@8ys1Or`u0vfsOl3YwZ4~$)d-gN4p`E1+j+Gdc#l& z-gYM_eG1BpV8aE_60Q@dSjxIOkJWPnfFe{bM`B$;aX1My^cei+fO(q>#qVO;q{|ba zq5p>HWCXl)EOZ*IxpS7c3dmf#+z1+)e2_5_&`>I77rGn)8afDYIvD+sY5e-113&6V z|2%p6Q9s)Kwb4iN2qW8#<^SY<N+4tMF8lA*OY;clE_hynZxrp?^Nhb2gaXd#3)6(6cc!>6a|~ zT4&+W!2SKBwD=bO$I`%G|9M`ivxo8#@WkTs3DMABBTFpYHDvw*hFjJ-Xptd50o)b1 zd>+nMgVFD4FpMCT)$}_sXPG_rov>k*Oe8%Pa6;gMs#}5B@k^G4Pl5HLfuV|MaSY&5 z^GUzSc07%V@kayu&b17(0t~Z%Gnq30>^ppo)Vm5$394Uzb%GNn<~`ed?Ys_e4f1!L z5EQ51=ioof+ykT?f9Y9M&pu~b*14aKocEc}PMco>yK4tW&%)(&O{J{OpJZ;I9Wn;!n%g`Z{>M!0l6nvCULXF)j-Q)f zvUuU*+?=w-%Zikx&QLQmiVBw%EX^q@nvj=Ymb18UOnTW>#rfI!u4uj7C+08D%`a9= z>`}ek$BbE4R*+wkjDViH{AONIc3L*eoSj`-kW-SMmt9;2&r6F7b8_>~>lNGBG0V!! zip$HgOUp`7pV>KiImKo9CFkX4WzD=Gdty#u;W89!S;=`9jLXi#uXlb1h=JMao#JUO zMVC}|amli>Wx2}=nWon{6l}QYM7IL*Jcj@Ej#dmnzX7+vosV}IF5m}4ztV?)N;YmB zAX$3-dX;a?Sd=e&*~09SoT5ef-8(NryY?A#`huLi?1kk;xoqj|TvW7q4cs#pYj#;l&f>Dt-pzzML@jpt1@VpV8sCEQg}W6Q^WUpKk15V6 zDNT0hUG0DAi;LL(kfVj@)UHqT?0ctK-(#_Qr9Zas7=kM3?SHJL*t7e&TI=P1%!I{7 zSL75f&dXkmfx)rRy?!aL#*UwnzZ5cMstp%PfzCl3xnS)030~xa(=x^Z$B!B7&6dq< z+02$rmTaxAvu4lmW}ZJY%gZTTlwG!rJ;6J5PHBEg>8UwO%S)J}Q}gn#$S%t- zEju;0xcC(0ERPj2oIlAba~OTD~rNOfjHbNT2_?r z6_hfM1*ObmK`HZCBAXJ~l*lGuHu{W*}?;i6?_VRYBrg8aPUUe?m$ zva7>=y=)(M>MP&seN2yS~(irZ+ulJdYF7za7UFCJJ0lEzL z@|PETMGF>si%Uzrobo(xaZZ_6TE4(rQnuJD$zPP6TbQ$WX=zaX7Q;-GF9#)Dw73)< zB0n!IPs7%r3=9iY{EXpqp=Pgs)F3l;+E$~d76z5!3xD0}fozNp>BX#6Q zwxe@%y(P#<_{=jInYGnm6BzED!@3Wng~ChAOUqzDeS1raM;GLjTDy!K4#(lv2m10H zM1dmr9|at{MbX`J5C!@MijO*A=eC?2(>*Kj(Jh|dA%eJ&>F||UjD#Uo%Jai+?bkE< zuCZs+d$zn=nNJQv^(_4R#*1!Rwrf2R4UNvCk;e`;In4Q|G<$UE;;ZvVj`DI!N^-86 zGzqbq>(ZR!q+2dlJ*cnp()=(aXDPf}!EIP-1>{|_q;y%)=;WGbv^UzjEbJAP7Z!TU z7A(onEwdCTHn2YN7A#v=rLX{L{!I~T!=q?w_O2SL6hm7u>uRemQ*}`2y znMIev^byF~plc{+(*^qa-ulfnzGg3LmfE)9BfdU*1g&Xyw3xt|$6O}!hLE6Fb{FDwhZTU^Jki_%UJ9YmtH!;a)^lX7okfpM$W8v zyXBEfHhfBW%s`G~z2irpxX9vK2=QFzVY&=Y38$l~u|>(nYO}O#vZ~#>=9H1}Fsh%_ zypd=oB-uSD3o!i^jU0tQXND2j8=bCM3Sxfo(wxFt3Nsk;P?;AT`2sPMUU3divOMd3k7CisYPJtVY5)3yBMIaxX6}%qcDC z6()CCQCZ2d!Y&~+xuP1978(DAioRe^rR(e%YS`gxRe)XK~47E?xc~dtU`ANqSmma+&D3KnDuP@o`7WUZ_LE0nSr5wRj* zl&u0n*vtO^o_o)Gc}ZSoGRaJt0t0;~cR%;sd(S=h+OPGpD$88eus z5(AaJzg;Ly02xgDGMy9##z1Rxw`6ud$~J@huo1tx1g%a>CeP> z&ppNA;pGM`NZf@JqZDit3x@u^nS3U3#GICcL;c~Up}ue*P3`U;=mjwr1BV7f2h9u3 zUG)8s5>Jf)j&C>vbLX|q4z;!HPnnpWNE}=c`0o&^;SB}mhVa`6vk2k_{r~4jEQ{wn zKs_l|GU50Bc25N4K~;8Fq|Xmj1UJ{XgMt_qaSpDGT(GiE)bK-Ias>sI-$+CX@8FDX zGY?uRbPHZ@j&GGB*UNImN@;9RuA9{gR!FJPQc5o^BnjINbbFfrV_4Es(?WYP04W4( z4nvH{p|&L4(G8i*2#|6(`^n!3Pzz&PA4o2WoK>@S4@Y~65MiY$7^Kd^WC#z^#9+8% z;Uk74q*0)bL%N?F(!xqkW8_$ZCF3H7OXQYaLz|oA0r$X4np&#*sk0OrK96D__rJW(f-X|_=hbd`a1;wuydwq z%U-GzQJk}oyEsgTTOAqf_w5F#cjEHjYPc8U1R-EHg55H}_CDOm0(1|+J_HqZ!Wu|st(AX;M$L6;_WJ`eeS zSrg6#++Wv37Eu4TCLTGzPNZ=%_!U48tk^~`tP}BGO+2>-^~GW3B^Lvq%QUeW)&|oL z!2KJ*H{rel>EG7G^ed4c&erb+7zI24FpJh70!%s`;#$BUj$mfVy`4k2t^&TeWtjqW zeZe7~0~EVD#Lh{?!x_|hfTGzUeh8R3z#&!wrWQCv8c@gP@!RB&i-5cQ7V&h4=mv}) z>k#Vz{Um6eHwaD*ghu94;^RPo43Fvwm@d)B7YYMO(w^N@36yK^5{_8-u*)^gUP|U9ps{#Fo z)QBWt7M63j0J>Kr-t|cL>l)Dx=*KqdNr0JGYQ!4AoVD|e?vEw?_@5fH@L%_s-HQ z1x}FyOkd&@&jAMSc8Wze0xz5*I}$MQqEocr1h=zRJO`+MqE_s7GxBxUf);T5YQ;Lh zbVIF31A1oG3g<0wJNA=U72gJL#R_K842WrJGcOt(G>Io=bt`$ifmW=~$ z0BnB)cy2)YX?3FEZlwQwo!9~x{St7)3GCok>cqDI+yARhoDG=VyH0EX^vp!~J;-NP zo!AX9@eQN}%=qg>FJN*(omdU%I;c*p2hyGtrMF7iI>h0 z)3n{3^b5oKQ$lln8axaCJYT}q$1h=s17^OAalf0;lDmUH>?t&TPf;7k89>iILW}N$ zOAE8W0Gbh}S!fAB@eRcJ2J+b#X|P?M0pIiogciq6M|3~H{Q&m|+#m1&lsO-9<_pcg z0PX{YNPQ1}--q9SBmW;E-yb8t5O`82jK_gjy1J2XH_BNIco^{LMZ8|5jR`FkLs>(> z^JJ8d&9~?(xX(mA&PJZ+AT4-e`+10ujlIko#JddTf&V41#_wAZ?{1Xy7yNq?|6avE z*kpBmMiX(|;&nA>q8&HkQVTTU#+he*8TeQNej79q%xYSD7I9ueT>KL+Bm6b+zc)0^ z4?dQ7OVcuM!S7wT-_t~Tdxw_Y-XRh$hX{Vgp~XMz5b>QIT4ra5&_56NJ`U0Tb%&;Z z)8R;b%b`VaXCjCPh+P4P@b3rr_c8PR81ZnLJN>^7jjU*8{?DPM!K>O=I5ZdTzv#y} zwBT{b_jrf!fL8@i$M0W*SDlY^YvH~eajrnxtC8=m$TJQ1pB-8PXB_p{0pA5bMc>GN zvPO%0YecZ2MoZzUM)aF-f2T%dzEh*6_p5PcT52@+f*MEk$Qn)mX^lv%tkIIVBJ7IS zXxTW@-Gq1>(8vB%qeY(q4|=vn^FNRD&jY@I_|u(Qc5kQfd!3qRU#CdUbs|5fh%Rty z`a-90EdsykL|E7}=;2NgKLT-9IECkUrzXIUz>~BTp!*cKSA!3o<<#2GafG zy}$!}wOTta6(zm_h$A4LZ`W!Gf33*Osny(bYlXk1R!f5?Wm^Hki(KDB8vKjnU;B@0 zwK%S5yL*wQA8Gn)waftc!7$PtT`P3_3!VV~)rfN@@;DcMSAhTFUZ5+5xVIDUK-`Cr z-)8u4Mwz(RlzkrY-hum*81Jq+P3(yAzjGb>5BSP1;0xd(slC86_O8?1`+yhhQ>S@6 z;4w|$Pv5E2{Idb))M<%1NI$nubIk+4IRNPns?*}&AMrM%>jUhoBf093|0f(IV+oSM z=ui7e@W57~Eptu#^+o83F4v4{)8pv&JE~xW%TUemXK#^$>_W0ze56)FPn|ECfkLB1p0gL6VhZIAk1xBs0ly$T|f55mle~ZxnP1g@3BoDZ_jJR43tQ{;Xi? zTPhq$^Z)u({CrPTcl&8FJ?JW7`qK(V*)2Y<-0d*XO!#;{pj(Rr+RBx4f265KyMkO|3c^w$KtN%kavk}V06Y)O#jZi0UHCw?pAeU0O-Q!s%` zz~rBTNeO~3?*A#AQ6#r99z3`(NA3jHCIo{Rcm$(P<)6VBadOAmoiNg4ekA|QkqV|7 z6>Nu0O#TVZSMO*48Uvo7MmX(@PZov=$_HX0dY9+JW2xYXO3; zex?W3PSSsw^4B@P1m`F2Q~A)Ek>aKPpkVvm3P%5~VDJ$Iq#ysl(t@5WeB!zo~s!x=mG0zH(`y2O@ zAh*BH(3pqmW)tO;tx@rV-HLv+HYI--_YV(`FY%J9PY~u}C|qF8O)$#!_hZdXZk@wv z?JDD8?Ml$Y(3tO&?2dDP$Z|W;+K%FFviFnc}rG-AUIY@qmtppqu+=lp*OL$e(l-1W6Y`5NiYpNq0f+=n<+vWeoZ5$Euv< z_Y`zPcSG^gEMKkaIt~cqP$W1y~33?57{8uTM zX1w&dDxVa)Ne3zOhfY-Hzextt*P6VhGEc+h1M44%*R3vm0+VK`pL03bt^*On=w^ zsq}u1N4i)l*9DV81oaIHX8*(f(9M#6JIjA@hQV(r|0u)6Usd^@LzTad`~H+JYRHui zRsSH%lO7}8nJOLWP$|BL>8f9(-2Qr%59xv_Jo8NjUC_gjJM~Q!FV5}S&h3y0D1ZI8 z3~yF21zm{5i}`+>VUqcNnqh|5t!`sI$}r9_!|jkxs{G;x{JV0eI}{z_+%G*wdE7sv zTu%FQDqi*(1zk_D`$+{oPb;W%y!L06JH>8#J&Nj+eN@3TkH^f_>^ADh(8c{MYt)nb zMVw)Z(?_pR>B(jZ<)6f*C4%kTf3h4N<#CdQB_s;Z@VE*Z{fhg0km(RK`Z?1f$>T4| z(8c52#d4vG^Uv&|@^|yN&$O%ll;U)P%X2fnF2*-or_x24pSYPHWSAaq#z)^*)i1;8 zbgpND>zVPZ@HE#m8&d8R(>cm~$j$xL!{g4w_{2HiDEsTb;Pgy)5A($!uLnKMA5+W^ zg%NJ7BRQUn`DTLSc^LXRzHY29IlnB2yNqzo&u_%z{E{56onf5EX_onFR#SBKGyUD% zZW-pESr`?S{f67k!|j&jc5}0woAs%9DQ*`s*h1;t++Y0MK5p(WZZ1E`dZr+!i?hFr z>7BVz(KpHbA;@x4JM$F}!|b=Ye168vj9tsVkM+ z&rmQwb#s3WGQKJ9XS8=g^-plW_Z+D33o>*yt8hQB)3cmD$aN^-n=<+__wN+L z_Dj^fp5paicDkZh@MHz!EJtP7Esj+6ARC;7U;B{?k0f5EkZxYr(4GvrX|GJy<1~iY zvmrO_r4fwo&EuElob+KTpXiW+S);y}E4QCvl>Orj+dr@9LwkmlZ)$f1X)lr7@$WEx ztj~x}V}Euh*zMwY9)`&_g&*ySQa-d7MUeK~2tvQDAoi+cIlS(my;O2%nUA`D$K~@n zE^a_>r=-zdS%nwvDN_0jk2gQhqwNf7FOHqsTnsVK|H7 z9EJxnJd|NO!^0R3G5i_Bl?+d1cs9d}873Lt$Z$Qw`x$Ox_%DXfF?@}oI8D{#V+?m> z_$7wBG4wI~7Q_7*1{wZ8mhRYZp#qcDCXE01Kyo}+s3~yz)f#CxTA7MyC$olsb z`)3)x$&u|gLA2VFSFvjpmhQ~6DGdzpo8irRgT*vSZ zhWgJGe%89s?z#y(Fy z^B1qmr=4M8dc&<3gu6H(dB1(VjquDE;jTa0r%!xL?b~c-=wZAKe2n~~W8@z%5uWAz zjD46W&(nqZx0k@rDBsWF!Ge7!W8ci!w@aU>=u9?vDBI z89ub;W8kBgz{l9XGxVM=o+k_O(Rm*%Zmb^|W*8duaU1$S13!kz67V`*nqG6Hi`;c^Df0Mt<%;sd9{VC=3sBxZ!U=bH9_)$BH%d%wyBc9;(x7%2UbGTj*&(GoQ4AZ<1 zx|;W8cjj{a#(t@R2gA)AFU8Q!a0};CSdW5gna;v`m;nVp4EzmP5M1Qci+t=ojQk8} z9}9fDR6v1$oat=9f?(5W^tWh<{^aLBb=a|6wcv9HhT&m_YPLnEHs*r#9*qkIpSZ^Sd8 zpU(lLdEdd$`x?gnMv~n|KBss!q<8;0pAQ=O8|O2O^D8OVD`XiO@jikdQQ#vAd_;kdDDV*lKBB-!6!?e&A5q{V z3VcL?4;lqNwX10V%ABa{zvupVcH8XMK-As-MD3jRo#!l?8=cPoT+yBJpQ~+-@ok;G z2>-RsQ84O?y1qL*>T0Ebb6SyE+nhyn+VJ0xJZl}~YDHf75Bb=pKweQ7&e77}4*pQ+ z2MZUp4SDNh!~H{%KDIp9uyjesP+&wH65B8C3@)oNfw3NeNeqIrkS)-9ze>ZF%z!;p+Gw+t0p|2iRLOWm? zMp<~ob!HE^XK_ zFwaAoZ`rqkloBmNrS~5924xx3%ViIgQFhR-=rIzGR&)bbdyFh(y_05-$wQ*dEJnj3 zX9^NVB}b05N;wpQ1Zuf-lkPOp(k%Kib|v!amG?b zI&`5Ic7nT>E$irvjCj2>@f+rRVDHkHB2bmwl+*4qE0dp48XJz-q)okKlnSEXaLo}G;RBc4#duffD+9MiGRL#?75#(~S|yhD*iP0uANGe!pGpfau|G8vL;ssj3ynUx(@ z-Mv9fcpF|e20_B$Ej)+zFYOZrei+gnp(qTuzyNG)Xc#8H^wjitB0W7$$b}X!TNZ*D zCK#%LJ+7hUp%D)lYHohmk>bEqU>BswC5ML=55v$VdT{sh-fnmgcZND)7YJr$;v|nt zIa}In_mJsVW$LIIR;QGW)|O=>fk11U2VE^hHm`Fwt)*>R8g$ZLd68M`K;Ym-AxQJ9 zYb8CE*>!JcWIzeCu)-W{A{R{g=$qd(Xk(3Qu|(_j#bBR#B&={E>(b-I1q?I~4@=&b zy0*@q8CuYV3U`jb1k|=uu?eoKeG!pK&7B%+B5;SrWio?ArsyC|V0A^6QPz?ID8guS zpS3?wSMb@HH}_f1&`bN~tqX`)y4%{?g++QT^qaUcEhS=^1_OvNcm-q6KM4o=^6{yM zU8THGOG@GeFsTdsX`f^6jkjj#IBW#{W^K$ok!y{4UmmhT&`S*bn4K1_ejHVee zeDmTrkhQNyX^Oq8mlheDr{&5u0%0Lt1{R0FYPxNRwEtcrq*QLwPP}azX*<3|NU7YU zjroE!GlPMH+F%2XdY@4@*nkfNpb7!iV89aAyaT~J+K!*AwJvO%nXgQacIcK@Ru&akMj5lDNqG$M3Q9qTYb@+=Yd7`Awttc_}!(zkQxj*b1%4 zB~5B8av58@71bOx$wKOAvx0oLDS+*fDrBzm#v+$A$*njk^UqjgQE}DxwH3D(N0qCu zKwy$vand>JtOE|3JvVQ~*gz(#s~iXkn@Oo;NfSuI;xZcLI=#B;rV0ng0Y=g?gYPMq-jw;7XJK zGrOS*iVeu+I6q=KQwD^vv8)I^MX-iyubUKi(J6&h1HGfr(mJ~Vn*;J$RVHGy`NT#` z+Z^-*+VGfCh&Re-Ex?XH zn;UA9sqn%s`hd4yIzr(Q98JUUYLy)$VC)P{kzv-PUMB%p@`YN^3P6$=Q9~~tnrV^y zJ2OKFgbzJ@(?SiU>H%qCKK=z4h2}*M2Jf3%9V=I@dQ^+*glaJ2+W^|2^8D7>Udd#p zI*BE&)GwxfZy;2sG@>1C0d!KFP2zo(DZ(To%UWohTxzkF^#xkFPi2P-lZx2fZeZD+BMhNeB4h?h-^nyK;x^3+zOXS~x%M7^W z66)ygh~oYk?wO2IMty!{vk;`>a@-|~Y2r64%{N~svK{Tq^m#DN%GZ?Xf*#bP!=#UG zIgpyuRF2jM0?lUV7!pmBMWSi4k!avoEYURCOEhy!Ni=G8v#{-e&@7-#XIJTJPzkbb z!&Ri=n^(sw8nJOoHX_x}+oayZ#$`_2YsG8L#p-yLlzg6OEYgR&s5k;|yh0ornu)V9 z^oTPJ;gWs8E@3kzETJ17wZv$Y%2HQwiX`dA-taQ;h|yR6TSxHs%F6<3h!Dnp`~EYP zFdoDk&mq+xfblkhuSwx4kFQVCm*1hMJDYtA+X8apK0Mqn@5>c!h4Dz3LY<_Z?tyq2aj=49W&#h1&q0jj=G~lx+dg zVGOQq0nsMR*Fyt6f!1zJfWcr_i4kRNXio)64m;4XL1JNQu@>lTg^H!5xI2X~ZD&&qN#=Os0ym^5psUB zSEdDU+^tX^LIN68qn%{g3P*VZB=HpV7MxdRp4LfI$LKh2jmeA=T#&OQqRd5j8`W7u zWlsXuMa-GGD5|{0ZWpXG zJ)M~j^+aNWuqo3M=rT5P6)xk-tD(DNsH31?j4uPir79Gfl+DCykccdUi4jY4Hq1My zBkF_6%Y5H}o$pMtg@Uy(3{;?jpaJZ|;vx)#5aaJr#4Cyq+~CeVc{fWNWF52-Cim>D z_?E<>da@7jn8I>ge(InY z8HA+~=#ygIVcLe`{ykMF*aTa6;TVY6KhlA#F&35iRG}cL4CM*@W?oc5+@ES>RA*Zv zUB)rAsY1I3I!rU=I-T4tpmP^;z^Zj8mMc@OQDJH588Hg>#)ZKzfC(sk6gY#V@ zBXpMnS18(AAQIwen-2#_24ZAoa;Rf*%Ylb^k#!NvFxzknl7X%QR7!&bJ|Hx}Eyc=- zu>S{x2vu~0{XQ5JzzvQ{2SSw;=$c9lvEYW-sLXt7AZ~M;X&b;=GM#OX*v0p;raq~Q zuVxkFoOiH%s>mpoc`3KY1QA6FBP@&1UJnG6@e3n)HJ%a!nMVZIsFW0Czdtbx>!SQ4 zyG3n0g-$9;-Bom8P(7bhyd;@=XDgdX+s5ay)s4Yz25WUjqvXnT>d{*2NYookeK0xE zfuo7r0R5wChY^-YoEQT_%o_@s-coOgXQZSD+lZqF#wcD{ zFRM?4<6;zd^W~_*umY$Wna#pZ4Ygk*c+`%jpUoYb;O|y8h%D|oLVqGe= zQpNqyT7h!OXrMJw1*1(WtwH`15|xgNf|nX9&nEx%kBal*w}&cDpL-Fd;w&biHy1Lq zfb7<@(5+$AsJL5Kj`~EijUvSH)P&+6)#4~QvE;&%hlSKZFL@)ae6Ci6Uioc@ipWNw zhvS;<8eu9|bFx}NS)NiWR{MAR@~1C3rO<3rRc|P30xN3jYKoK(E1ISlLSY={fmNTD zT<_sEm(Qc@GeE-X!M!{-w?N0cER#oD1BOI(Bq$pnFWYiZO=KWMQ+okUbVREM$)d_hBaYR{%t3kKrV>H4Zw!Jpki=X?HxA%5C(H*mnaf4B zW@_w+#X6R^bRF2&gg1`zj*F@YysA(oZ~e*5v}sskTuFZ6#VfBED3mDNvw{o z15d8=z+f=^5Q6bWkK4uu28QGTNORdgc@>uy4uMf=gh`ZQP9Y;(Q6`C{e3UDi0!LdZ zFU0)Nf(|rgkibLncqSnXu#|3>o`0dQb}9p z&+Bhp(1%e3&O`RVC!qw~Q?I;;(n4%uMR1w0Onp&Nqfqr5x%!4>Bqp)Ru3(8{bR8i0 zS0gU}del)|BY78E2>h@kGK9y?fpYi=x(Ba})kKWDnx!J+B%T@BxaeOZt`BilS#+R! z^o)H_Yj=&FUws%CevE-_UnCvOr`MNS(Ym8&X573%N67EL1;*57vRFZ;eK4UImul>b zpV<$Yg0L*8D-_gH@j2Ad*{gJ()u=*?x{4uQkRp7ze*kwT$VMMYMpP}tY})ZQ;O;NM6Cm9CZqx28Sal9 zF&t*Afdz_TAV$+I;t~3iOj46A-xg8E^OWQkloKnCniX>dBjE>>zUVBb20kna94LFW z#N;(467IMkY>JUOLUy7^zr3fT`kx|N-mU~wC!Yg+uxZm23)3JouLpi8I>ap6e6y`~ zCDJ(f9a+0-39{NM+)sQx6zOUC{$PyO8aPtLN8od7jYVKz#+G&=j>P8kX8Am2;l|dmwH18XZ;eTIfAQtMvtpYjG^(plR?L-bPi2%nwd0W;n zhEiT)w1=FPa8(zZt%1IL5n!yMK_WA?2XiQ8fyr@vnSe%SCf=EpOTDkUTnCsr&MS;c)k$L~C3$8Y{y- z`Ik3R#z2vHhV0VgtQr_N$02AMKX*Ng>6Zcl401qdQbO%d4~D6u1o63p!wwT>x1 zy)cf(+Zy$kYc3OS%L=R-%!&2ct@&gNR3Z||b)HQh1-?cFY?qN$RRRpkJePzO68030 z^`fXNSEfm>K#5>w5z{r5zld|(WpI1 z2fuPWtNIp@5@)fRp`hDPMa-Dh?1UMI*T%uiniVheftSs;+gLXE_clul%dYGDSch_W zozZ$~!mJ+Ie8x*-imlsMK5LH7d5g4uRdL;I_-ttTY}z84ls{<8o;{6Im{MFB*Wu9o zoq*CJdY*8VtHBuCFnkQkdMdg~k{Jb-o}*|y(UE9tq2(<@^6N+n6COLXnkx%&(L)r79b&ZsNHq`cXPVw=^aytuQG@&zk5$5JZQZNJ&C*CEQ9yNk-Ls?4f2Tvl{SUI-|6_QTLE8==xG z1LR^^hGCzRlvq_!Y1_UMFpjZ0)$at9Ta~RQk6L@0oYgGqsmi;Y&8>hkYfc9TEuFvo znSf@^viOg&b|_B=QNcE0VHL)Um?6biU$nU(s30rL!|r3Pt;;68wPnW^q4>>OC^YJ^ z3juLgzT#?bW68@mC>jqMMPe-rR0O5&pAQgRB+rn-2IF^ogKY~!0oVhsaQ9*=k%F!y z5FK(CkBTe1-v^_zXwmttv%R4O)K&7E6L;Z12+b*LNImz1T)_#iUQfmIlFgm7f@!HC z6r$(3>F{<(7hXuiBLO(T+m9=zbhn{sFCSNB$eW&dh;)Spun`;X=xdR04B-^}%n(dg zH{j@WK_So|$@N5MM{ftR4t4ZUN$SYvxR1R}l4vKdt(dnxo75_+0gMNCj*9QS6C3V@ z6_OTtpcy>M!k3#uuv0AgGR~kKjNN4Oy+e{xWhbJjxJ*ecjlrpXT3y) z6&=HAqch-iD@*86tLpuOO}m2oZnGOgmZdio?s z{qXQpu&=`FO7hsd&l`<|yTVH&@+qW#b(}0jZ_k$x^!2e1JazdNlsxZ- zhry(XV8{;T*^+BThsXaPo_UEZ;IUO5=9M9s-VAlb77r5BoL?QoP`a8*M7T~R{}PM9 ztG*u^o;_A`Soy_A#O=jh+i_S!QAQ)GTULB{x}u_1RjYyz8?mI&R0+5Ha0#Z3QZy%l zlCEb+L9x(Y&FJg<4^O|EDeoT)EH|%on_{&gZcjQb%ua2435X`7v28jPXvOHHX(d{* z`IJs-#oUuE5llp_ZRCBhQ)M`5?A27Bxd^k_i&s<9`UF-GTW2*jL3Ei2Nqs)qtlAd3 zKXJJN8Jk|EZIQ-9q!}&rhS8)sbOq`!2D7B~O$$aHYdaw-Z!>K_8b4DND&3$J9daty z;9CKQqC0SvaH#!F4wYw$C~xIGsr4w(p_gGH%|-(`+1BE0Hi9OVDE5q)3~!CihLw># z#G^YXCD<@+(zl@H>2HjoxV7u4ZgWmtHK~J7foe{~`~jP8`;RhzOlC`sHY0Gi9Bn=r zeSD5RA53c1MpGy?h31VxT;1M;G=N|<(DGPDDtkb7OM<6ZsRej^9OcN+WW)3EgGBfeIP&hUc zF0h+sRNThoYpK48@g;kvJq7JD8aF+^TWE2nvPhXH&eoy#FTi>Ayv{#Cxyy`EfXaK3dQ!{lofq!HVmlCv4a5RM-~8Zv z>Q75-Zj>4*BciVj)>!evmk~3*(To|NX^rV?JP79QV#{au_6~G)3`GX|1IxO?QTYP} zY%r6po@*p4v_j?e48hDS&NnEeGB=xPl1(Mkcn5}HeZfq$;che0IupO5g1pg=7|sY7 zC8cuM#r-8q@)4gO=n2I-`td9!>U5h~RN@;YQ#D2U@mf}-+q5Hyl$-uwrcBN$m9m7< zFQIRE*GOG5Y3VCDLSOy*n8|(W%@KZVBou`ewSlgIUSPgC|j+wy_FhD1UjqD>&sH{XXEZzKWy?-hzFDH= z>UhZpXMW+6!%5aL7j9dqs#ta@r8`V{oTT*ar6DMXy>x#7YEQ5Ew3B>oB43gNU&bBx zLSd6U3#CMPsg7Q(u(>6WyU#^d95WY0h#|B}gM3Zss4k#1inJTM{aw15TADOVA+o86@>)grKIBC$Vk zN@Gt;26U<1jk0{?r!?tDN4=$(?ret&uaksh5eU>jVUuh5Z>m^1LA`|&)EBPlzzN`2 z%n9o4IYF~MC!klIx`+Fsp`P9WoXhMEPz!X)zQd1FSo-}`twB?Gu&V>>E?N*q2kjs=h$elJK%f&vGnv9xBC^j7rt#Eg9x|!T)4i(BAIS7?fmm#id0ax}sZeQbUQ; z5=1X*3B@TpBUql3nE*NXa8Y&+IAVNvIjeQ|lI~z_ZDI?f!lFt4(mrAJ6CxJh=hObs zoR)(_T|=>65f`!5hDVRoLNqmyXTtQP7nvdFljmB)vp>9)+)0Z&)EDj}O&UuIyUFlf8Uu;QCnJ3phB_ntzRrH~*=+d0 zBO-mW?9&C~-LX&)YC!R}uy6k`5XIXt&CInNh0hE8a12r(#0HZL$Dd%+gN4-X^x8k;DZ?x6+&W#CmmOLoPT7OzIHk7C8i z>wtr1&n3rdas&eh!7eryr=5|3Q1H8RxYR^(SS&Jv3N8pQr?hJ+yxSMa^#Srtl5fP< zo$tHOvx7er`oY2lSdZ1mhWm#ieXJyDSh@uK4Nv!=C32BS?DBe~7DaN8nSHoA*#YH< zjJTm7Vv#6j$A1giul4mFDm|T_DG8rSeJ)2f9h&VM=x>q62q|o6Nenv**43xNZBs!u z;8NCQeA)SA^7CmT-B)k8c^Jy29=zu|L~2Y=$QS8`VjJT)KlZH1fKrP#p(i1wLM_XY z&<5V#fyKCvD+xSTz865bY{^B)Z=|_ivbs-Ebgyd2qIN_sq~TPoxu`)rb}iy5Y!X?O zm8D9U=T+5+myF<35muWKWH-Vggnqn+1!WUD9qH)7p$lSITc}*kD>U!)(g(kifZ;H~ zc}hf+6PRhAY`}FBkz+?RipwEJpGZw?ma^g-CL&LD@Wd2Hk;d?0oml6Cm>I70OY+;pG8pXAst+~%QG zccEw=sE0iRMX8k)q;Bl#g`~!;se<%PgW>+6*m5E;#8sm2>dJF5$eO4ii{3$cn+}XD zKW&3zj+jw!lnbUCY&Fn05Lq&(ccY$qJJ=gXZ?VTlt~mGS^M-2YRZyi++rW4 zQA+-S!>B9h_14kUZ1#i_4P?}8WqvZeCWO92TYa*B#Y=@s#=qJOMRDEgJp`lS6EoDG3fKY1eEmHl{xd@%%#Sz-I8S0X& zvQ4=Vx+ZD*XLA0uAj;2fb1qOe6%~=K-w~4Wcl(z{kWFiNaJWw`xTH6Bx8);su_3uO z(;FzR!gmM@Xt}UisXXOCqYUH9JvcbX4skJ})~XcH6^``Eb!LKGy&au$DYsTS zq=bp?Bx#c*FRU-S`sAu>o#Bj1*+0cj^hGLj133r#Az}3o$=uV@V<^^z(ZM-vGQ3oV z8FoT~rW`i2b7(-WZnCCRt)91-?qTU6)Sng-wZah+pX%$t>hC(|RDArA%MXM@Wk4oDv9leVO#M-aV#Of-~ zuTgBNZ=k?Naq|mW_y`(HtkB4Rd=;vUPMWW5-mf-J70e8YU%#V?Y4zf6$4s%t*(i>y z^@!tv*T&B@h<|-{R(K>D>O=P&T88y@Z+HlM@3~%L9}QBmBUbFi>rSk9D##6qWmts zbDsJ8JZFtlZx%Ho{tXnCI0T>6AwoE~YQwQeNMxL`@M7qIIwF08^vW6dl0481A#8C3 z^8h`LszL+e`JHmLLZ#kuN;+_*j;gH@nN}2*UW6j#3&pwBs?1W{Q5zl!Md@mAj`Ut? z5a~BKBu)h3YwBmCD?mZBq%RPQbp^zmwI1^X1)T5IHc5>{1RRD=KFByn*VTw*4+_^8 z69$VxR&*#!eX!?XWGbGoLnSE;eyXmDjVqQM8i|CLj@AXm%XQQi;+36hL}Ce**(*e? zO`j~?jm0z5sMT{l#wU*3o5Tw0ap*UoPnSHaBmT3uM;7+%K3;-6O2yIhh0}PHcSNk7 z-XPAI?yZ+bgL)z{JTZr7bjP;0BhV_)@N{4{yk|+)}VW^4J_Fj>gQ7^9CK{&W@5-o?k*!+wPv2r-ni=JX=A#+D^?MdRZkvGZg z6$ZDs=wpgS4NhViC(l4XMU9={!w?WE=>RuwKO%Kvflje%MsGO0v|~A>gR^Et1_y`3 zJbcF2rLNl1DYD0EXd*l2R=Ohv#8ERWEhbjZaEka!a4!)*p1xW6&BW<<>X($i#~cWw z5ZRW+j)Fx%to*o79=(Ae5fV3{Hi=VF+O#I|oNIy(SBYEq!O1i6&;eetX`xS?c<2Y@ zjNU_cY1^PVwihtT*6?9kvF20iR*Pg=KCuy#hI);)_;~c{^_p-j6nA_Iy{S;yC}8d($? zYj^O8)mLjyXZmc6kD88Q>|@zWG@}eRap{hO!=05Ryl%(-&Ypny8wRMJAf}*SAiryf zHAJ9x?iBa!c-T+`@Xr+IVhYXRi$+sINmCcpy!`_>KS&B|xrc>j2UGVM%SLEd4+n0( z(5&v^n4F_qmeCGP0Hu) z?-c3F2-Da#~dV0rALZ#U*>7A+Dg( z?8Jl>JBPbM;?~#D*`&OnFv6B8se4GMs2sPopR-CH6O!d)dpey-FaiO|cKzpgTT`6) zmGBYb*>|Z5YVsta8&bvAJ8>c&{(OP0$`LHiyIJ{S$N^&S|C$fJH5F z^6#|BvKA_b_KI-?LHv%muRPjDBZCuo^Y?`j;tl1`k+=^*YcnjM<#7^ky>x`aBha^@ zAj%zsu~TGrk!yQk{Kl@z%z`>Hecy=Fss91}-kJF$eUlGRe>B?fy)PDUTI&IUt#DLG zT(hgFwa}l&Vd_Mzo@2~TK5yu53(IPs@m zvFb#vLEL$xHdFlW8FL&i;DLDR3I_&*%q#gQ<(QS<#AC>JTC;fWYhH2tPp1&M#dCK~ zq0T7&b(c01^DQ&__j|aCe>{wiC)WKPIw){Y>?Paw!T|WHNTB=GA!;ynRVuRGx~JEtmN`m2R*8uE)V$`7 zZdR|gh;+bK_S>|lcw$e;e*Yo%l6ixWPrga>F6$T7Eh?+jr+o6HHV{l{RP)5j6??ND zqc{)h(DwS9Qkq%V5)uE{2gpG-5f0+1QoTh=Hl@vzQjzSG80aLIvydvD-n)@vD_qKm z|0GID{T3SR60$B6rc+#bQ0iGUSvv| zVBTcF&oz*Oy)zIysrmE4sZ1ya<$wX4w_&E%iSYyG$_?Rq~QM7~5Ess88 zE62K@P*D+ht00d_{u1f|XW~uJxGL(cYOb1k$%&@kidQC`ddIy2>ZN{JQzi8t_-57A zyMCgnciQAr?@XdzW-X}4v%^c@RAc&qebt!0`a9M2%6s>%nyxobG+i$OUBz#x=6b5y zeslSEs;=K16J5U*TTs8_wov^t)z+c3f1_{qm^yqjf1z*sqG_1byyBm;b273xYA!a!Nur113M(8a1_mKj zy*;ODNjx*p3q1r*ry%2@!3yRku`U3i^i{&Sg4DG3vRAA(6)V=8@)hg8Q5EYC&TCmV zLW)DR-!VRso|Kml&x5WT^+G$ofv?ctq~BS5g`?R#@qi6Ly1_!nOVA*ftCft+isNF0 zUJXiRS4^jtUWuec%b`6?~@ns$AW7;Qqw*`2nh|2n%)MuKPzy^vuv{J!X z6v+iXkC{PeDGs89q$<7zH>N7<2;%w$cB{dEybJoKficF@-PGgt`+$92V@E93vAm`0 zz`iCd1l6gY2Nsg$Ksxk;)7FDHl0&y$LpZDp!o{z5O362?gLh}%Q{>)U6DvdW9+uPLHap@sv(2+LVTF9a* z^i(hlhQr}&kMxRbkDSGkdc!?KAz1jpDTy)PY!mMti9@!kyvX7u<)=I5ryf3<8y&)) zLt*3j#0fWPx%0(1#a8lsaq$3`bWz%(f?kFhOq!(-r#^`tyikaGV`!+O6A!@*l$L`+#+Ub$h#!!tsr*#tJ8RH( zAi^x^#zwGu@yAF=ug;LsX`F*=QyY{@0cn5q7x@%0N4_4@62wLxg@*fyI-y8U%l8Lk zgcNo+dIn-kJ7PHlHh2~=5HPkj0%}8=Zo$KT(ceL5C-zSl(cLTgS_#{;RIe48#=a%l zt=4|U@#ktyu`v1L?oaX}4ZYKJK6nvY4~^9w#w|@UE)%0NpP!2%8{dK< z>nzDJpZ*?D?i-Hbe_se2OI?wnj z-$C&}yUpvA>%*3_yOYeGX}s7Sf-(2GZ#`YJd3y8yi!_oHECWQGa1B)WIB>Y~2Xr#h z{#1FjUrXTslUpht2LXrL-<1NKpr&5fY z`ea^bCXZ@3m+@@0yn(Og1>>P+Yb9YqQBw-)(=35O?+CihY71JF-cX~;v%p6kvUtc2|zp@!4&%zjn{J6OSMQ^ z`<$eUrD=+vh`qOcd@ny0=Wqs1;xsd!@w>$IgXfuyouPjBqyqmHESbBc?Pu+ z)9)-Bt4ZMYM6ipo0h`k zx3oW8E#pE_cz%_r7LY;3vcj*IN(-Q#hxkWN_JO{VA2 zgnPk@qfWSwzOFjxqcKz59g7iGJ}pT8S!(%WBM@DCqg-ykGCWC_HE7tIO0zNfyOn** zRWNz51w70)bBT-NuUM#Af3k)hBei-Soyu4_OaOJE$1XTc4YX*|YGEXcQGEUkoWe7& zmqUkTOfYu3tmMt;GZl?P8{*tGLJO-?qz2(Q8i<)+Vj$K|@;eQeF7t}3mR0|7!Uac; zHe>TE2Jf7s`NWE6F<+#9Me_wzns^5QN7g%WgTEU~G8})f+a!V^2I^4MBMiQ2oXe#v zH4neVVxw0egw?=)E6z_po}7K%QPt&srdW-OWuu zRSH!6yoOF@rv89bbsllg|6zap7g|HUjccrTx<(8-=4rU;r0!_eYSC{;#1l7a`S&A6 z$*h#UT$#~RcVYy+d!JTh{g-uKu;^EPbDxFd#g3eX_KD1T-3o1LnROJ;&^}`ZjStBu zjV@5@6;B+cDXuGC|G8EMYaHXL&)*-Tjm8_18mvmJ`kfSN3zjpa9xz|B7RUVqVgSAS z5yMswKX$FQ3Hpf7i5Gva6|hus_c2-l6Qk8)B@e@qrdyWsLPj#NS2IMo2SBnK@a**#%`Y*B*Y_7USZHJ6=zBw4=Ll?MqWtD_zYaM67 zYWG59s-$0A+c?WTL{<1^B75=$8g(wwZL2l0|Liu{R22}j|35MH*J^3-$iDQajbhSx{MO*UJ`D5vG3i2QL2;` z#Y&{#oS+$FK*g1=Qn7Enf<_K7g%I$b{OTVg_H)w5fV(nbC~dhteu4&rGj#g8wW}5O z{*L+`(xq2XEOho_ELNVVEfi~RK%^`pp@`EdY7-l+q30=JP#0ETaSG-{Oe2508!;0J zinFjyY&cQF%74DN`6QBQAt6^`?ph>I0wWsLdbGIyB$TeNB|H|zOPKlMvDLtjSQu0P~!b;6YIYszWFF|iqvEDP0Mdaev|(o z-ccDNDAt8M{43hG5f959l8oZfC$28!(M(dql0GcDA8X6B3*y1z{-)JMT)ZF>EZe_D zY(2@PpL!-W4@hm!5CNhTyllvPl06!w6TEmP@F_d{75|`y_$QG?En#^}dDU5(VVdL@ z*P$1cqw6nPx0k#BbNjEgiQ1{U6V@agWow&g>qV+f@=FU&?us!EI%~WV{pIlE8XW}~G0Kg(n-0Nib2PokLn#U$OSeI!GEwerPh zF3|Efi3wM>Tu>QLqneDqORNsAjw-HaFUZkzOrjN1{-R4YKFfxqUU{d-3MgK_);XR+ zVkqwK(r@;jyed59kF}g0j5CZ;-k+*Xx>-Yxh^dLC$ ztCv>HVS5ni>&XMhiKQ{ZE7uSb_Yn5U`{|qg3#CG#(w3`PQoT=|ejW^my9Wc}oi(J# zP^VWzoluJ4Gz4tD4u!l_vo#licTx6z;?#?@KKUmuBb+i1Q5pIs`ex);{~P^I$#3@W z^gH>a^pW4R{AT1=e~SF$_`Y95dGp1&uE#_9#jA*ZKc7LEojL=UkwQ6rNuid9Hij$F z^5BUgrR6yjHi@v{?pNm>mA*W0U)}BiWv=&wKhZ$P0HgzyrhonrpaYchJSJvz^;Yd^ zmueI7F#3Gb`HS-|r4Dc|kt%a7eN%rXf>kzQ8azx~eVJCyly1Z?&F+#ffWmUYTRKka zH`+Lq8O>@F-v_~Rw%yj(Xw3Jy$3d+4Lqs4$l~MW#$Cw97y!mtGx+}(04U0}@63&+t zpq8O}Cs1#UDp~u=x`&mUtDTpBTv=8>DN0D4p?FfiOm-IIxJPmLlUI$0!hG7J2_2<` z*2^A?r(`GjD-kP!Z>3|YsLtY~-%cd4t{;O~r(V4^i1iW?EBg=T(c^qT+zVPl*Nss_^rmsECdb$GR&-vGwfn`#(e&w4 z>6?|`^eXxLOsaK(POS2qVwGcg<&B0u%qPyhl{#yJ@Qa^M--P;JK)BLFbAd=#r+Dl} zO&XxYVv}}CQV7NPzLerL$Vj*{H!sxLdDvm%=$jz*KYa&sjsHg0m-nyLCU=ORYL>@# zyjBXGCD5B8obrFmMFG3>djO_{L0ZwFi^90>UT?(VHf!h zDkU*SDFrm4BO69u)(PT-REf$Jq%TF&4TMW-Ok8M>iS~`vUoqiMy1gVClvG;Um1&EU z-cJ~$B?caG+3lLov?3Pj=@;whc*E2>h2Ztc$0gPKjNNn-b`EY<|ML!w?b8w3r#y&C zWv)j@s#US^xI=g`)Fa}d-)nh~Qwn{(=EZTESVw3uipR-}{El6ZtC!31#sY5o;eH*S zxL6!mB2HSbm3$adoVp&gT1Migyi_V)`MtJ0=X&$^)Y2z@5?m>BDt+~{=$pEjzS-;P zn_frX#6LfY(LU8)^c3+s3y$SX;B%Jmk@h*SSpwqjJ4=(=e1r4dV;m6QEYFK(?$o^9 z;^<>B1L%(vHT5SzO=qLriwtzOVhRxHyR>F9V>UE!c$kdM-F3I-#SR6mt>RTQSgOE- zj2!d6yMe>9C{AHQyz_+EI4JVK3#T%*yw?*Df2Q4G{V6Ih^>5jRFQQbOjyZLM)_`|J zsg&b4YW3ojjas8P9j9+G)eK=LzO@^Qm|(C=+ymLFZYE&`LwAwBj$UQ38R>4nTk}yC zxX?o8HA!*R)kjwK@=>^SBj&WLPzL2ert;LIr6b~Z_d@)jH{_@{qk^GWM?W4Tw0#&L z5)ilDt2O4T7Lv!oic9;?lc;bKl|;2)rf*Vy;}^Tob#a=kI|7t(+m~$V{(PfGIFY4Y zsSSp?He8DRH=QGkU*e*)_zDN&Ao7J9H5l5%359F!MR@v33Qu1{;gSa8o{btAEGG-k zG$KdDUr8u6T~BV*@XTkYv~BV1M&yvXj&jKSk#g{XK`5b}3{^+OiT8kHtEncbEQLc& zXTClb8pN2#-Tk2@BnFI-eT^4!SqX}~rMM!7nHX15V_n0&cxM!svU^aV6b-QY7>uH! z*t4ap6*4(atGsg$O39!U)b@4y>RaUZm`~B~l=0{PU+4z5Unvrlv`Lbk=7_i3}JF*t;|BFK0zj>%Dg zc;G%wc0=_dNbc#%r(x=qB18uUVeA#JdBEf_kxoxi(P?~zgL75S{*H((_kks2h~*mV z_4}zRzoocD!hs&u8O_rH6L2`-#rripA2fsqBBeeKia@;IC@wz^3?Ykxf$%LT-dXYx zLYXh2kHFsF0~#!jlxY>U!6M=}*sLevS9p4r7EBhgPAW>|N)NlJ9(1Z@=$EaFLI*GM zgv34XXt2Hl<11yN(`Z2lmfDIWTKE;C(`b**9EFZD|6;-ExOX+!b14&{NuZCoh%A(k zQoh$%*yK!JL&%aOZsa;iq06N5@egXvrgYRoowd-Y#@Jb^9Klaosokz7JIC=yXh3xJ zN~Lwakq+x|goz~);a+^+UtIi<)_})arvxs?{T26_TE#sNY1Xd(r*|}%cQM=KJ~VQT zyrE!vt@tYq20G(tdi~;EZGkn_W%3Lo(H#Zcv55+px2UVHtJml*JyDpY8H~Y-Mt5K) z+j?k0iv@b3H1@hiM)0N4hm!}A#rR+bo2;iUhbfK4Ee9Tk)k#47RpL4sIse(LcBQ%R zKMLM|d)T00rMb6^lKW|@F)GdafKjqeQoF_yPD#ub;*N>8DPLrv*A^a!{bJYw zreKSO=@%Z;4leLIj~XwF#)I(j4=DpYD}@$(gctx@A=^4RZ@3? zu=*bL8vH4p-djZK_^&=qo$M0&#xJF>e!2WDzX^OxE;Hnl9&(}a$P?pVXdrc~oCU>Z z((fi!sn<~18Tn1$LBG@MrO!tB`;YX^K0@Erqx97uC;YHV!n#1MY`erQPr}B2&La{L zzbCOjzGep`NXTzWe&ZLBPfC6hSIN+I^wl%+xB5O#zq9YrH~H%wQ5Y7ptqb})IAdHn@`50Z{bKW9hE?gsj%Z=-KUe)Sanj>~W6cKV%tn7;bIKJreIE^%?hJmsLY=g9(#^X z0u>Y~A0RTeCaWz>jiB2kC;By2G~hn#C`vn5T+07efx*RYPo?dRm`-g@Gvc< zNjtcWvyaNKnYwE_I>)E;zWe9#)>B`gJ4?hSCa#2*yb}H(yUXRWyWA){%FXmm+(O^@ z9rCyQCh-+DcoHD}f(A?OZ)7!?az7oSjJ`k8Q4kg}v zO@p>G7lfxdEFDnjvIm_0St2>Uax(EirJf%x7+lcY37zS1A3801ulVchxQaW3hz0R1 zv0&Tld}1OQnR)zIR7664lj`?sS*ZM`uZnCq$kXHVG~ z^-W#7bJ@N3^moc3<`qOt{W{8Azn;F?b@F?o{N5zLH`6zLCw(*b(>M8d`X*kGKJuH$ z%HQ%Ef02G?=}~Ont=t-~4T_UiIIzz=IwCoxv^3mV zr1>dxqe7~)XF_FWr|zZQ(A7l5MFP{GFw5N`CbhyZ!2Qu%3R? z%CpFzzUFoiPMzdJ;#Aw`kPsb5q# zexCb9731e6qEQ;Sp;w&mzd< zCT!Cm_qg>>rU@CumH~d3%jI^tgYaliJMargIo+|rX?LZ{zHmX zU1|q+8uUTU;@cy~tRt~QsxI>h%E+IZ;mJ&+LS4bQhNPY#XbFE0l@km`Jqbt9lX51u zPs~Woh;L6+OoD`n?+Uu$U`UQ2ypuqJ%69vaIj{urbq%rce|+cES3Djd6t7Fn$ke19 zscF%K*6#Kv9jWbP)iPgnwR=n5NfewZ!K$D4@>e&&)t~aHVR{ zWKR9#{)978ldTJ;wG`qLt?*xb`_wKd&W|qX_j^DqPjY)t(3O}G|8m@w)!;@)1{E^M zF8v8)Co36u`m-|-4Jpww9#1>c<4@lSl>SsO=}0@Xpu9Wi$!wRM7IkO-%dOX>QI5YI zgrXMA)OfO=1_k1ubD0D{b)vyS3dBDbpO$u@x1&qB(NAP~?$jrMu_uke)Sj+MAq4o= zCO!dt<4$Tzm*4MCZ|{kJk*eu&5q2&VI!mfX%0pN@X8LlotB+THOQw9co{h9HFBlL6Ia06IrzyP!TJ<@317BQY)G z01#qjqY!)HC6@Jv3IV<$o;Xv8-2m5Is^FI09Rf$Jgz!4VR{=N8a>(#qBFddP%^}?I zPaaz%!`J+wM#g{TZ#7~_L;O!W#Vmv``ixUx)g_Xm0aaL>dGukio=i%u~QaOb6U zG9SD;C_aJrRMS2o07N~zv+V9-w+jIj4s^wrG+m@n4{|@m?ku~XXSaK<3cnhWD1MOL z2cST5r`df0yR+iKQPgm)K z-O9b3-RULDy&Y^FQT|zW|B2nMhzfs$-Fm-rcjK}>#ZQbV_bu#BvHP#=Zofo@&&0)7 ziXU}4WP2XX?l`*>>~__u@Hg4*VfSZn5lP}VU4^%^yL~(5#?4F_9%uKz+3nv+h1cLB z1Em+cDt8OJ+rOsVU2s$XOt2f*Jyd^X_ucHyvRlL1X^QXKL#6k!+t2PWyW81)HoH9- zZ`7Y3Fv8hA0|#O$Kb_qz>`phU_&;HHmfe@J+w~0!nPvi@A{5Pza5my5LH4s zX7>-+on`mK>~`(1(!a-U54-;h3w&7~yZhN4W%qUL_J9#ldw=5dDt?sRyRkdL?m6sE zvHJpc`@xSX{ar@<|5NTREQ6^06uaMJxBplb{$CIiDLjs45T&ODg^bVcC)k~3_lz&9 zaQ}%azK`8$c7KoE0*eRA?`Q0Gv-^A_J-gSlJNp0I`wsZXsqJ^elJdCxh-za<_g-s1pCZ<6>F;^7ZT z{MQhV65mZcPW;LPCA@+4o8sp!#1Z1f#O=f%As!|E4)HkgOKZswaVK%)!xDc#@i6g! zh^L504`TUUBL0s*Snvq(6!AFmb#I{ir^WyM#AC#_zmfb9-*<@Mnp?&Ht%nM3A^zxL zg0r`Y`+tc?iGO{#xQ`R>e}v#E;sf6#c;t2o{|Dl6;-9>k{)sPti{RQj#DDu+1&AGe+%&v;x6L*iARWAB251-3I7e^N#X;d;$Cx) zxHl2E6W>OhB|bPteu=Lp9wXkXPTae`An~mv9w)w#c#^m^&h-CB{2vB6M)yzRUlETH zU)3P)lf*|i3O4SO@c$sLB|frA+}nx&K-}_U@qbCPxMzt^J&N(Y=*7DK9PoC*UBs_A zTJQ+5ag5-pmx%x05NCH4JaVkKj}jksoZyL&5=Zx{C)iH)NL*PI~k%)sPQfF@w-JvK?+rOt@iR$$0dehCiGSzSOrLlYv2mHW z|DL#o_`o&dzl%6YJVN{_;tAp(6HgKEd6I;$yVte--xw&JbMlH^ILrp0uzj?k)ci_xrj8cM)Hl6x{xdxIaid{`Q@9`!c%e z|9HW1;*o`d`-t1|2Cr(5cN32i|A9DrlDJ=(lK4i6kH9)ewWkT<2Z(D=7XQh#xMzv~ zO*~F~Rj;^@_lf^;;z{CP5*z*EzVlk~UrT%_aToCt;t}HY#AC#}VO^&9Z5fpKZYItW zw`Rn>eMsEjx?b?)R>7~{Ah_l-!3Xy-KH|?2XNi~ei~HzR;{Tt-ksAcxG$8KTj|o0? zP;lgvg7?n~9wz=4@yMsdef^o@-g3L(WjVnU#7`1W5ucYA_nJGze{DfhK;#X{x_>4Oxd>3&o@twpi#5->i|6Rl<5f9%d z;jbqiCH^h(1aaGD2|xUR_`j2Qf_SGbi2;;$vXpSX*7?b*y9@ypH;JV6{Mo+3Vj zxaLI?|A^)OV!=-k*X}I%@N*@;kv#?X6W6?6@cqQK#B1Iq{@aOnA7*&se&R9W?-5TD zFL<~3uia1LzlV5&`1i!d{^Gvhd&K`3@p9q`;tPmd4iNv}ARZz9hZUaqkoQV_lf-L? zr-(m5Y#b=@eV(|M__xFn;@0ydzAW)g#3RJJzK{G9cM;dtO8lQBZjT9G`F`;~5f}W7 zg&PDP{sD2HB0h+|{)p>{Yc`4d5OE9f7l?<6|4cki{F;jxAMrxsn#~g52I6+&&kzq2|A%;z zxbA}zU;7pbe-805@i&RbiT_I+`Ih)!^dSk~Mf^eHapE5kPZ1wleUwu<`*@!x@!{%-snao^)I!N$3Q4UaocQa+ z`&}vSj}!M0KSg{t@qdZ$A>Qi>$=_qdhY-K&Dv7U&_%z}+;=d7}L45Sp;{QzICyCD^ zHm(u(D=h!Sw-9e8{vz>}#1q8#5dSan1o1zJYp_wE+T(6lN`Bgj_ah!5ejD*k#K#lw ze656EP25GCA-<3J9OB4z;{OujPY{2Sc;D;A{a)goM+JX}c#8NJ#82EH?tdr#8}V*e zNq({)7x(>%pZbL0DDk=*1t*A~yh-pWmiwm#_gn6_34S;6OK%r^8S&x7Hxn-*{vvUK z_+jGD691Zbiuf7gnmZ)^J+GGhj1wP3{1kCL@xEi?zm<3q@#(~;5uZtXKJojACy1{i zewO$);{EQF_`gEjL;M5cdx(EW{21}SiJvBZ^B=I=$y~Ixue}{P0 z7bN~)5T8x_cjB)95%=A$ll(kIyg%{b_lbLyI7^%$zK8e};tAq@;+?-J;onWXjQBF* zTZwNb{u}WZiTC)DgnyX0iTKyVJ;cutpG~~y^^%{h#0L@IN?cF8ZQ}P4fAlNj|0?3o65mGL{#9}R3UTe%1pk1ziTHQK-zWYz@h9#V|F0aC{H%LG z@FB#*#LdL}eO=s_6F)_qBp&;QxDOFONPGct&x7KAEphxI!FLirP5c0H%{RsUapI$i z|495%;+<{~{vRaXhq&fj68;F{!-i*8$G$7>tBEIx&m*q+p19vjJWBi%h4(a!k(ceN z<%!)tF8DFxqljyF7xzKpEb;ZkBgEe&9wUD7ClKGuVgEr~Py85hFYzStwZI3weAqaA z4@vLy#OD+Ln0So%S>nftU;Rl5zwe$B{_Vsq#G}Ml5n+7#CzQ&?)MO%LcIBv;{ROYQR1HyKTW*n%@Y3P zy~O`9#5WPIC4Q3lV>oWEf+#m-rNMZCva%zGdMd!LPeT{7a1_&)_NC!QdFTH|43_*rpZeXIC48YKQ}HOBrm*Vp$EXBq$Aw~7DAIpRM> zJh@Tu0~!w-U9|r=^mgSR_6V;Re&0i!JwW=y&oqYp#VQGZ*d5}3v|aSmmlKa4DB)i+ zCh51Y7XOD5*X%ENA#wI)g1a;xHbxH@d=_#0euB5s|Jb>L|3o~DcB0C2^_{{`q+9S; zi6_q%9Jx!}hhbQx{S%K7pG!QkR@^_OG4fj{;U6K65F4M7{7n!?iQC^K{yT`fju4z9 z9%FgjsPV8dMt#KhiCf6eQ^dw!g`b^2EBv>-PU>Sn3n#@rPCQJ!jCh>5hq#9NjI)Wy zE|&0HiQ7Le_*RV#qqbG}-}`ftU!zlS9dQ?Nr{&%*?pG6!(|z~POZeJV;(idZ(II%9 zg-;ZG7xDNK!QUdzE)=}a-4b6H-H#-mS|skH#FI^e_qs>?*U;T09w9zoWAulcCI7qK ztNdd>jrDOFadtl`|67Tt?iT;iFNl9*QqsGac?~6=tO492kp1eiUdyu&O+v30JOX9!l8Nv4w53@b(@nvzJVtS`p zcvngP7sM(x)qjp17ylEF2p%OKA^$aBA%E*6y-SE|!IujEC*q0YBz*f<>Aze2j}zAp z3tsRwaUXwB!e2)mStH?%`^CNeEb*Tu9^WeXFT`Enm+&hd5dR~65`K($iv6MX>*5|k zy(xarAs#+f{QsPI67zs^Kjs_a->4DyQQ~%ti^}~!#KQ-QdlLCj?Q0zUOu2uV*m#?` zAN&yGd%cwZ2I6+^k01C=y7x=?JBcG1!Rx*y?qdUjpCz88{nXZPi~BIg%d@`2__oUU zz4E()N3RmR^Y;XgUoZG};?Y6b4>@T<+$Xqy^&jHNwUYkG!{Xll2FcHx9uaJ`34WZo zWmk8=KQ1`?6T#miZhu1Xg+CVek=25a{fXeoZYiHzh>aTszvc;XAN>pS zM?A&-(!VOa%dpY+u<#%Gso;Gc6+Ec0%0Ks;zezm2O45Jp&%`~_DCKoM@d)*|=l)#W zr&zvcObQ-plK2z95M0CXXA!qAm-y}^&Js@&kFAmXT>MK3KY5toqyJa%`0E7!o_GR= z!aJe7Z~K+FcdZb7`mY7oP6y%3 z;Bl7!SBYCzNcm?mj;s3ZVtpL&7s1A`w4e7APq056`~Sp!nEOS4Ag*bX`nvkB;+{QI z`rm@T37+Ks&?aKz3`zeLe;4<$lLX&RTzjIF|Br|#ZkG7|M%+dF5g4_R3;v2tG(z}d!_$I+mXbgGvYQe94n)$m<@JYlq zl;3Y49&Zx&N#c=K$#3m55`L8KN#YiUzm|CXa0!1W@$i0vA0QrU7ycLjQ{o#tQSd{= zqi0C{ME^y8mk54@c!cd~@L6%M9g*-g{}$ZE@%}F27S_*_|A>2bljP?K;&I0R?*EGW z6x-Ln=!JUz;dpf?@hJT--AUZD>m>dEB{oi$_}}#+aqptN%uz2EJk0if+|IfYcXM8;`6FkoO;MKbep5%P+An_>s z&pmsHdzN@`Pr)_BHxf_%SH^=i*t}BZHOle&WoYaQ51%3KUm`X*e&rztDED^yKjM{w z4dO}STG|tA-AmkS2c)57Rzj2y&XPU)s~{S}VAQ_Pu{3ZlV3|=MED036|HH2QxhFv2G?Fr~4%F zB>As@gZMXieqj~y2_ zJeiR4Ig5Ch_GGtO?wk+bc?A8}O8xwVco<4IHJ=~-CUKvled7;_Yq});SH4-?YuO%; zCaxjwC2nbw`n!a9gzk3{k6Q8oaXalt|7Bsezk}W)>DAEwe;IKL<2#c$OMBq&y_Not z7JmPGq~PHP1-~{Tc&&T|VxQq2S*(~l^ zw*SkIVtnjRC%s*84cq5Sj~3j{^uI_v#{Tx6W9Xmb#f4bcss3KW_?Kc`s&FmGi@y?& zGyI(`bSJ-;E)bmM_;lj&f+txXUm~t0KW|$o?!!D!^g|0XzvGJ-zD3HrJ0W<=6#Pfx zEXS|Z3F00hKW|(tILrRAo;bqs_%;i3JpK!@LHC9wjF0-MJn<;UNSI37Jp+)jRXJDKrO z-dayQ%J%+w;+kfW*Yc+@zOyC%PoGL0mGOSx(*$>&E&k6tUGUf@X`fSP&^;sgucqM1 z7V-b?q~MW@B>tav3m&~x@LoN{M~VOI(+to2|Fc(c_N~Itq3Z;XQ$9Q~BY0xHq<{8$ z`Y#Cn?r)X+MGAsj)`tg^TaJo|KFB7(?8%n65lBI!`2Z`(*Hxm!;JsP_rm`!Sbu+4+RtL*5#l6q zE%i<36Sr{x^H$i{$KyVkwA{Jq+P5BA_A zJvi>ctsdMhcxS7<6Ac1?2Y=IpU%l1sKjpz=9{ev4KH)NV z_)9(b2@gK#a<~8W9(=tAf8T?5zrr1Ukq5umgTL&-fAioYu5`zDh6iu;;BR`cag{s# zfgXH{2akI2PdxawSG(go$%8NV;2(JK9@n_TFZAG~2Vd&JcYE-I9z5y6|M1|qebk+P zn+LD;;43`%b`O5QgMaM7fA-*=uXU%tj|aDU@HrlQmj_RH@Jp_9$A5&z!}fS|oCmM= z`0w-Jb3J&(gYWj>UwH6;J^0}3-TXCs@H!7Z*Mqlu@Qoh)OAkI^)ScctJoriv{(%SY zb%Q&6y9Zyaaj|?a_uy;AzdfIP+7tc(kNcw@_uqK%|9SA9A9M3}kOv=*%RpCxZUB7@ge@ZD zNf5Shjo*Q=6>VU<+Q1gJkp&qj8*I-SFT!^h(9WP2gFcS1p8$OlbR+0fpqoH9gKh!g zDiz~aP#?l#+sVL|rm+^`pc*l--EKgoV)THp#bjVR(}2pw=m%k|*?@Y*NP}LBzsEqh zHpReF%E0o;z>>+pH6+GeAZ$k%p9Osmgsln#+ffFt3^AaVFunl7bsomO2>(OSeW1tH z7r(8+_Y_bks0;K({5Fj5yFu9EHr@;R5`NncJnRp8J?KEtm+{+6@ZA;kNBDUizK4SL z#oveF`+9trfxZfV_u%_Q(EorQ1APs@-49B@-wB{aAgDQw2k_h1LEivHfTN&#P&4R3 z{Pqy&c>KK(^iBNzb;R`z(6>O}1I6(BQOYm=o>0HT&$mI}0eu(rJM?v2Q z{Q&es(2qcmfnd^MOo9$VTnB?5$8S(i8$SU(p}uhY8R!X6E!?5HH+}~CIcO5}3(%=> zI~`<#ehK`)pkIMn;C}(=*ZBK4px=U?1pN;5d(a<1e**m(GzEg%(fAwa@1TEx{txst z=o!#ILH`0h3;H+cKcN4D)W87su7N95j2DA;2JHgE6)MKAAY6rFK)q(X4759F573^V zmxG{|GipGu1nmXd8}usBKA=~FUIW?}^jZ*9ddBNP`+@cc9RNBIR0}!?bTH@*pf`dJ z0UZiD40JdM>OSL5AgD==w}9RXIuZo6v$}f4h=O7usKJals2X}<*D2GGAi{{}q^f@y(q6X+Ju&7fOBzXbgX^uM6rfX+ZX zuSEELK}Um*0m0d)Wz6$q;K}r194N8G} zKxt4fXf0?RC<9s#+5q|h{P*E|EW-EW`+eX6d{4r65Z{gXX7T+9zGvdQ1>YRLqxk0W zy&T^HzHdhOA$-3Ayb<3k@!f>))A(-2_ha~O!M6?m&cb&q@H_Dx!S|z}v+?&i_@0CB zxuAD}E(IROcN4zv#`k)B--GY_@qI78pThTc(0Ta#5`5o>Z#TXtgWiw7S0Swr;CmXr z=i_@4z8B!T0^bYqJr&=J@Li4X2k|`}-w)w?GQJn%yAt0It^RiM?NX3$Zf13?|2H-grJ4h5|T-HfoefIbbn6?7Zu zcF-N5G0>f$yFi}-eHQdN(C0ySg9->g1o{l>2xl zU>az^T+nzuXg|>YpaVb$f@(nrfer?}0rW=DA)qTk*rG6Cc4!<9Is$Yg=xv|~C<>|r zHGmpH*p4uoK}UhGb*Hu?jAKB@f{p{VfEIvY24TQ-!&n5umYs0|s1>vn)CO7xS_$d^ zb%NdjIt_F>2qr;>3F-!=Ks}%|s28*rv<{R3tp{xY;Yu9i0?>t^4}vZReHb(X`UvPy z5VpyT4}q{fW?Tlk0(3R#8qh~U*MhDCT@M-s!OX+>80h1mPk=rN!oT5#FOo0J5{#2T zCxcA>b~e7}fZhu_4|E~uBGAR44}(4ex)gLd=nBwPpsPXGfIbSk7IYoxdeA6HrRSt^ z{Pq?89tS`5x-&Os(wo#ykOMkV|F?`PLL3 zbXyq3!=)*(Xr}s-@SehxX=Wjv>(2}z3pVQwc%m-ZpXY0Q(YkDMOWz=#TGKzbTCvv+ z4GiXb(z$ex*`Lhf=Q@;&lN-ede)DpnessJwr}#?X#Z>ikd|fiXt`$H%+gxWhrOzBn zg26%SrzTS;BhPEMsYim9SN#uJTiLnP)Old`eGCTh7sQ^Qy9V_dBAHXqRs%VBlV9)# zweplrW^%1^hrSxKd2jC#|ME7kqhg z92_~CgVmet40!_9txG4-*z)d1ST|=auP>d?m)p#iIGY(+m}T3N6Hmc!bi(O;g zVdWU>OQ$y_iyqg9d2F!j#8c)C8`G)6U~YCDenltxvekQa-(Bb|)M6Tymg|a6^(ef4 zW4=^lk0*zEGU}sW}tvZ7)J}qZ^;+Z{g~7S zQyVad#?7Xlp`4yMTQ{qTq+Wy8Wqr{ENIgjwiE!QssV-OgTeK(dE49(O%s_UiU?zK# zSzNC(FwVpdY;q5bH!{YDy3JS%;l0>T4jL{+*16dm`E(^uhX))W3dn%xTF~0uPb== zjM#b71sPQ}Mu8vYC@iwAqz8KXf?ra`sHIv$os3h1d~|u0xhjGd-q|SQnJ<2K%H=Gu zB4nsdDt-DTS$rVbpPp%;#(oHc$+hW3rr3vE+y$@ZN-!4~U>e>6!%WLssfA++21^8+ zhRqTor)M^J86BJ9GCJIU3Fa1Ap`>v~t7Nt4;VNdDYE5hwlHGl2%&J%)@)t;7Lupez zQ?GxnZ1_gk)T`MT(s3>4b*h-h;Q^95(&g$``Crx@#UzX0Vo`4>yRO{Jw{q^ZGvNXY48M~#dAp`1=y ztz24V8PsT9%8^(iY|$BYD@S4pQI4>7Y_&(F{ne?Gu(*}DqfpmM^d=;z*$n7GX#|vr zSM8ztTF7dn&M(0RX>e^x$n&zz$RHM)dRac#0z$D)r&I4h`Kt9aafpvifqI&%=B=E< z#R54a8)~O={8WOpnq6~PL8@MW-5@jFm+r4zMz1%`TsoUjy1`}L&<`%O7?T0JRD$kZ zwq+S;Kb7ddFxJ}9))_N5CK4;Xszz(i&(VUApq)u?OKVPVPNlPIi?Wn2S9`R&OIvtc z@lETJ1+D?Zec7$2gcmEZ5?-w0^m=hIZF$mtT>a0X)KUXWMca0>s6gie+0BkU=xA15 z$}3b+=qx|uA zg`Uw>3ujcIZv&k_%4^Yp7U`;oA@nkiPK{g|$y%~nIMm}nHFFq^tj`Y)$Py40T;=8I z;hL}}+ z+j9-Dc)_MIa#p7;0FWtnuCt%wLD*Sgej1x>=U6U0&4G zHhYN~w6rD}UeuV=9MJVt|6wler9gSP?01eh8OOq5`qBw**t%T2#)4%HF8&fl2b)0| zIfmenGC~UWAS~U@vvg{m!GNn;n{CY$CaQ>Ogv=GQ9dlzr6))yiGLSl}vZM2a8RVs! zCJ$xZrDhIgMke|4PY);fr-$V-JuG$(m!ZQ)d-=_(^7dSEvUJV#iw0)Y@tng_>4sNq zqH6vxuK`-4lV)EHIDzd>4X865YV`12*@l?AWjF4SEgLqGOFI@s%{FBh?Ur_6>+F?w zR!DQEL8@KLL9#I9gitk3)G0KV)Z{ol+N=JO>Dr54nJQe{N0m(gYW3Wxbuej|<)@PQ zv{t!6?TqGYTYZXL)a>l&+b(S_qqx!z9c0ksoIN#AfGDS&tPWY>46kjNLQ2)qhn0w3 z=j>QMRfea&>+)%3rCH{v;w%b%FrXA$j>@)mfzrsfQKFX8W*fI`PiD7StF~*lA|;xo zC!cv>JKbr7&2~$&*A_EM{HA2DnjfTc`)A!A=hlv<8A~y%=h=$-*3QMwt}+e#;4+5A zO7~D|1CE6BW%32HCyfI-gE#}E&F!6?W1D3)##M;6)t4N~I!R6I6>mpuGxn`f5$hqV zf+~pozYtg_^?n*I!BJ=NsHEYDP#S0)CkkO940 znX=K`&1cN!nBmGR++L@w**hb-Jj`ltJ~JF>h&SU*>s~w01F6-ToaNhr72eE>*fJ#4 zlR!@z_Q*p#vox=L!X7OBqm3vip|W#iL`{VrbD16EtYyr1*3v5Q=cb)1))Fh-DpsY` z(_`s#G{dTfT{`7b>z5r;Y-vUQ9DOlBhObO*>l|R@Y%?dFU`42!mBhfhgbAjsW!)zv z=V4F7njUAvsb2ZII4#w3`~u3Hs?N2QdF8T6a5>Cwp2oQ0*bH#WB;NXWOwVf7J(DTW znyBZbF#QdB&gN*FKdf|0&(XF|(IvPFvFr+&gXy_SPNA#e*>wutr39#~bo})^Cj-|? z$UKgdYSqf9Dp41sp~IH$T$Qa%oI>p^wE5+(jI1SE_=-0gH_d#uFH?Zpp|@9`+pTof zg#&<2-D`h8246?L&r?bsUD!OO0+qNoKcUS-9fb5uYh^VSZ)I`8(Bv0V<|&i5nk`^i z(y$FLgHS!OQURi(lD17^19psd+|%xsa@yXU*}fKqcIy&Bd!DSVBFa|AwzhH@pf?8Z ziWfKf_2`AbmbDcw@Ht+Y*Z7Vm>`0tjZD9TuH|6h;L)0y8`!+i>D9=PNA7V!JeyYU!#X2otj=8~_Q5i-pi?*ASqF5YuCU(1 zEgg~Fw!Oik{yGWwTV-V$&Zs|Akb~!@p~-|aO8%9UaYDkgi|xTIobPS}T1Me7Z2YLJb~y(Kw6C8%~K-Kpvh!KBM?(DODIh4-O38KEHeCEybuLo!oZQe; z#rgM)y6$|1^NjEV2P+1~c;Eo{u3&-PZ|m)^`hF?X$P`|PXZ z+CfuBc=iJoI#raTnOA68tMb$|d#t|Avy^AUPHDq13S+;ouH^n@P&m)1P+nSkZ_$-j z=<43jsu+oIz3n=fL2=N#!5hf7Pa^P)QqK0iO>UpSsrF_Q|EbChxTR+Mvgf*)Kv@Rz zppQL#Ruy}mskEwi^V~32RlIF>51>`WTaTMJOIsgHFJ2|CG8!HaOq8tY2Dqf zUw*+T=e*uoy&&T8QAQlr&|$4U{c%_e`fwjs997bR)csgH&hP{At%}E=861SHp3~*} z46b@!2y@T0M{s9&q8uArb9F(4Q*R`PCBr(9ZfS@Lo{kzIa(#7f@=Y#Mu_rmN~kUY zp-k~WCJL*?S7!xO?-J2>3AJZF-^Menr{Pxb5;2x2XNic(F8oRu*J2XGc9}lyOJQVW zY#z#H(>ZlVi+&-fDKnVYhv(($PVs}M9XE%&Mw^_spYUZNyewMI#P6LOXA>2dv8&b% zz(B*Ywwq0aTz$`7gcgSzBj!L%-MX!N$6S=(syU3(qYkKd)-DdA1ivqZ>UM0BtHqAD zYAcRsj`(TJr_&n-dwUajaL}}yx&E~$lXJZ!5v`P09b0xVkG%jh-itq4)yk>AJ6}+* zFcuPXlS=c@03KTH?HkY$ z9CR0-A}d%k7wRp_cMH{X&vpa1Wdc0(+0erS zI54XiHqf2-WOJGROd+!|twkzzEu~q&02($t1$qywv1mhP05{`gdhEFEmhG)v zzt@tQEMLSWwOKyjp(Bu=4ZBUx{E}118tQPZN1%pr=5$ZTbKDBE$q%^-$fUL7^X(!X>qb@zL z(d_RrQ-#g5&xt#-S>#2xhneQ3Ih``+qC$%Cl*qxf1sh8&T!b}SXY1nGm#ZtT5=>3g zx)-V@67?4Abb(Y|%s7y4jOuar84OyQy;#!>1L>wR)0A`|Z%X#$zJ0g$Pz;+lSEacz zhG{Qns_`gu&3q<>W}eFotTi`g(wo#oPHmN1JK`ClWTueU2j(I2_?Nlcy~H~fG-7JS z|FqS9)fsxD1dCQ|(Mn3f%u$wnCwRy9-wr{XI*@xRo zv5p?d6i}Q&KOY$lbBMuV@v@c2FIa|4GKRX%RVgaf7Bab!s;)*rOrP}B(oF;!IHuY*=WSiZ8YMql4!)Ag=oZ| zhiJr~iD<;1i)h53jc6nwAJKq(L<8~>4ai3{ARo~Ho}&Rg2lVV{G=S%50MD_2bOSnk zv@U?>x`1-73*fmffR})=AzB~6OF$Ef2DG7AKpTn$lxZyH=PwrX*Lf@;Z?SrR8N})X z!UhaXv3h^m#{x#HSio=<3+QaIfZkRY&|d0dexB><{Jhk~1AeRbx97Ts0Dl3)VqKHJ z-PZ+-Bk_QK84oD?c%8qT<3W>2y}wPy1L`9lP|op2e}3Zu`G^M$%JG198V_iv^-({6 z^#OggK45CD59pWmb$*`f1InsC?r#V64gP*v-x%;)KpE5r)Mb4@88ig+t%f>(+h_0IgJ7HOk>R77a9Y`=f;3G z)EM)()5ZX)v@xK6GzRpA#sDd_G47u$8sq*p)EM`-m&Sm(qOm?8zJNB_*bq=I0d2A| zVBBqN3<%p8z;jbXtq)Jt?+Zrjph`(M#Bmc~3a`d#O-Ul2?oVa|{Pd(9etPIet{}quvajhf_Nj zzChFkEsHj1GwD=%Q%1j&Ie_&v-a-;C*36LWApa$}4)Q)7*Q;7*$WxI25h7e&j92lSp0ZP~LDi(3n5wBIXwtB&`8;umUWbqEVYA9iTnq?`j z+LLPfR`jvX%-R8H8FeGGTzv66--fk3QDI-M#Z>t9UV-*yZl;n{0LRw;`4iQW^5;GGJyp!U{t>W~DiDXa-t zaW8{xL}?)99UqL0dNPOCnQfKV#uS*aCgbYf<` z2Cd8b@Mvitba3h)c0tv@z``%G+PWDw!5z4w!YE7KJcX}}TMLm1gs*n23;Z~W>^7ba@ ztbln*Pf5_l`6aFktk~16btI=1d&gZj4$IN8$aI9%LDWWmK66$&Ay+y}Fzr9kMU`zH zl6kT)luI`-US%`}RB-c+pgI6R^LibHqQWpY&XSfAb~R&RE6 zF7q{q$NuC7F7ZT+&~0InrB+BeX14kuuXWB9?XUJoXQFUvnRf?=S zJy>0WSDEX^(U?oG70Dp)=y6f?GX(#1E={>1@5ob=G7qF#U9qU#_Hi}`aU~qqCpu&G z4XHkdcw+rt*Ax%@y*?TX_2s8JQ6|L?bJim^Rv02aq$#0x#}W73gykq~vzc<3 zOx-|tuQj^_i-eeAydq)DEfPk{5(%9N%Ke3o+}6R+VGeTBkjdwV@IJuI$5*>`lg);{ zbb3>A3r~hSIBm$MQ@M0uE|TE%P@I>Ub|W844R3vV4w4tw!m}D68o>5Py-ix|`BsWt z7H+!~9V^W69E8~9`Pg?y=0J9>Hnu00D_xxGxz;#^@hg$Q+7UxvPi8GlY!dCR*h%MW94?&t~oIZOz?aeW)M8Axw13$de;VEG7}9Cq_qnjb+7` z9qR6b;z*k-qkC!eB9ZK(pRAPZ<#^)qNz zm^Zg?yA}op*vL8NiHtmZA*mh zds@qR&1+*1G%C44z22oERUL+ZXoyRkn;GcA0m?Xzc;0Rh0GV9ClNbR(+A@A6%lQ`)$0= zg{D&0s%H#U?fc*V!;ilA|F&;KZh)d3`y0@?s(a$_F4gfc{D_+IzH~m{syDS8O*rD0 zY%-H;Rh6V0Hu8~J3Tj_vF3PUNk=1G%FN&F}L#9-YkO{I~^-QCLmv$_O>Vc)Yi&t~1 zT12(C1I1_+vzhEnpmA1H2g+=2Rr6X0*g^;Qa28MY^qG!Vht(Aa8xxH*SJC^f;V(+o zn~=&<%9cwj%zd^pP@?;7W$JylGWt=A<4u^Usrx|GMGJ|<#*S5Hz)0j}*Rl@rC5`t> z{Ym?8tty$-sPza>@g+fwJ(}Bka;tauwi&O@y}7}DZMKAi@M`PCT{YA5E9^p!>F^Ko z-lByqSeeZr?q~+_fR}IZo4+pN!OZ|S;^=13mMOapY*$9hXA|~s+pjz~hn1!!4prI9 z@fh2zeo*+E|XJH{%`~GoMYSv;=%S4FK)sLbaB4OqVt6k<)7-?DB4* zjoztZ$KZa8+%dDma9`iO1(%JOy@R;fZ<9K>w9Y+2S8J&^r1PmHOiY!%oVI08WR!r> zA@-ov-sIz|#j927BD1@D{rZq1FP=i<8U%U7v@1q3K8Yj1`X|URI5;imiM07oOfPHY ze4U+G|9oZ5b+*h-Zr)N;PmI8FCN+rjqYz&s5Rxq0>)wGxqF$@SJPI+Zlg0djW46d; zP8-~PC4Xw8)l^(}O=%^fFLgvPJPF@*vh!V6u?Qy2 zbZ!7wH|g3^r+o)=t;lPmyiceNdog6(~??TQXQS8!{?X&%-Oxs6Lk`9byOI7P8BLDfPdjaJ!?F zZA_PKpSsmP*{3h4Sb;Ys)g-(jJzy4(A$di_Dr(H~riCro22aUV#BY7EY|R0jVudzt z2P^SL^$rLQ-(>nB49Ie#>Px+80OE_OmT#Cs%v=(0nIrJO)XxJ|J(~@fnG1uxiH;teqHS-N_ZPhV$l6KvEA6#5 zp$@k*Vbrd3EMV#a7BKkDzksO=UcfYmEnuS9{Kc-emV4FWE79E%_Kr#wam@|<9fH?} z^yVzK#ZaLUlcPLJ7$={K^K|f(P(|h4tY&1*Z%79$b1!3CH0k2C>}n^jqs_Os#pP8M zu?$ry71cWm$Gp56LRWSNPebt6OqE-mg z=&u~sZ&kqeGzypMVs=_ZPj#^ys$fc=spqiZ=hPCgBXe(Nz`N>1Ub6(Jgj2Mn2zo6{ z|K^u){AL^42BR@V!tqHnu=wTZ)vcz7(j0D!2}>87ewNC4{YkW5%=hG88r(X?nb+r0 zpQrXa$~{#La+W>Jxmlg+)z`2j!mn0wq{z#!ilJm(E_2Xd6AGnr;P6*S_CZVHo3s;dZ!SIoq;a!3`2x(rKhtnn|cu|R5bs!lD3hXQ}oQd|0HTcd7s=>jfrfjKp{$NI5mOXD(^nYUy$-uc&(INz>*_TE}mY_Xvn%w9|i z%L91gMH#wW=8*&IQinEMR`XcWZR05`8dKwedIQ$X;yy^c6-4X#>R~XG$DJL}ZsmH? z)}~k0FxQaG!-5qBh-X=IndE?_+Nu@?)s&&l@te8m!vc!aXq7ZTv4_c|IN(P{+acr2)@j7jv?fuTO@uCQw92hCth`~@_mWgfv>qVoncq-X9-Q(o`X&7{!x+*z$fSAMIa`?4o6Kf&@+@IFZs78g#VUtjH3k0F@op|XQ{ z+`f#5mAp$1iiK6gZ-dgC(T-cr3hp30izP_NnzQ=KS!+Gy&6j@MC6R=wWAztwlcRCa z$bSdmS{vT5Ne>OEjivyFD_ckv*$YMM@!-$kCccnfV)Ur4t?xQ@L2yV@t}ae_aBYX+ zwI_6VsID^A1`Du+37*cYi`M~trrhF{hfphh)(zUCoYn04YFo_I+l(*`&f~!fbw7}a zcbfX;70TefxWuvyUZm=V0;%3KG1!(HBeYw-vw1<5t={cPq>@W#k!l2~;svZ3r-}f0 zh!4+>U~hT==Q~>4m8PwMzp7tM^%zKNJMKzKRGv-@>2wdJHb6E}k5}Me0~CmZc>NTo z8eRl#cn3!poa>`4|? z1y)d$jy$Ze)rOu;RO|K>hf#Bx6(1$H+RP`4)dS8}`}Lu!hAM1-PWH=fjTTdLlPqeZ zGVa|^0aVr(JkJwU8j1V4nhIlSP*1|;3l)YgK2}s=1oO~(ogG|vI^B$$(2Q{@k-r&N z%J^Il5k9u@(t?Z?v*S>YxO%izqZ+1}O2#iAbLH0?vY{)W0Ts|3Q~}kS$@!@P@3HuD zrd~Z~%%UBXHA(p@x$*>K<-X%w)+4lWbc9Of0X4;!%}v>_N-I;T*>I!|9F;FU_$nr( z_hEoTZmm1vig%^0Ktm1~`QX=aHD$fJn$o_RveDEUx7p>AoV13pi{$jdD`C4bnKWS! z?twCj%6ThuQekV}W-_9$mh~-*vyp|{QLWo~c~k05(jh4YbZeFq`<31?S{N*o!qXD7o-CMlQUiY7Y!R(8mGcSUC$ zW!6wFEYz7ME)ByKCf$QWxWHP!g%)HT4MSfwj-pUwSX63xOkb&E>Ps5bsuX$B!;Iyu z#WnRK7Q%fnAHlXq=Th-f?iw0btf)jqwGz;Y=Cg9-Lht!44_C(I7Sw6y8F`1hGFeE^ zGwR4o;3C3tDRNVPax=~=;B8*&3R(CTkL9j+eba%X^-q%q?ppGO4hiADa@*D<`1ISz zwEM2p%9RUV&SVB(5LU7Q7k{fk6LUzWbFqn5t{Z}sF$~_;!A>9QGZ=^#g%0K>7A$Kf zaCwe;3s-`+E?qRx(bkWigRw?k#y#ip4^6mr zqqN=*V^zJY)I2B3@aE*1>h!x@Mw5|Ko2)>eS6IN=Ugc3T{457^4y`) zXAUL%ih-Im##wju6V_BppH1mVgbfHm_6>BRHeRYqYMGkfR5OnZ4P?&5GaI_o1G^Nr zU`$P-YVNg)N$Z*QV;yqgok(Ksdb)&Rm$%Pb!Jea~i>6}h$J4ra6J@V>Sh$oj*kqsE z<)u^!x&dVd^qCiFds1XU4bR!{pW7wVkV~uX&!ctEi^kBTHpBulU5RXsQBJ+O-r5e8 zwG!Xb18OukGrg@R=W}Y^05c|XN|H zb2sj7_DtTh+TxVM-X4J)yGf9(+=B*6QuElC?k!*~j5}nfXVlrXu=JKZX4bB&Bmz~y z{X;qY7c&QQW(wx{dM@i1eT14*^6N6-t=~RSDy2JJKoq`;$iz8T>~tpjowNINVNob{ z*a#*_6#-_xnM8qdv7?AKsyOOmvx%dwPQ?Kg&CK{vbFb7YE^ITC*q2Qwu`wG>^g9Ka zY04vp%xp?0HxzSJ7n1|*`EJ8N+nBLZanrU`lCj%SZ+j zg6_In+Q5v$0nsI7DDii0WqtMj&@34bRD&u`J@bJ2$+HyFGhfkIL+{?|kS>)k)(OSM z{d9T>c-E>Lb>Kc^`w=PF`yjIX2sRBUqtdoT-t{uc=Gn1ms+4OrumvsAbjepD%jjiU zK)VF57W09tzKSz|5zcNkSkAz@%39hvSF^}Z**k&3x*~blO)H?W__Rz&W;!y;T6B&X z4j)~GX|=YNf0d55B^J=0Qg$!FRWk}`3}v@2+jgorHqEZ&wWR3R0t95j_Bor4CyW0X zj9JYxX2mdOEeP7VwZ`ZjvK$=Sn%jy0a>g{P)?_lA9i#5SRn6+qnw8!Do$b<|5d%7u zV|0}*+PRn-c5qY5>8EU`DWWY8PbXBAU3Zqzq834BY)M%m_tWfm(6H8PHayG3!sj2= zca+8O>=IPMqC+Cq-6xNIoGm;4gitTKSvuA)9!j*a#- zjlC=r?P=pE$3lC$NJ<)LPZxMP`|Rn$pRmzgmaLB%u6Wv2G_l0OMUZAYTNlIJd=~Mz z%e3-{Sgw^vP%PUD<0yY%FMC$=$%?jlWT4qfu$|v@jY4rVr*pB7YkMM?6@zm!3;TF0 zzt)foUHlbUzL!}E^OMJUDgSb2USDaeDY)`RP}ZF5OS9tiX02UvvZD9MW|8@_nF*KN z(a{~UEj9*~0;%HC+3{`O%jlCkg30@I+1O@sqqB8s&4xoYR9Q0%F?gRQ$I4pH(a_5u z;me+vExEotJ-`gbD=+()p355eET$|edv?j%!ukxOIdvY2+gY2sVU-kv_|Pg;3Ux3q zhlr{lAGNoH^cfP=G~VsWYb(yR#P!3 zD|=N2#j`X1jGnOLXB0KLv|~ZkY*S6dU5z-Vy`xv7&Yfzl&(+M$Y1Voxnr}i5O0lcQ zO*1=G&{oOG6yD=B^DuSA!vJaZ7_xuw8|OUK&*}{TJfJ;@P0Vz%zg0i?1C#7V6E_~m zUDm-d=xnt7-<|ABB3d)qt5Olm#2MOo&69AZPh8js5*uVa6vxog7__aW2H8GbfYGWg zb}ldXDMdNfu;cnJMHN(Y;Nn< z9BjL3G!#ojvzLFxi!;Q{XQa}p44l49ogNJup09%Nk?IEXw-C8J6|R=T}){4#z{6Yj}vGd%X)dFfm2@!bTbS}#_>@?JK zzHE7{M_Y&CohU75S@J@8*OqmlvE#qz%WR}BTTA2Rq$JiKJcx%o9nEBJZC(wQOJ_G! zh|Z-Fp{-k_U3Hu<@GMppD3q@-`?4}Q+PIF0z+-WeoMa1)EOpvmg)f(XWRa9C` zs$`UVJ_(}rS`gQQA*>IyVCcO@rSv-f`O>Bu^$Ccv=8-yE%vIyr#cm z$fNaAR+^P)y_TwQ^RM-qiyQ|?_83Ct9-rw~O|E+m>H?+eZ}#damPeXa&yu$48!z;YPG||GBsVkz)mE$lQmtcJp_Qg46GHJ0Qtze4 zFv(6>WmaTnRPL=~o>57i1gm6JT0G6DG_QH^=*7#prYk^WKED^11#IigYmo#Gz3LXI z=?7l9r49Z{sH@b5dMoN&{VUl4w{0h)1`z5 z8LpjNS@+_kuw{wsZ*V(J%tSMCYYna28%rM%#QUylEG^6WF4#t`oEs+95Ro0ir83>g z)CMz;J4o?fNMT*8HjvopzOt}fd(>^iIOf^>CR`jhsLWzAxztc!61S%g_4cN7V*G;b zwtTiPQ{ay(OC9?9W@m+2by0CiJ8sg9sSQ4=uRX=7WNtO$ZuHIAbIQQX%cKEiqV{~} zt4tbk^-MaqG3~m_%W6mgcZ-I#NA#zV))*AYjLRX_fsX1bg&p=bQ|L*Oc%=~g%(#5T zQLTM>ea_W;O9~bpYMAj@ilx+TVCvFf*KL!CBGB3l5jaeqQB}c_TIOfX7!v(M8&X3{ zTorX!1Ju3?Zig}g1u@sFiN~f?~PULcfIo-D` zhgc^r`^*fi^|{3Am*Y;g+?ECMypVczZF4G})sHtaW_!Niq3mcxKPj)@^0N~0`H2n| za6#O7v?kMf~b&qW8o)D z!a~F{1IlDF*^|sFQ^qWA)EZ0;_J#6Ohx`QvP_He8q|u!A){(3>qm|xtXC=gFa07Uc z**i3lQq90rZ`XS~G&vZ)Al-l7;mrZTrnrT&W#k74R5M2o9bwh$|JI+*J;7oz}5CT8*N%Lsk&^SqJmD$NEP7n z2hIDkD&}2DlW0N3!#-}-tGL)pGv4Ygb9>^$N-hK0?kwIo<$lG%+3D*XoxUzWhk@Vx zoxU!((>Diqdi8Ql&rm;}sP7wuaa~U$+M}iF3s*j9}ur5n4x*MA4#zagDZC9`Q5 z^7f>&gLqLPk?BynmHtG-y7cBu@0O5ysB_8V3!@m4b(>KejGJ=MRkL%$9=PQp_t+jt09;;5Lh6-tOT{?+J1q{3?kQp${<6Bpn zsY0$#{_54s;)XbpdOj8+wV;9YCi@SwKi#jiM)HGFkI7#+M1@AC;>;^A{1Z>Z_Vy*$ z<^^ubAshH}h1s1Mh;=I-@F)J#?5qIWY3QkXk+ych>)`NMK;Yt)CgNDcnzh72b8*MY z)$QtWUhKrL)e+#ww!|rnjR5Wub8T*LD2q7Pw3~}EYjKICT6Uh08Au|x?jKumQMmKB z)!80gnC<*!4SId=7GGfbxfp!;B6H!Ylg$(g$qLV(XhI@!wmTRGcN&6aEcy z8As5d-goXxHxEH0(hE~$6!Z!x<71hggp<#v%%G^*8qjl0@SLi39D4X(v; zOwIH{eRsN!5H2xieilXT7h|HX<)72R-BcGw)s^K|Y#z6mZYffwzSbEDYMv2q`;?sf zdPq1!i(+;jbW?({!vJ3Ufffec%cTsiY=;9HUiHL*_wM-FN9MdfeRi?+TlFCY;B1rS z>NFdY*(^>9IO=vz;+lgz%D#A_zu4+#!5Q`l)Ej4WP+)=E4V6`6v%M{rn0-Z}e`3UQ zKV{|Xia|Zm+Yuc-uJQs;cKW&D1_0i;*GI(zm57N&5qGaHFHmtf(=2dU(y z`-;n$go$h>m0o5jPpy|3$}??v#2@dhQdG0!dIJdT!Q*Nop5HQH&CfnnVabs78AO}b z^QTFzrT@l}(|nc}G`(FHGsN&J(7*LCYJ6t0Swuse&%DQD(RI#2M5~ z>BYNxkmS^_&iz{ZSNGb$`ngyo_AmCsZ8lLerYz%h8c88p{P1Ac|@vR9gKKxp2rMgts z4Mf$2oLnv2y{MeobX6r`Ij64L7%>N6ZDgffR(G((jLHI+PPufankMCG0*9nrUe^pu zy3Ei_yQfbqX$Cf_aSi4*`nkqj0m?nqCz95KG0+=X_5ij^0V+`_*u-CVdTmDS;}=rU zD9TUjAh_!XrTz2AlO4*X&5fB{0q>n!LG=WnfF5wvA8R*nHnZtmeh`X2JUeG@jHqAp z1!dcpTDJk}jEoLl{AKM>G6y|dDxK+ry0a&iDe55&GZX7^!oFlT^pv6 +#include "jwt/jwt.hpp" + +void basic_payload_test() +{ + jwt::jwt_payload jp; + jp.add_claim("iss", "myself"); + jp.add_claim("exp", 1234567); + jp.add_claim("Exp", 1234567, true); + + auto jstr = jwt::to_json_str(jp); + std::cout << jstr << std::endl; + + auto enc = jp.base64_encode(); + std::cout << "Base64 enc: " << enc << std::endl; + + auto dec = jp.base64_decode(enc); + std::cout << "Base64 dec: " << dec << std::endl; + std::cout << "Base64 dec: " << jstr << std::endl; + + assert (jstr == dec && "Encoded and decoded messages do not match"); + assert (jp.has_claim("exp") && "Claim exp must exist"); + assert (jp.has_claim("Exp") && "Claim Exp must exist"); + + assert (!jp.has_claim("aud") && "Claim aud does not exist"); + assert (jp.has_claim_with_value("exp", 1234567) && "Claim exp with value 1234567 does not exist"); + + return; +} + +int main() { + basic_payload_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_jwt_signature b/externals/cpp-jwt/include/jwt/test/test_jwt_signature new file mode 100755 index 0000000000000000000000000000000000000000..d3896331783b1f4bf11d8b3b86bd8908e7ac801b GIT binary patch literal 740348 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

k&ixD=+MI9K# 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

=4Sw!^!?$sYGH{}{`vQ810h#f={s&O1! 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 z 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⪙a@g1QgB=(ENErxvbR~B^g_3)HR^&EyHWeRytD`U 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

multiplayer/chat_room.h
+ 1 + + + + + diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp new file mode 100755 index 000000000..9000c4531 --- /dev/null +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include "common/settings.h" +#include "network/network.h" +#include "ui_direct_connect.h" +#include "yuzu/main.h" +#include "yuzu/multiplayer/client_room.h" +#include "yuzu/multiplayer/direct_connect.h" +#include "yuzu/multiplayer/message.h" +#include "yuzu/multiplayer/state.h" +#include "yuzu/multiplayer/validation.h" +#include "yuzu/uisettings.h" + +enum class ConnectionType : u8 { TraversalServer, IP }; + +DirectConnectWindow::DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(std::make_unique()), room_network{room_network_} { + + ui->setupUi(this); + + // setup the watcher for background connections + watcher = new QFutureWatcher; + connect(watcher, &QFutureWatcher::finished, this, &DirectConnectWindow::OnConnection); + + ui->nickname->setValidator(validation.GetNickname()); + ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); + if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { + // Use yuzu Web Service user name as nickname by default + ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); + } + ui->ip->setValidator(validation.GetIP()); + ui->ip->setText(UISettings::values.multiplayer_ip.GetValue()); + ui->port->setValidator(validation.GetPort()); + ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue())); + + // TODO(jroweboy): Show or hide the connection options based on the current value of the combo + // box. Add this back in when the traversal server support is added. + connect(ui->connect, &QPushButton::clicked, this, &DirectConnectWindow::Connect); +} + +DirectConnectWindow::~DirectConnectWindow() = default; + +void DirectConnectWindow::RetranslateUi() { + ui->retranslateUi(this); +} + +void DirectConnectWindow::Connect() { + if (!ui->nickname->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); + return; + } + if (const auto member = room_network.GetRoomMember().lock()) { + // Prevent the user from trying to join a room while they are already joining. + if (member->GetState() == Network::RoomMember::State::Joining) { + return; + } else if (member->IsConnected()) { + // And ask if they want to leave the room if they are already in one. + if (!NetworkMessage::WarnDisconnect()) { + return; + } + } + } + switch (static_cast(ui->connection_type->currentIndex())) { + case ConnectionType::TraversalServer: + break; + case ConnectionType::IP: + if (!ui->ip->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError( + NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); + return; + } + if (!ui->port->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID); + return; + } + break; + } + + // Store settings + UISettings::values.multiplayer_nickname = ui->nickname->text(); + UISettings::values.multiplayer_ip = ui->ip->text(); + if (ui->port->isModified() && !ui->port->text().isEmpty()) { + UISettings::values.multiplayer_port = ui->port->text().toInt(); + } else { + UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); + } + + // attempt to connect in a different thread + QFuture f = QtConcurrent::run([&] { + if (auto room_member = room_network.GetRoomMember().lock()) { + auto port = UISettings::values.multiplayer_port.GetValue(); + room_member->Join(ui->nickname->text().toStdString(), "", + ui->ip->text().toStdString().c_str(), port, 0, + Network::NoPreferredMac, ui->password->text().toStdString().c_str()); + } + }); + watcher->setFuture(f); + // and disable widgets and display a connecting while we wait + BeginConnecting(); +} + +void DirectConnectWindow::BeginConnecting() { + ui->connect->setEnabled(false); + ui->connect->setText(tr("Connecting")); +} + +void DirectConnectWindow::EndConnecting() { + ui->connect->setEnabled(true); + ui->connect->setText(tr("Connect")); +} + +void DirectConnectWindow::OnConnection() { + EndConnecting(); + + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->GetState() == Network::RoomMember::State::Joined || + room_member->GetState() == Network::RoomMember::State::Moderator) { + + close(); + } + } +} diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h new file mode 100755 index 000000000..4e1043053 --- /dev/null +++ b/src/yuzu/multiplayer/direct_connect.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "yuzu/multiplayer/validation.h" + +namespace Ui { +class DirectConnect; +} + +class DirectConnectWindow : public QDialog { + Q_OBJECT + +public: + explicit DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent = nullptr); + ~DirectConnectWindow(); + + void RetranslateUi(); + +signals: + /** + * Signalled by this widget when it is closing itself and destroying any state such as + * connections that it might have. + */ + void Closed(); + +private slots: + void OnConnection(); + +private: + void Connect(); + void BeginConnecting(); + void EndConnecting(); + + QFutureWatcher* watcher; + std::unique_ptr ui; + Validation validation; + Network::RoomNetwork& room_network; +}; diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui new file mode 100755 index 000000000..681b6bf69 --- /dev/null +++ b/src/yuzu/multiplayer/direct_connect.ui @@ -0,0 +1,168 @@ + + + DirectConnect + + + + 0 + 0 + 455 + 161 + + + + Direct Connect + + + + + + + + + + 0 + + + 0 + + + + + + IP Address + + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + + + IP + + + + + + + <html><head/><body><p>IPv4 address of the host</p></body></html> + + + 16 + + + + + + + Port + + + + + + + <html><head/><body><p>Port number the host is listening on</p></body></html> + + + 5 + + + 24872 + + + + + + + + + + + + + + Nickname + + + + + + + 20 + + + + + + + Password + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Connect + + + + + + + + + + + + diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp new file mode 100755 index 000000000..cb9464b2b --- /dev/null +++ b/src/yuzu/multiplayer/host_room.cpp @@ -0,0 +1,246 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/announce_multiplayer_session.h" +#include "ui_host_room.h" +#include "yuzu/game_list_p.h" +#include "yuzu/main.h" +#include "yuzu/multiplayer/host_room.h" +#include "yuzu/multiplayer/message.h" +#include "yuzu/multiplayer/state.h" +#include "yuzu/multiplayer/validation.h" +#include "yuzu/uisettings.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/verify_user_jwt.h" +#endif + +HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, + std::shared_ptr session, + Network::RoomNetwork& room_network_) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(std::make_unique()), + announce_multiplayer_session(session), room_network{room_network_} { + ui->setupUi(this); + + // set up validation for all of the fields + ui->room_name->setValidator(validation.GetRoomName()); + ui->username->setValidator(validation.GetNickname()); + ui->port->setValidator(validation.GetPort()); + ui->port->setPlaceholderText(QString::number(Network::DefaultRoomPort)); + + // Create a proxy to the game list to display the list of preferred games + game_list = new QStandardItemModel; + UpdateGameList(list); + + proxy = new ComboBoxProxyModel; + proxy->setSourceModel(game_list); + proxy->sort(0, Qt::AscendingOrder); + ui->game_list->setModel(proxy); + + // Connect all the widgets to the appropriate events + connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host); + + // Restore the settings: + ui->username->setText(UISettings::values.multiplayer_room_nickname.GetValue()); + if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { + // Use yuzu Web Service user name as nickname by default + ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); + } + ui->room_name->setText(UISettings::values.multiplayer_room_name.GetValue()); + ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue())); + ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue()); + int index = UISettings::values.multiplayer_host_type.GetValue(); + if (index < ui->host_type->count()) { + ui->host_type->setCurrentIndex(index); + } + index = ui->game_list->findData(UISettings::values.multiplayer_game_id.GetValue(), + GameListItemPath::ProgramIdRole); + if (index != -1) { + ui->game_list->setCurrentIndex(index); + } + ui->room_description->setText(UISettings::values.multiplayer_room_description.GetValue()); +} + +HostRoomWindow::~HostRoomWindow() = default; + +void HostRoomWindow::UpdateGameList(QStandardItemModel* list) { + game_list->clear(); + for (int i = 0; i < list->rowCount(); i++) { + auto parent = list->item(i, 0); + for (int j = 0; j < parent->rowCount(); j++) { + game_list->appendRow(parent->child(j)->clone()); + } + } +} + +void HostRoomWindow::RetranslateUi() { + ui->retranslateUi(this); +} + +std::unique_ptr HostRoomWindow::CreateVerifyBackend( + bool use_validation) const { + std::unique_ptr verify_backend; + if (use_validation) { +#ifdef ENABLE_WEB_SERVICE + verify_backend = + std::make_unique(Settings::values.web_api_url.GetValue()); +#else + verify_backend = std::make_unique(); +#endif + } else { + verify_backend = std::make_unique(); + } + return verify_backend; +} + +void HostRoomWindow::Host() { + if (!ui->username->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); + return; + } + if (!ui->room_name->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOMNAME_NOT_VALID); + return; + } + if (!ui->port->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID); + return; + } + if (ui->game_list->currentIndex() == -1) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED); + return; + } + if (auto member = room_network.GetRoomMember().lock()) { + if (member->GetState() == Network::RoomMember::State::Joining) { + return; + } else if (member->IsConnected()) { + auto parent = static_cast(parentWidget()); + if (!parent->OnCloseRoom()) { + close(); + return; + } + } + ui->host->setDisabled(true); + + const AnnounceMultiplayerRoom::GameInfo game{ + .name = ui->game_list->currentData(Qt::DisplayRole).toString().toStdString(), + .id = ui->game_list->currentData(GameListItemPath::ProgramIdRole).toULongLong(), + }; + const auto port = + ui->port->isModified() ? ui->port->text().toInt() : Network::DefaultRoomPort; + const auto password = ui->password->text().toStdString(); + const bool is_public = ui->host_type->currentIndex() == 0; + Network::Room::BanList ban_list{}; + if (ui->load_ban_list->isChecked()) { + ban_list = UISettings::values.multiplayer_ban_list; + } + if (auto room = room_network.GetRoom().lock()) { + const bool created = + room->Create(ui->room_name->text().toStdString(), + ui->room_description->toPlainText().toStdString(), "", port, password, + ui->max_player->value(), Settings::values.yuzu_username.GetValue(), + game, CreateVerifyBackend(is_public), ban_list); + if (!created) { + NetworkMessage::ErrorManager::ShowError( + NetworkMessage::ErrorManager::COULD_NOT_CREATE_ROOM); + LOG_ERROR(Network, "Could not create room!"); + ui->host->setEnabled(true); + return; + } + } + // Start the announce session if they chose Public + if (is_public) { + if (auto session = announce_multiplayer_session.lock()) { + // Register the room first to ensure verify_uid is present when we connect + WebService::WebResult result = session->Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + QMessageBox::warning( + this, tr("Error"), + tr("Failed to announce the room to the public lobby. In order to host a " + "room publicly, you must have a valid yuzu account configured in " + "Emulation -> Configure -> Web. If you do not want to publish a room in " + "the public lobby, then select Unlisted instead.\nDebug Message: ") + + QString::fromStdString(result.result_string), + QMessageBox::Ok); + ui->host->setEnabled(true); + if (auto room = room_network.GetRoom().lock()) { + room->Destroy(); + } + return; + } + session->Start(); + } else { + LOG_ERROR(Network, "Starting announce session failed"); + } + } + std::string token; +#ifdef ENABLE_WEB_SERVICE + if (is_public) { + WebService::Client client(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); + if (auto room = room_network.GetRoom().lock()) { + token = client.GetExternalJWT(room->GetVerifyUID()).returned_data; + } + if (token.empty()) { + LOG_ERROR(WebService, "Could not get external JWT, verification may fail"); + } else { + LOG_INFO(WebService, "Successfully requested external JWT: size={}", token.size()); + } + } +#endif + // TODO: Check what to do with this + member->Join(ui->username->text().toStdString(), "", "127.0.0.1", port, 0, + Network::NoPreferredMac, password, token); + + // Store settings + UISettings::values.multiplayer_room_nickname = ui->username->text(); + UISettings::values.multiplayer_room_name = ui->room_name->text(); + UISettings::values.multiplayer_game_id = + ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong(); + UISettings::values.multiplayer_max_player = ui->max_player->value(); + + UISettings::values.multiplayer_host_type = ui->host_type->currentIndex(); + if (ui->port->isModified() && !ui->port->text().isEmpty()) { + UISettings::values.multiplayer_room_port = ui->port->text().toInt(); + } else { + UISettings::values.multiplayer_room_port = Network::DefaultRoomPort; + } + UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); + ui->host->setEnabled(true); + close(); + } +} + +QVariant ComboBoxProxyModel::data(const QModelIndex& idx, int role) const { + if (role != Qt::DisplayRole) { + auto val = QSortFilterProxyModel::data(idx, role); + // If its the icon, shrink it to 16x16 + if (role == Qt::DecorationRole) + val = val.value().scaled(16, 16, Qt::KeepAspectRatio); + return val; + } + std::string filename; + Common::SplitPath( + QSortFilterProxyModel::data(idx, GameListItemPath::FullPathRole).toString().toStdString(), + nullptr, &filename, nullptr); + QString title = QSortFilterProxyModel::data(idx, GameListItemPath::TitleRole).toString(); + return title.isEmpty() ? QString::fromStdString(filename) : title; +} + +bool ComboBoxProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { + auto leftData = left.data(GameListItemPath::TitleRole).toString(); + auto rightData = right.data(GameListItemPath::TitleRole).toString(); + return leftData.compare(rightData) < 0; +} diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h new file mode 100755 index 000000000..a968042d0 --- /dev/null +++ b/src/yuzu/multiplayer/host_room.h @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "network/network.h" +#include "yuzu/multiplayer/chat_room.h" +#include "yuzu/multiplayer/validation.h" + +namespace Ui { +class HostRoom; +} + +namespace Core { +class AnnounceMultiplayerSession; +} + +class ConnectionError; +class ComboBoxProxyModel; + +class ChatMessage; + +namespace Network::VerifyUser { +class Backend; +}; + +class HostRoomWindow : public QDialog { + Q_OBJECT + +public: + explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, + std::shared_ptr session, + Network::RoomNetwork& room_network_); + ~HostRoomWindow(); + + /** + * Updates the dialog with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* list); + void RetranslateUi(); + +private: + void Host(); + std::unique_ptr CreateVerifyBackend(bool use_validation) const; + + std::unique_ptr ui; + std::weak_ptr announce_multiplayer_session; + QStandardItemModel* game_list; + ComboBoxProxyModel* proxy; + Validation validation; + Network::RoomNetwork& room_network; +}; + +/** + * Proxy Model for the game list combo box so we can reuse the game list model while still + * displaying the fields slightly differently + */ +class ComboBoxProxyModel : public QSortFilterProxyModel { + Q_OBJECT + +public: + int columnCount(const QModelIndex& idx) const override { + return 1; + } + + QVariant data(const QModelIndex& idx, int role) const override; + + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; +}; diff --git a/src/yuzu/multiplayer/host_room.ui b/src/yuzu/multiplayer/host_room.ui new file mode 100755 index 000000000..d54cf49c6 --- /dev/null +++ b/src/yuzu/multiplayer/host_room.ui @@ -0,0 +1,207 @@ + + + HostRoom + + + + 0 + 0 + 607 + 211 + + + + Create Room + + + + + + + 0 + + + 0 + + + 0 + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Room Name + + + + + + + 50 + + + + + + + Preferred Game + + + + + + + + + + Max Players + + + + + + + 2 + + + 16 + + + 8 + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Username + + + + + + + QLineEdit::PasswordEchoOnEdit + + + (Leave blank for open game) + + + + + + + Qt::ImhDigitsOnly + + + 5 + + + + + + + Password + + + + + + + Port + + + + + + + + + + + + + + Room Description + + + + + + + + + + + + + + Load Previous Ban List + + + true + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Public + + + + + Unlisted + + + + + + + + Host Room + + + + + + + + + + diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp new file mode 100755 index 000000000..23c2f21ab --- /dev/null +++ b/src/yuzu/multiplayer/lobby.cpp @@ -0,0 +1,367 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "common/logging/log.h" +#include "common/settings.h" +#include "network/network.h" +#include "ui_lobby.h" +#include "yuzu/game_list_p.h" +#include "yuzu/main.h" +#include "yuzu/multiplayer/client_room.h" +#include "yuzu/multiplayer/lobby.h" +#include "yuzu/multiplayer/lobby_p.h" +#include "yuzu/multiplayer/message.h" +#include "yuzu/multiplayer/state.h" +#include "yuzu/multiplayer/validation.h" +#include "yuzu/uisettings.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/web_backend.h" +#endif + +Lobby::Lobby(QWidget* parent, QStandardItemModel* list, + std::shared_ptr session, + Network::RoomNetwork& room_network_) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(std::make_unique()), + announce_multiplayer_session(session), room_network{room_network_} { + ui->setupUi(this); + + // setup the watcher for background connections + watcher = new QFutureWatcher; + + model = new QStandardItemModel(ui->room_list); + + // Create a proxy to the game list to get the list of games owned + game_list = new QStandardItemModel; + UpdateGameList(list); + + proxy = new LobbyFilterProxyModel(this, game_list); + proxy->setSourceModel(model); + proxy->setDynamicSortFilter(true); + proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); + proxy->setSortLocaleAware(true); + ui->room_list->setModel(proxy); + ui->room_list->header()->setSectionResizeMode(QHeaderView::Interactive); + ui->room_list->header()->stretchLastSection(); + ui->room_list->setAlternatingRowColors(true); + ui->room_list->setSelectionMode(QHeaderView::SingleSelection); + ui->room_list->setSelectionBehavior(QHeaderView::SelectRows); + ui->room_list->setVerticalScrollMode(QHeaderView::ScrollPerPixel); + ui->room_list->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); + ui->room_list->setSortingEnabled(true); + ui->room_list->setEditTriggers(QHeaderView::NoEditTriggers); + ui->room_list->setExpandsOnDoubleClick(false); + ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu); + + ui->nickname->setValidator(validation.GetNickname()); + ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); + if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { + // Use yuzu Web Service user name as nickname by default + ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); + } + + // UI Buttons + connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); + connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); + connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); + connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); + connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); + connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); + + // Actions + connect(&room_list_watcher, &QFutureWatcher::finished, this, + &Lobby::OnRefreshLobby); + + // manually start a refresh when the window is opening + // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as + // part of the constructor, but offload the refresh until after the window shown. perhaps emit a + // refreshroomlist signal from places that open the lobby + RefreshLobby(); +} + +Lobby::~Lobby() = default; + +void Lobby::UpdateGameList(QStandardItemModel* list) { + game_list->clear(); + for (int i = 0; i < list->rowCount(); i++) { + auto parent = list->item(i, 0); + for (int j = 0; j < parent->rowCount(); j++) { + game_list->appendRow(parent->child(j)->clone()); + } + } + if (proxy) + proxy->UpdateGameList(game_list); +} + +void Lobby::RetranslateUi() { + ui->retranslateUi(this); +} + +QString Lobby::PasswordPrompt() { + bool ok; + const QString text = + QInputDialog::getText(this, tr("Password Required to Join"), tr("Password:"), + QLineEdit::Password, QString(), &ok); + return ok ? text : QString(); +} + +void Lobby::OnExpandRoom(const QModelIndex& index) { + QModelIndex member_index = proxy->index(index.row(), Column::MEMBER); + auto member_list = proxy->data(member_index, LobbyItemMemberList::MemberListRole).toList(); +} + +void Lobby::OnJoinRoom(const QModelIndex& source) { + if (const auto member = room_network.GetRoomMember().lock()) { + // Prevent the user from trying to join a room while they are already joining. + if (member->GetState() == Network::RoomMember::State::Joining) { + return; + } else if (member->IsConnected()) { + // And ask if they want to leave the room if they are already in one. + if (!NetworkMessage::WarnDisconnect()) { + return; + } + } + } + QModelIndex index = source; + // If the user double clicks on a child row (aka the player list) then use the parent instead + if (source.parent() != QModelIndex()) { + index = source.parent(); + } + if (!ui->nickname->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); + return; + } + + // Get a password to pass if the room is password protected + QModelIndex password_index = proxy->index(index.row(), Column::ROOM_NAME); + bool has_password = proxy->data(password_index, LobbyItemName::PasswordRole).toBool(); + const std::string password = has_password ? PasswordPrompt().toStdString() : ""; + if (has_password && password.empty()) { + return; + } + + QModelIndex connection_index = proxy->index(index.row(), Column::HOST); + const std::string nickname = ui->nickname->text().toStdString(); + const std::string ip = + proxy->data(connection_index, LobbyItemHost::HostIPRole).toString().toStdString(); + int port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); + const std::string verify_uid = + proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString(); + + // attempt to connect in a different thread + QFuture f = QtConcurrent::run([nickname, ip, port, password, verify_uid, this] { + std::string token; +#ifdef ENABLE_WEB_SERVICE + if (!Settings::values.yuzu_username.GetValue().empty() && + !Settings::values.yuzu_token.GetValue().empty()) { + WebService::Client client(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); + token = client.GetExternalJWT(verify_uid).returned_data; + if (token.empty()) { + LOG_ERROR(WebService, "Could not get external JWT, verification may fail"); + } else { + LOG_INFO(WebService, "Successfully requested external JWT: size={}", token.size()); + } + } +#endif + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Join(nickname, "", ip.c_str(), port, 0, Network::NoPreferredMac, password, + token); + } + }); + watcher->setFuture(f); + + // TODO(jroweboy): disable widgets and display a connecting while we wait + + // Save settings + UISettings::values.multiplayer_nickname = ui->nickname->text(); + UISettings::values.multiplayer_ip = + proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); + UISettings::values.multiplayer_port = + proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); +} + +void Lobby::ResetModel() { + model->clear(); + model->insertColumns(0, Column::TOTAL); + model->setHeaderData(Column::EXPAND, Qt::Horizontal, QString(), Qt::DisplayRole); + model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole); + model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); + model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); + model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); +} + +void Lobby::RefreshLobby() { + if (auto session = announce_multiplayer_session.lock()) { + ResetModel(); + ui->refresh_list->setEnabled(false); + ui->refresh_list->setText(tr("Refreshing")); + room_list_watcher.setFuture( + QtConcurrent::run([session]() { return session->GetRoomList(); })); + } else { + // TODO(jroweboy): Display an error box about announce couldn't be started + } +} + +void Lobby::OnRefreshLobby() { + AnnounceMultiplayerRoom::RoomList new_room_list = room_list_watcher.result(); + for (auto room : new_room_list) { + // find the icon for the game if this person owns that game. + QPixmap smdh_icon; + for (int r = 0; r < game_list->rowCount(); ++r) { + auto index = game_list->index(r, 0); + auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); + if (game_id != 0 && room.information.preferred_game.id == game_id) { + smdh_icon = game_list->data(index, Qt::DecorationRole).value(); + } + } + + QList members; + for (auto member : room.members) { + QVariant var; + var.setValue(LobbyMember{QString::fromStdString(member.username), + QString::fromStdString(member.nickname), member.game.id, + QString::fromStdString(member.game.name)}); + members.append(var); + } + + auto first_item = new LobbyItem(); + auto row = QList({ + first_item, + new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)), + new LobbyItemGame(room.information.preferred_game.id, + QString::fromStdString(room.information.preferred_game.name), + smdh_icon), + new LobbyItemHost(QString::fromStdString(room.information.host_username), + QString::fromStdString(room.ip), room.information.port, + QString::fromStdString(room.verify_uid)), + new LobbyItemMemberList(members, room.information.member_slots), + }); + model->appendRow(row); + // To make the rows expandable, add the member data as a child of the first column of the + // rows with people in them and have qt set them to colspan after the model is finished + // resetting + if (!room.information.description.empty()) { + first_item->appendRow( + new LobbyItemDescription(QString::fromStdString(room.information.description))); + } + if (!room.members.empty()) { + first_item->appendRow(new LobbyItemExpandedMemberList(members)); + } + } + + // Reenable the refresh button and resize the columns + ui->refresh_list->setEnabled(true); + ui->refresh_list->setText(tr("Refresh List")); + ui->room_list->header()->stretchLastSection(); + for (int i = 0; i < Column::TOTAL - 1; ++i) { + ui->room_list->resizeColumnToContents(i); + } + + // Set the member list child items to span all columns + for (int i = 0; i < proxy->rowCount(); i++) { + auto parent = model->item(i, 0); + for (int j = 0; j < parent->rowCount(); j++) { + ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true); + } + } +} + +LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) + : QSortFilterProxyModel(parent), game_list(list) {} + +void LobbyFilterProxyModel::UpdateGameList(QStandardItemModel* list) { + game_list = list; +} + +bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { + // Prioritize filters by fastest to compute + + // pass over any child rows (aka row that shows the players in the room) + if (sourceParent != QModelIndex()) { + return true; + } + + // filter by filled rooms + if (filter_full) { + QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); + int player_count = + sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size(); + int max_players = + sourceModel()->data(member_list, LobbyItemMemberList::MaxPlayerRole).toInt(); + if (player_count >= max_players) { + return false; + } + } + + // filter by search parameters + if (!filter_search.isEmpty()) { + QModelIndex game_name = sourceModel()->index(sourceRow, Column::GAME_NAME, sourceParent); + QModelIndex room_name = sourceModel()->index(sourceRow, Column::ROOM_NAME, sourceParent); + QModelIndex host_name = sourceModel()->index(sourceRow, Column::HOST, sourceParent); + bool preferred_game_match = sourceModel() + ->data(game_name, LobbyItemGame::GameNameRole) + .toString() + .contains(filter_search, filterCaseSensitivity()); + bool room_name_match = sourceModel() + ->data(room_name, LobbyItemName::NameRole) + .toString() + .contains(filter_search, filterCaseSensitivity()); + bool username_match = sourceModel() + ->data(host_name, LobbyItemHost::HostUsernameRole) + .toString() + .contains(filter_search, filterCaseSensitivity()); + if (!preferred_game_match && !room_name_match && !username_match) { + return false; + } + } + + // filter by game owned + if (filter_owned) { + QModelIndex game_name = sourceModel()->index(sourceRow, Column::GAME_NAME, sourceParent); + QList owned_games; + for (int r = 0; r < game_list->rowCount(); ++r) { + owned_games.append(QModelIndex(game_list->index(r, 0))); + } + auto current_id = sourceModel()->data(game_name, LobbyItemGame::TitleIDRole).toLongLong(); + if (current_id == 0) { + // TODO(jroweboy): homebrew often doesn't have a game id and this hides them + return false; + } + bool owned = false; + for (const auto& game : owned_games) { + auto game_id = game_list->data(game, GameListItemPath::ProgramIdRole).toLongLong(); + if (current_id == game_id) { + owned = true; + } + } + if (!owned) { + return false; + } + } + + return true; +} + +void LobbyFilterProxyModel::sort(int column, Qt::SortOrder order) { + sourceModel()->sort(column, order); +} + +void LobbyFilterProxyModel::SetFilterOwned(bool filter) { + filter_owned = filter; + invalidate(); +} + +void LobbyFilterProxyModel::SetFilterFull(bool filter) { + filter_full = filter; + invalidate(); +} + +void LobbyFilterProxyModel::SetFilterSearch(const QString& filter) { + filter_search = filter; + invalidate(); +} diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h new file mode 100755 index 000000000..82744ca94 --- /dev/null +++ b/src/yuzu/multiplayer/lobby.h @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "core/announce_multiplayer_session.h" +#include "network/network.h" +#include "yuzu/multiplayer/validation.h" + +namespace Ui { +class Lobby; +} + +class LobbyModel; +class LobbyFilterProxyModel; + +/** + * Listing of all public games pulled from services. The lobby should be simple enough for users to + * find the game they want to play, and join it. + */ +class Lobby : public QDialog { + Q_OBJECT + +public: + explicit Lobby(QWidget* parent, QStandardItemModel* list, + std::shared_ptr session, + Network::RoomNetwork& room_network_); + ~Lobby() override; + + /** + * Updates the lobby with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* list); + void RetranslateUi(); + +public slots: + /** + * Begin the process to pull the latest room list from web services. After the listing is + * returned from web services, `LobbyRefreshed` will be signalled + */ + void RefreshLobby(); + +private slots: + /** + * Pulls the list of rooms from network and fills out the lobby model with the results + */ + void OnRefreshLobby(); + + /** + * Handler for single clicking on a room in the list. Expands the treeitem to show player + * information for the people in the room + * + * index - The row of the proxy model that the user wants to join. + */ + void OnExpandRoom(const QModelIndex&); + + /** + * Handler for double clicking on a room in the list. Gathers the host ip and port and attempts + * to connect. Will also prompt for a password in case one is required. + * + * index - The row of the proxy model that the user wants to join. + */ + void OnJoinRoom(const QModelIndex&); + +signals: + void StateChanged(const Network::RoomMember::State&); + +private: + /** + * Removes all entries in the Lobby before refreshing. + */ + void ResetModel(); + + /** + * Prompts for a password. Returns an empty QString if the user either did not provide a + * password or if the user closed the window. + */ + QString PasswordPrompt(); + + std::unique_ptr ui; + + QStandardItemModel* model{}; + QStandardItemModel* game_list{}; + LobbyFilterProxyModel* proxy{}; + + QFutureWatcher room_list_watcher; + std::weak_ptr announce_multiplayer_session; + QFutureWatcher* watcher; + Validation validation; + Network::RoomNetwork& room_network; +}; + +/** + * Proxy Model for filtering the lobby + */ +class LobbyFilterProxyModel : public QSortFilterProxyModel { + Q_OBJECT; + +public: + explicit LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list); + + /** + * Updates the filter with a new game list model. + * This model should be the processed one created by the Lobby. + */ + void UpdateGameList(QStandardItemModel* list); + + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + void sort(int column, Qt::SortOrder order) override; + +public slots: + void SetFilterOwned(bool); + void SetFilterFull(bool); + void SetFilterSearch(const QString&); + +private: + QStandardItemModel* game_list; + bool filter_owned = false; + bool filter_full = false; + QString filter_search; +}; diff --git a/src/yuzu/multiplayer/lobby.ui b/src/yuzu/multiplayer/lobby.ui new file mode 100755 index 000000000..4c9901c9a --- /dev/null +++ b/src/yuzu/multiplayer/lobby.ui @@ -0,0 +1,123 @@ + + + Lobby + + + + 0 + 0 + 903 + 487 + + + + Public Room Browser + + + + + + 3 + + + + + 6 + + + + + + + Nickname + + + + + + + Nickname + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filters + + + + + + + Search + + + true + + + + + + + Games I Own + + + + + + + Hide Full Rooms + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh Lobby + + + + + + + + + + + + + + + + + + + + diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h new file mode 100755 index 000000000..8071cede4 --- /dev/null +++ b/src/yuzu/multiplayer/lobby_p.h @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/common_types.h" + +namespace Column { +enum List { + EXPAND, + ROOM_NAME, + GAME_NAME, + HOST, + MEMBER, + TOTAL, +}; +} + +class LobbyItem : public QStandardItem { +public: + LobbyItem() = default; + explicit LobbyItem(const QString& string) : QStandardItem(string) {} + virtual ~LobbyItem() override = default; +}; + +class LobbyItemName : public LobbyItem { +public: + static const int NameRole = Qt::UserRole + 1; + static const int PasswordRole = Qt::UserRole + 2; + + LobbyItemName() = default; + explicit LobbyItemName(bool has_password, QString name) : LobbyItem() { + setData(name, NameRole); + setData(has_password, PasswordRole); + } + + QVariant data(int role) const override { + if (role == Qt::DecorationRole) { + bool has_password = data(PasswordRole).toBool(); + return has_password ? QIcon::fromTheme(QStringLiteral("lock")).pixmap(16) : QIcon(); + } + if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + return data(NameRole).toString(); + } + + bool operator<(const QStandardItem& other) const override { + return data(NameRole).toString().localeAwareCompare(other.data(NameRole).toString()) < 0; + } +}; + +class LobbyItemDescription : public LobbyItem { +public: + static const int DescriptionRole = Qt::UserRole + 1; + + LobbyItemDescription() = default; + explicit LobbyItemDescription(QString description) { + setData(description, DescriptionRole); + } + + QVariant data(int role) const override { + if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + auto description = data(DescriptionRole).toString(); + description.prepend(QStringLiteral("Description: ")); + return description; + } + + bool operator<(const QStandardItem& other) const override { + return data(DescriptionRole) + .toString() + .localeAwareCompare(other.data(DescriptionRole).toString()) < 0; + } +}; + +class LobbyItemGame : public LobbyItem { +public: + static const int TitleIDRole = Qt::UserRole + 1; + static const int GameNameRole = Qt::UserRole + 2; + static const int GameIconRole = Qt::UserRole + 3; + + LobbyItemGame() = default; + explicit LobbyItemGame(u64 title_id, QString game_name, QPixmap smdh_icon) { + setData(static_cast(title_id), TitleIDRole); + setData(game_name, GameNameRole); + if (!smdh_icon.isNull()) { + setData(smdh_icon, GameIconRole); + } + } + + QVariant data(int role) const override { + if (role == Qt::DecorationRole) { + auto val = data(GameIconRole); + if (val.isValid()) { + val = val.value().scaled(16, 16, Qt::KeepAspectRatio); + } + return val; + } else if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + return data(GameNameRole).toString(); + } + + bool operator<(const QStandardItem& other) const override { + return data(GameNameRole) + .toString() + .localeAwareCompare(other.data(GameNameRole).toString()) < 0; + } +}; + +class LobbyItemHost : public LobbyItem { +public: + static const int HostUsernameRole = Qt::UserRole + 1; + static const int HostIPRole = Qt::UserRole + 2; + static const int HostPortRole = Qt::UserRole + 3; + static const int HostVerifyUIDRole = Qt::UserRole + 4; + + LobbyItemHost() = default; + explicit LobbyItemHost(QString username, QString ip, u16 port, QString verify_uid) { + setData(username, HostUsernameRole); + setData(ip, HostIPRole); + setData(port, HostPortRole); + setData(verify_uid, HostVerifyUIDRole); + } + + QVariant data(int role) const override { + if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + return data(HostUsernameRole).toString(); + } + + bool operator<(const QStandardItem& other) const override { + return data(HostUsernameRole) + .toString() + .localeAwareCompare(other.data(HostUsernameRole).toString()) < 0; + } +}; + +class LobbyMember { +public: + LobbyMember() = default; + LobbyMember(const LobbyMember& other) = default; + explicit LobbyMember(QString username_, QString nickname_, u64 title_id_, QString game_name_) + : username(std::move(username_)), nickname(std::move(nickname_)), title_id(title_id_), + game_name(std::move(game_name_)) {} + ~LobbyMember() = default; + + QString GetName() const { + if (username.isEmpty() || username == nickname) { + return nickname; + } else { + return QStringLiteral("%1 (%2)").arg(nickname, username); + } + } + u64 GetTitleId() const { + return title_id; + } + QString GetGameName() const { + return game_name; + } + +private: + QString username; + QString nickname; + u64 title_id; + QString game_name; +}; + +Q_DECLARE_METATYPE(LobbyMember); + +class LobbyItemMemberList : public LobbyItem { +public: + static const int MemberListRole = Qt::UserRole + 1; + static const int MaxPlayerRole = Qt::UserRole + 2; + + LobbyItemMemberList() = default; + explicit LobbyItemMemberList(QList members, u32 max_players) { + setData(members, MemberListRole); + setData(max_players, MaxPlayerRole); + } + + QVariant data(int role) const override { + if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + auto members = data(MemberListRole).toList(); + return QStringLiteral("%1 / %2").arg(QString::number(members.size()), + data(MaxPlayerRole).toString()); + } + + bool operator<(const QStandardItem& other) const override { + // sort by rooms that have the most players + int left_members = data(MemberListRole).toList().size(); + int right_members = other.data(MemberListRole).toList().size(); + return left_members < right_members; + } +}; + +/** + * Member information for when a lobby is expanded in the UI + */ +class LobbyItemExpandedMemberList : public LobbyItem { +public: + static const int MemberListRole = Qt::UserRole + 1; + + LobbyItemExpandedMemberList() = default; + explicit LobbyItemExpandedMemberList(QList members) { + setData(members, MemberListRole); + } + + QVariant data(int role) const override { + if (role != Qt::DisplayRole) { + return LobbyItem::data(role); + } + auto members = data(MemberListRole).toList(); + QString out; + bool first = true; + for (const auto& member : members) { + if (!first) + out.append(QStringLiteral("\n")); + const auto& m = member.value(); + if (m.GetGameName().isEmpty()) { + out += QString(QObject::tr("%1 is not playing a game")).arg(m.GetName()); + } else { + out += QString(QObject::tr("%1 is playing %2")).arg(m.GetName(), m.GetGameName()); + } + first = false; + } + return out; + } +}; diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp new file mode 100755 index 000000000..76ec276ad --- /dev/null +++ b/src/yuzu/multiplayer/message.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "yuzu/multiplayer/message.h" + +namespace NetworkMessage { +const ConnectionError ErrorManager::USERNAME_NOT_VALID( + QT_TR_NOOP("Username is not valid. Must be 4 to 20 alphanumeric characters.")); +const ConnectionError ErrorManager::ROOMNAME_NOT_VALID( + QT_TR_NOOP("Room name is not valid. Must be 4 to 20 alphanumeric characters.")); +const ConnectionError ErrorManager::USERNAME_NOT_VALID_SERVER( + QT_TR_NOOP("Username is already in use or not valid. Please choose another.")); +const ConnectionError ErrorManager::IP_ADDRESS_NOT_VALID( + QT_TR_NOOP("IP is not a valid IPv4 address.")); +const ConnectionError ErrorManager::PORT_NOT_VALID( + QT_TR_NOOP("Port must be a number between 0 to 65535.")); +const ConnectionError ErrorManager::GAME_NOT_SELECTED(QT_TR_NOOP( + "You must choose a Preferred Game to host a room. If you do not have any games in your game " + "list yet, add a game folder by clicking on the plus icon in the game list.")); +const ConnectionError ErrorManager::NO_INTERNET( + QT_TR_NOOP("Unable to find an internet connection. Check your internet settings.")); +const ConnectionError ErrorManager::UNABLE_TO_CONNECT( + QT_TR_NOOP("Unable to connect to the host. Verify that the connection settings are correct. If " + "you still cannot connect, contact the room host and verify that the host is " + "properly configured with the external port forwarded.")); +const ConnectionError ErrorManager::ROOM_IS_FULL( + QT_TR_NOOP("Unable to connect to the room because it is already full.")); +const ConnectionError ErrorManager::COULD_NOT_CREATE_ROOM( + QT_TR_NOOP("Creating a room failed. Please retry. Restarting yuzu might be necessary.")); +const ConnectionError ErrorManager::HOST_BANNED( + QT_TR_NOOP("The host of the room has banned you. Speak with the host to unban you " + "or try a different room.")); +const ConnectionError ErrorManager::WRONG_VERSION( + QT_TR_NOOP("Version mismatch! Please update to the latest version of yuzu. If the problem " + "persists, contact the room host and ask them to update the server.")); +const ConnectionError ErrorManager::WRONG_PASSWORD(QT_TR_NOOP("Incorrect password.")); +const ConnectionError ErrorManager::GENERIC_ERROR(QT_TR_NOOP( + "An unknown error occurred. If this error continues to occur, please open an issue")); +const ConnectionError ErrorManager::LOST_CONNECTION( + QT_TR_NOOP("Connection to room lost. Try to reconnect.")); +const ConnectionError ErrorManager::HOST_KICKED( + QT_TR_NOOP("You have been kicked by the room host.")); +const ConnectionError ErrorManager::MAC_COLLISION( + QT_TR_NOOP("MAC address is already in use. Please choose another.")); +const ConnectionError ErrorManager::CONSOLE_ID_COLLISION(QT_TR_NOOP( + "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation " + "> Configure > System to regenerate your Console ID.")); +const ConnectionError ErrorManager::PERMISSION_DENIED( + QT_TR_NOOP("You do not have enough permission to perform this action.")); +const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( + "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); + +static bool WarnMessage(const std::string& title, const std::string& text) { + return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), + QObject::tr(text.c_str()), + QMessageBox::Ok | QMessageBox::Cancel); +} + +void ErrorManager::ShowError(const ConnectionError& e) { + QMessageBox::critical(nullptr, tr("Error"), tr(e.GetString().c_str())); +} + +bool WarnCloseRoom() { + return WarnMessage( + QT_TR_NOOP("Leave Room"), + QT_TR_NOOP("You are about to close the room. Any network connections will be closed.")); +} + +bool WarnDisconnect() { + return WarnMessage( + QT_TR_NOOP("Disconnect"), + QT_TR_NOOP("You are about to leave the room. Any network connections will be closed.")); +} + +} // namespace NetworkMessage diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h new file mode 100755 index 000000000..eb5c8d1be --- /dev/null +++ b/src/yuzu/multiplayer/message.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace NetworkMessage { + +class ConnectionError { + +public: + explicit ConnectionError(std::string str) : err(std::move(str)) {} + const std::string& GetString() const { + return err; + } + +private: + std::string err; +}; + +class ErrorManager : QObject { + Q_OBJECT +public: + /// When the nickname is considered invalid by the client + static const ConnectionError USERNAME_NOT_VALID; + static const ConnectionError ROOMNAME_NOT_VALID; + /// When the nickname is considered invalid by the room server + static const ConnectionError USERNAME_NOT_VALID_SERVER; + static const ConnectionError IP_ADDRESS_NOT_VALID; + static const ConnectionError PORT_NOT_VALID; + static const ConnectionError GAME_NOT_SELECTED; + static const ConnectionError NO_INTERNET; + static const ConnectionError UNABLE_TO_CONNECT; + static const ConnectionError ROOM_IS_FULL; + static const ConnectionError COULD_NOT_CREATE_ROOM; + static const ConnectionError HOST_BANNED; + static const ConnectionError WRONG_VERSION; + static const ConnectionError WRONG_PASSWORD; + static const ConnectionError GENERIC_ERROR; + static const ConnectionError LOST_CONNECTION; + static const ConnectionError HOST_KICKED; + static const ConnectionError MAC_COLLISION; + static const ConnectionError CONSOLE_ID_COLLISION; + static const ConnectionError PERMISSION_DENIED; + static const ConnectionError NO_SUCH_USER; + /** + * Shows a standard QMessageBox with a error message + */ + static void ShowError(const ConnectionError& e); +}; +/** + * Show a standard QMessageBox with a warning message about leaving the room + * return true if the user wants to close the network connection + */ +bool WarnCloseRoom(); + +/** + * Show a standard QMessageBox with a warning message about disconnecting from the room + * return true if the user wants to disconnect + */ +bool WarnDisconnect(); + +} // namespace NetworkMessage diff --git a/src/yuzu/multiplayer/moderation_dialog.cpp b/src/yuzu/multiplayer/moderation_dialog.cpp new file mode 100755 index 000000000..c9b8ed397 --- /dev/null +++ b/src/yuzu/multiplayer/moderation_dialog.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "network/network.h" +#include "network/room_member.h" +#include "ui_moderation_dialog.h" +#include "yuzu/multiplayer/moderation_dialog.h" + +namespace Column { +enum { + SUBJECT, + TYPE, + COUNT, +}; +} + +ModerationDialog::ModerationDialog(Network::RoomNetwork& room_network_, QWidget* parent) + : QDialog(parent), ui(std::make_unique()), room_network{room_network_} { + ui->setupUi(this); + + qRegisterMetaType(); + + if (auto member = room_network.GetRoomMember().lock()) { + callback_handle_status_message = member->BindOnStatusMessageReceived( + [this](const Network::StatusMessageEntry& status_message) { + emit StatusMessageReceived(status_message); + }); + connect(this, &ModerationDialog::StatusMessageReceived, this, + &ModerationDialog::OnStatusMessageReceived); + callback_handle_ban_list = member->BindOnBanListReceived( + [this](const Network::Room::BanList& ban_list) { emit BanListReceived(ban_list); }); + connect(this, &ModerationDialog::BanListReceived, this, &ModerationDialog::PopulateBanList); + } + + // Initialize the UI + model = new QStandardItemModel(ui->ban_list_view); + model->insertColumns(0, Column::COUNT); + model->setHeaderData(Column::SUBJECT, Qt::Horizontal, tr("Subject")); + model->setHeaderData(Column::TYPE, Qt::Horizontal, tr("Type")); + + ui->ban_list_view->setModel(model); + + // Load the ban list in background + LoadBanList(); + + connect(ui->refresh, &QPushButton::clicked, this, [this] { LoadBanList(); }); + connect(ui->unban, &QPushButton::clicked, this, [this] { + auto index = ui->ban_list_view->currentIndex(); + SendUnbanRequest(model->item(index.row(), 0)->text()); + }); + connect(ui->ban_list_view, &QTreeView::clicked, [this] { ui->unban->setEnabled(true); }); +} + +ModerationDialog::~ModerationDialog() { + if (callback_handle_status_message) { + if (auto room = room_network.GetRoomMember().lock()) { + room->Unbind(callback_handle_status_message); + } + } + + if (callback_handle_ban_list) { + if (auto room = room_network.GetRoomMember().lock()) { + room->Unbind(callback_handle_ban_list); + } + } +} + +void ModerationDialog::LoadBanList() { + if (auto room = room_network.GetRoomMember().lock()) { + ui->refresh->setEnabled(false); + ui->refresh->setText(tr("Refreshing")); + ui->unban->setEnabled(false); + room->RequestBanList(); + } +} + +void ModerationDialog::PopulateBanList(const Network::Room::BanList& ban_list) { + model->removeRows(0, model->rowCount()); + for (const auto& username : ban_list.first) { + QStandardItem* subject_item = new QStandardItem(QString::fromStdString(username)); + QStandardItem* type_item = new QStandardItem(tr("Forum Username")); + model->invisibleRootItem()->appendRow({subject_item, type_item}); + } + for (const auto& ip : ban_list.second) { + QStandardItem* subject_item = new QStandardItem(QString::fromStdString(ip)); + QStandardItem* type_item = new QStandardItem(tr("IP Address")); + model->invisibleRootItem()->appendRow({subject_item, type_item}); + } + for (int i = 0; i < Column::COUNT - 1; ++i) { + ui->ban_list_view->resizeColumnToContents(i); + } + ui->refresh->setEnabled(true); + ui->refresh->setText(tr("Refresh")); + ui->unban->setEnabled(false); +} + +void ModerationDialog::SendUnbanRequest(const QString& subject) { + if (auto room = room_network.GetRoomMember().lock()) { + room->SendModerationRequest(Network::IdModUnban, subject.toStdString()); + } +} + +void ModerationDialog::OnStatusMessageReceived(const Network::StatusMessageEntry& status_message) { + if (status_message.type != Network::IdMemberBanned && + status_message.type != Network::IdAddressUnbanned) + return; + + // Update the ban list for ban/unban + LoadBanList(); +} diff --git a/src/yuzu/multiplayer/moderation_dialog.h b/src/yuzu/multiplayer/moderation_dialog.h new file mode 100755 index 000000000..e9e5daff7 --- /dev/null +++ b/src/yuzu/multiplayer/moderation_dialog.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "network/room.h" +#include "network/room_member.h" + +namespace Ui { +class ModerationDialog; +} + +class QStandardItemModel; + +class ModerationDialog : public QDialog { + Q_OBJECT + +public: + explicit ModerationDialog(Network::RoomNetwork& room_network_, QWidget* parent = nullptr); + ~ModerationDialog(); + +signals: + void StatusMessageReceived(const Network::StatusMessageEntry&); + void BanListReceived(const Network::Room::BanList&); + +private: + void LoadBanList(); + void PopulateBanList(const Network::Room::BanList& ban_list); + void SendUnbanRequest(const QString& subject); + void OnStatusMessageReceived(const Network::StatusMessageEntry& status_message); + + std::unique_ptr ui; + QStandardItemModel* model; + Network::RoomMember::CallbackHandle callback_handle_status_message; + Network::RoomMember::CallbackHandle callback_handle_ban_list; + + Network::RoomNetwork& room_network; +}; + +Q_DECLARE_METATYPE(Network::Room::BanList); diff --git a/src/yuzu/multiplayer/moderation_dialog.ui b/src/yuzu/multiplayer/moderation_dialog.ui new file mode 100755 index 000000000..808d99414 --- /dev/null +++ b/src/yuzu/multiplayer/moderation_dialog.ui @@ -0,0 +1,84 @@ + + + ModerationDialog + + + Moderation + + + + 0 + 0 + 500 + 300 + + + + + + + Ban List + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refreshing + + + false + + + + + + + Unban + + + false + + + + + + + + + + + + + + + QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + ModerationDialog + accept() + + + + diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp new file mode 100755 index 000000000..4149b5232 --- /dev/null +++ b/src/yuzu/multiplayer/state.cpp @@ -0,0 +1,308 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "common/logging/log.h" +#include "yuzu/game_list.h" +#include "yuzu/multiplayer/client_room.h" +#include "yuzu/multiplayer/direct_connect.h" +#include "yuzu/multiplayer/host_room.h" +#include "yuzu/multiplayer/lobby.h" +#include "yuzu/multiplayer/message.h" +#include "yuzu/multiplayer/state.h" +#include "yuzu/uisettings.h" +#include "yuzu/util/clickable_label.h" + +MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, + QAction* leave_room_, QAction* show_room_, + Network::RoomNetwork& room_network_) + : QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_), + show_room(show_room_), room_network{room_network_} { + if (auto member = room_network.GetRoomMember().lock()) { + // register the network structs to use in slots and signals + state_callback_handle = member->BindOnStateChanged( + [this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); }); + connect(this, &MultiplayerState::NetworkStateChanged, this, + &MultiplayerState::OnNetworkStateChanged); + error_callback_handle = member->BindOnError( + [this](const Network::RoomMember::Error& error) { emit NetworkError(error); }); + connect(this, &MultiplayerState::NetworkError, this, &MultiplayerState::OnNetworkError); + } + + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + announce_multiplayer_session = std::make_shared(room_network); + announce_multiplayer_session->BindErrorCallback( + [this](const WebService::WebResult& result) { emit AnnounceFailed(result); }); + connect(this, &MultiplayerState::AnnounceFailed, this, &MultiplayerState::OnAnnounceFailed); + + status_text = new ClickableLabel(this); + status_icon = new ClickableLabel(this); + status_text->setToolTip(tr("Current connection status")); + status_text->setText(tr("Not Connected. Click here to find a room!")); + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); + + connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); + connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); + + connect(static_cast(QApplication::instance()), &QApplication::focusChanged, this, + [this](QWidget* /*old*/, QWidget* now) { + if (client_room && client_room->isAncestorOf(now)) { + HideNotification(); + } + }); +} + +MultiplayerState::~MultiplayerState() { + if (state_callback_handle) { + if (auto member = room_network.GetRoomMember().lock()) { + member->Unbind(state_callback_handle); + } + } + + if (error_callback_handle) { + if (auto member = room_network.GetRoomMember().lock()) { + member->Unbind(error_callback_handle); + } + } +} + +void MultiplayerState::Close() { + if (host_room) { + host_room->close(); + } + if (direct_connect) { + direct_connect->close(); + } + if (client_room) { + client_room->close(); + } + if (lobby) { + lobby->close(); + } +} + +void MultiplayerState::retranslateUi() { + status_text->setToolTip(tr("Current connection status")); + + if (current_state == Network::RoomMember::State::Uninitialized) { + status_text->setText(tr("Not Connected. Click here to find a room!")); + } else if (current_state == Network::RoomMember::State::Joined || + current_state == Network::RoomMember::State::Moderator) { + + status_text->setText(tr("Connected")); + } else { + status_text->setText(tr("Not Connected")); + } + + if (lobby) { + lobby->RetranslateUi(); + } + if (host_room) { + host_room->RetranslateUi(); + } + if (client_room) { + client_room->RetranslateUi(); + } + if (direct_connect) { + direct_connect->RetranslateUi(); + } +} + +void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { + LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); + if (state == Network::RoomMember::State::Joined || + state == Network::RoomMember::State::Moderator) { + + OnOpenNetworkRoom(); + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); + status_text->setText(tr("Connected")); + leave_room->setEnabled(true); + show_room->setEnabled(true); + } else { + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); + status_text->setText(tr("Not Connected")); + leave_room->setEnabled(false); + show_room->setEnabled(false); + } + + current_state = state; +} + +void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) { + LOG_DEBUG(Frontend, "Network Error: {}", Network::GetErrorStr(error)); + switch (error) { + case Network::RoomMember::Error::LostConnection: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::LOST_CONNECTION); + break; + case Network::RoomMember::Error::HostKicked: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_KICKED); + break; + case Network::RoomMember::Error::CouldNotConnect: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT); + break; + case Network::RoomMember::Error::NameCollision: + NetworkMessage::ErrorManager::ShowError( + NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); + break; + case Network::RoomMember::Error::MacCollision: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); + break; + case Network::RoomMember::Error::ConsoleIdCollision: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION); + break; + case Network::RoomMember::Error::RoomIsFull: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); + break; + case Network::RoomMember::Error::WrongPassword: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_PASSWORD); + break; + case Network::RoomMember::Error::WrongVersion: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_VERSION); + break; + case Network::RoomMember::Error::HostBanned: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_BANNED); + break; + case Network::RoomMember::Error::UnknownError: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT); + break; + case Network::RoomMember::Error::PermissionDenied: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PERMISSION_DENIED); + break; + case Network::RoomMember::Error::NoSuchUser: + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::NO_SUCH_USER); + break; + } +} + +void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) { + announce_multiplayer_session->Stop(); + QMessageBox::warning(this, tr("Error"), + tr("Failed to update the room information. Please check your Internet " + "connection and try hosting the room again.\nDebug Message: ") + + QString::fromStdString(result.result_string), + QMessageBox::Ok); +} + +void MultiplayerState::UpdateThemedIcons() { + if (show_notification) { + status_icon->setPixmap( + QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); + } else if (current_state == Network::RoomMember::State::Joined || + current_state == Network::RoomMember::State::Moderator) { + + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); + } else { + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); + } + if (client_room) + client_room->UpdateIconDisplay(); +} + +static void BringWidgetToFront(QWidget* widget) { + widget->show(); + widget->activateWindow(); + widget->raise(); +} + +void MultiplayerState::OnViewLobby() { + if (lobby == nullptr) { + lobby = new Lobby(this, game_list_model, announce_multiplayer_session, room_network); + } + BringWidgetToFront(lobby); +} + +void MultiplayerState::OnCreateRoom() { + if (host_room == nullptr) { + host_room = + new HostRoomWindow(this, game_list_model, announce_multiplayer_session, room_network); + } + BringWidgetToFront(host_room); +} + +bool MultiplayerState::OnCloseRoom() { + if (!NetworkMessage::WarnCloseRoom()) + return false; + if (auto room = room_network.GetRoom().lock()) { + // if you are in a room, leave it + if (auto member = room_network.GetRoomMember().lock()) { + member->Leave(); + LOG_DEBUG(Frontend, "Left the room (as a client)"); + } + + // if you are hosting a room, also stop hosting + if (room->GetState() != Network::Room::State::Open) { + return true; + } + // Save ban list + UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); + + room->Destroy(); + announce_multiplayer_session->Stop(); + LOG_DEBUG(Frontend, "Closed the room (as a server)"); + } + return true; +} + +void MultiplayerState::ShowNotification() { + if (client_room && client_room->isAncestorOf(QApplication::focusWidget())) + return; // Do not show notification if the chat window currently has focus + show_notification = true; + QApplication::alert(nullptr); + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); + status_text->setText(tr("New Messages Received")); +} + +void MultiplayerState::HideNotification() { + show_notification = false; + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); + status_text->setText(tr("Connected")); +} + +void MultiplayerState::OnOpenNetworkRoom() { + if (auto member = room_network.GetRoomMember().lock()) { + if (member->IsConnected()) { + if (client_room == nullptr) { + client_room = new ClientRoomWindow(this, room_network); + connect(client_room, &ClientRoomWindow::ShowNotification, this, + &MultiplayerState::ShowNotification); + } + BringWidgetToFront(client_room); + return; + } + } + // If the user is not a member of a room, show the lobby instead. + // This is currently only used on the clickable label in the status bar + OnViewLobby(); +} + +void MultiplayerState::OnDirectConnectToRoom() { + if (direct_connect == nullptr) { + direct_connect = new DirectConnectWindow(room_network, this); + } + BringWidgetToFront(direct_connect); +} + +bool MultiplayerState::IsHostingPublicRoom() const { + return announce_multiplayer_session->IsRunning(); +} + +void MultiplayerState::UpdateCredentials() { + announce_multiplayer_session->UpdateCredentials(); +} + +void MultiplayerState::UpdateGameList(QStandardItemModel* game_list) { + game_list_model = game_list; + if (lobby) { + lobby->UpdateGameList(game_list); + } + if (host_room) { + host_room->UpdateGameList(game_list); + } +} diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h new file mode 100755 index 000000000..9c60712d5 --- /dev/null +++ b/src/yuzu/multiplayer/state.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "core/announce_multiplayer_session.h" +#include "network/network.h" + +class QStandardItemModel; +class Lobby; +class HostRoomWindow; +class ClientRoomWindow; +class DirectConnectWindow; +class ClickableLabel; + +class MultiplayerState : public QWidget { + Q_OBJECT; + +public: + explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, + QAction* show_room, Network::RoomNetwork& room_network_); + ~MultiplayerState(); + + /** + * Close all open multiplayer related dialogs + */ + void Close(); + + ClickableLabel* GetStatusText() const { + return status_text; + } + + ClickableLabel* GetStatusIcon() const { + return status_icon; + } + + void retranslateUi(); + + /** + * Whether a public room is being hosted or not. + * When this is true, Web Services configuration should be disabled. + */ + bool IsHostingPublicRoom() const; + + void UpdateCredentials(); + + /** + * Updates the multiplayer dialogs with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* game_list); + +public slots: + void OnNetworkStateChanged(const Network::RoomMember::State& state); + void OnNetworkError(const Network::RoomMember::Error& error); + void OnViewLobby(); + void OnCreateRoom(); + bool OnCloseRoom(); + void OnOpenNetworkRoom(); + void OnDirectConnectToRoom(); + void OnAnnounceFailed(const WebService::WebResult&); + void UpdateThemedIcons(); + void ShowNotification(); + void HideNotification(); + +signals: + void NetworkStateChanged(const Network::RoomMember::State&); + void NetworkError(const Network::RoomMember::Error&); + void AnnounceFailed(const WebService::WebResult&); + +private: + Lobby* lobby = nullptr; + HostRoomWindow* host_room = nullptr; + ClientRoomWindow* client_room = nullptr; + DirectConnectWindow* direct_connect = nullptr; + ClickableLabel* status_icon = nullptr; + ClickableLabel* status_text = nullptr; + QStandardItemModel* game_list_model = nullptr; + QAction* leave_room; + QAction* show_room; + std::shared_ptr announce_multiplayer_session; + Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; + bool has_mod_perms = false; + Network::RoomMember::CallbackHandle state_callback_handle; + Network::RoomMember::CallbackHandle error_callback_handle; + + bool show_notification = false; + Network::RoomNetwork& room_network; +}; + +Q_DECLARE_METATYPE(WebService::WebResult); diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h new file mode 100755 index 000000000..7d48e589d --- /dev/null +++ b/src/yuzu/multiplayer/validation.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +class Validation { +public: + Validation() + : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, 65535) {} + + ~Validation() = default; + + const QValidator* GetRoomName() const { + return &room_name; + } + const QValidator* GetNickname() const { + return &nickname; + } + const QValidator* GetIP() const { + return &ip; + } + const QValidator* GetPort() const { + return &port; + } + +private: + /// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 + QRegExp room_name_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); + QRegExpValidator room_name; + + /// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 + QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); + QRegExpValidator nickname; + + /// ipv4 address only + // TODO remove this when we support hostnames in direct connect + QRegExp ip_regex = QRegExp(QStringLiteral( + "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" + "2[0-4][0-9]|25[0-5])")); + QRegExpValidator ip; + + /// port must be between 0 and 65535 + QIntValidator port; +}; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 2f6948243..6cd4d6cb2 100755 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -102,6 +102,19 @@ struct Values { Settings::Setting callout_flags{0, "calloutFlags"}; + // multiplayer settings + Settings::Setting multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; + Settings::Setting multiplayer_ip{{}, "ip"}; + Settings::SwitchableSetting multiplayer_port{24872, 0, 65535, "port"}; + Settings::Setting multiplayer_room_nickname{{}, "room_nickname"}; + Settings::Setting multiplayer_room_name{{}, "room_name"}; + Settings::SwitchableSetting multiplayer_max_player{8, 0, 8, "max_player"}; + Settings::SwitchableSetting multiplayer_room_port{24872, 0, 65535, "room_port"}; + Settings::SwitchableSetting multiplayer_host_type{0, 0, 1, "host_type"}; + Settings::Setting multiplayer_game_id{{}, "game_id"}; + Settings::Setting multiplayer_room_description{{}, "room_description"}; + std::pair, std::vector> multiplayer_ban_list; + // logging Settings::Setting show_console{false, "showConsole"}; diff --git a/src/yuzu/util/clickable_label.cpp b/src/yuzu/util/clickable_label.cpp new file mode 100755 index 000000000..89d14190a --- /dev/null +++ b/src/yuzu/util/clickable_label.cpp @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "yuzu/util/clickable_label.h" + +ClickableLabel::ClickableLabel(QWidget* parent, [[maybe_unused]] Qt::WindowFlags f) + : QLabel(parent) {} + +void ClickableLabel::mouseReleaseEvent([[maybe_unused]] QMouseEvent* event) { + emit clicked(); +} diff --git a/src/yuzu/util/clickable_label.h b/src/yuzu/util/clickable_label.h new file mode 100755 index 000000000..4fe744150 --- /dev/null +++ b/src/yuzu/util/clickable_label.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +class ClickableLabel : public QLabel { + Q_OBJECT + +public: + explicit ClickableLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~ClickableLabel() = default; + +signals: + void clicked(); + +protected: + void mouseReleaseEvent(QMouseEvent* event); +}; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index cb301e78b..e10d3f5b4 100755 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ #include "core/loader/loader.h" #include "core/telemetry_session.h" #include "input_common/main.h" +#include "network/network.h" #include "video_core/renderer_base.h" #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" @@ -60,6 +62,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 << " [options] \n" + "-m, --multiplayer=nick:password@address:port" + " Nickname, password, address and port for multiplayer\n" "-f, --fullscreen Start in fullscreen mode\n" "-h, --help Display this help and exit\n" "-v, --version Output version information and exit\n" @@ -71,6 +75,107 @@ static void PrintVersion() { std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; } +static void OnStateChanged(const Network::RoomMember::State& state) { + switch (state) { + case Network::RoomMember::State::Idle: + LOG_DEBUG(Network, "Network is idle"); + break; + case Network::RoomMember::State::Joining: + LOG_DEBUG(Network, "Connection sequence to room started"); + break; + case Network::RoomMember::State::Joined: + LOG_DEBUG(Network, "Successfully joined to the room"); + break; + case Network::RoomMember::State::Moderator: + LOG_DEBUG(Network, "Successfully joined the room as a moderator"); + break; + default: + break; + } +} + +static void OnNetworkError(const Network::RoomMember::Error& error) { + switch (error) { + case Network::RoomMember::Error::LostConnection: + LOG_DEBUG(Network, "Lost connection to the room"); + break; + case Network::RoomMember::Error::CouldNotConnect: + LOG_ERROR(Network, "Error: Could not connect"); + exit(1); + break; + case Network::RoomMember::Error::NameCollision: + LOG_ERROR( + Network, + "You tried to use the same nickname as another user that is connected to the Room"); + exit(1); + break; + case Network::RoomMember::Error::MacCollision: + LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " + "connected to the Room"); + exit(1); + break; + case Network::RoomMember::Error::ConsoleIdCollision: + LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room"); + exit(1); + break; + case Network::RoomMember::Error::WrongPassword: + LOG_ERROR(Network, "Room replied with: Wrong password"); + exit(1); + break; + case Network::RoomMember::Error::WrongVersion: + LOG_ERROR(Network, + "You are using a different version than the room you are trying to connect to"); + exit(1); + break; + case Network::RoomMember::Error::RoomIsFull: + LOG_ERROR(Network, "The room is full"); + exit(1); + break; + case Network::RoomMember::Error::HostKicked: + LOG_ERROR(Network, "You have been kicked by the host"); + break; + case Network::RoomMember::Error::HostBanned: + LOG_ERROR(Network, "You have been banned by the host"); + break; + case Network::RoomMember::Error::UnknownError: + LOG_ERROR(Network, "UnknownError"); + break; + case Network::RoomMember::Error::PermissionDenied: + LOG_ERROR(Network, "PermissionDenied"); + break; + case Network::RoomMember::Error::NoSuchUser: + LOG_ERROR(Network, "NoSuchUser"); + break; + } +} + +static void OnMessageReceived(const Network::ChatEntry& msg) { + std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl; +} + +static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { + std::string message; + switch (msg.type) { + case Network::IdMemberJoin: + message = fmt::format("{} has joined", msg.nickname); + break; + case Network::IdMemberLeave: + message = fmt::format("{} has left", msg.nickname); + break; + case Network::IdMemberKicked: + message = fmt::format("{} has been kicked", msg.nickname); + break; + case Network::IdMemberBanned: + message = fmt::format("{} has been banned", msg.nickname); + break; + case Network::IdAddressUnbanned: + message = fmt::format("{} has been unbanned", msg.nickname); + break; + } + if (!message.empty()) + std::cout << std::endl << "* " << message << std::endl << std::endl; +} + /// Application entry point int main(int argc, char** argv) { Common::Log::Initialize(); @@ -92,10 +197,16 @@ int main(int argc, char** argv) { std::optional config_path; std::string program_args; + bool use_multiplayer = false; bool fullscreen = false; + std::string nickname{}; + std::string password{}; + std::string address{}; + u16 port = Network::DefaultRoomPort; static struct option long_options[] = { // clang-format off + {"multiplayer", required_argument, 0, 'm'}, {"fullscreen", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, @@ -109,6 +220,38 @@ int main(int argc, char** argv) { int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); if (arg != -1) { switch (static_cast(arg)) { + case 'm': { + use_multiplayer = true; + const std::string str_arg(optarg); + // regex to check if the format is nickname:password@ip:port + // with optional :password + const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); + if (!std::regex_match(str_arg, re)) { + std::cout << "Wrong format for option --multiplayer\n"; + PrintHelp(argv[0]); + return 0; + } + + std::smatch match; + std::regex_search(str_arg, match, re); + ASSERT(match.size() == 5); + nickname = match[1]; + password = match[2]; + address = match[3]; + if (!match[4].str().empty()) + port = std::stoi(match[4]); + std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); + if (!std::regex_match(nickname, nickname_re)) { + std::cout + << "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n"; + return 0; + } + if (address.empty()) { + std::cout << "Address to room must not be empty.\n"; + return 0; + } + break; + } case 'f': fullscreen = true; LOG_INFO(Frontend, "Starting in fullscreen mode..."); @@ -215,6 +358,21 @@ int main(int argc, char** argv) { system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); + if (use_multiplayer) { + if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) { + member->BindOnChatMessageRecieved(OnMessageReceived); + member->BindOnStatusMessageReceived(OnStatusMessageReceived); + member->BindOnStateChanged(OnStateChanged); + member->BindOnError(OnNetworkError); + LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, + nickname); + member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); + } else { + LOG_ERROR(Network, "Could not access RoomMember"); + return 0; + } + } + // Core is loaded, start the GPU (makes the GPU contexts current to this thread) system.GPU().Start(); system.GetCpuManager().OnGpuReady();

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 ZL1tP0VS}G0FS7}L+l +#include "jwt/jwt.hpp" + +void basic_sign_test() +{ + // Create header + jwt::jwt_header hdr; + hdr = jwt::jwt_header{jwt::algorithm::HS256}; + + // Create payload + jwt::jwt_payload jp; + jp.add_claim("sub", "1234567890"); + jp.add_claim("name", "John Doe"); + jp.add_claim("admin", true); + + jwt::jwt_signature sgn{"secret"}; + std::error_code ec{}; + auto res = sgn.encode(hdr, jp, ec); + std::cout << res << std::endl; +} + +int main() { + basic_sign_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_rsa b/externals/cpp-jwt/include/jwt/test/test_rsa new file mode 100755 index 0000000000000000000000000000000000000000..cc16227c9a3fe95452a48cd1df8d74fd6950b671 GIT binary patch literal 62720 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 Bzy$yR literal 0 HcmV?d00001 diff --git a/externals/cpp-jwt/include/jwt/test/test_rsa.cc b/externals/cpp-jwt/include/jwt/test/test_rsa.cc new file mode 100755 index 000000000..a40624a31 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/test_rsa.cc @@ -0,0 +1,47 @@ +#include +#include "jwt/algorithm.hpp" + +static const char* rsa_2048_pem = +R"(-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m +iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 +lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq +Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH +TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P +45aLU54rj011gG8U/6E1USh5nMnPkr+d3oLfkhfS3Zs3kJVdyFQWZpQxiTaI92Fd +2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT +n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp +wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G +z/9vAQ+sOlvsb4mJP1tJIBx9Lb5loVREwCRy2Ha9tnWdDNar8EYkOn8si4snPT+E +3ZCy8mlcZyUkZeiS/HdtydxZfoiwrSRYamd1diQpPhWCeRteQ802a7ds0Y2YzgfF +UaYjNuRQm7zA//hwbXS7ELPyNMU15N00bajlG0tUOQKBgQDnLy01l20OneW6A2cI +DIDyYhy5O7uulsaEtJReUlcjEDMkin8b767q2VZHb//3ZH+ipnRYByUUyYUhdOs2 +DYRGGeAebnH8wpTT4FCYxUsIUpDfB7RwfdBONgaKewTJz/FPswy1Ye0b5H2c6vVi +m2FZ33HQcoZ3wvFFqyGVnMzpOwKBgQDXxL95yoxUGKa8vMzcE3Cn01szh0dFq0sq +cFpM+HWLVr84CItuG9H6L0KaStEEIOiJsxOVpcXfFFhsJvOGhMA4DQTwH4WuXmXp +1PoVMDlV65PYqvhzwL4+QhvZO2bsrEunITXOmU7CI6kilnAN3LuP4HbqZgoX9lqP +I31VYzLupQKBgGEYck9w0s/xxxtR9ILv5XRnepLdoJzaHHR991aKFKjYU/KD7JDK +INfoAhGs23+HCQhCCtkx3wQVA0Ii/erM0II0ueluD5fODX3TV2ZibnoHW2sgrEsW +vFcs36BnvIIaQMptc+f2QgSV+Z/fGsKYadG6Q+39O7au/HB7SHayzWkjAoGBAMgt +Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K +TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA ++wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV +mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX +LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK +tEOtzsP5GMDYVlEp1jYSjzQ= +-----END PRIVATE KEY-----)"; + +void basic_rsa_test() +{ + jwt::string_view sv = rsa_2048_pem; + jwt::string_view d = "Some random data string"; + + auto res = jwt::PEMSign::sign(sv, d); + + std::cout << res.first << std::endl; +} + +int main() { + basic_rsa_test(); + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_stack_alloc b/externals/cpp-jwt/include/jwt/test/test_stack_alloc new file mode 100755 index 0000000000000000000000000000000000000000..bada01b064ba65dab4b96b15c1906c185de8ebbd GIT binary patch literal 21516 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 literal 0 HcmV?d00001 diff --git a/externals/cpp-jwt/include/jwt/test/test_stack_alloc.cc b/externals/cpp-jwt/include/jwt/test/test_stack_alloc.cc new file mode 100755 index 000000000..ab158db95 --- /dev/null +++ b/externals/cpp-jwt/include/jwt/test/test_stack_alloc.cc @@ -0,0 +1,22 @@ +#include +#include +#include "jwt/stack_alloc.hpp" + +template +using SmallVector = std::vector>; + + +int main() +{ + SmallVector::allocator_type::arena_type a; + SmallVector v{a}; + + v.push_back(1); + v.push_back(1); + v.push_back(1); + v.push_back(1); + v.push_back(1); + v.push_back(1); + + return 0; +} diff --git a/externals/cpp-jwt/include/jwt/test/test_sv b/externals/cpp-jwt/include/jwt/test/test_sv new file mode 100755 index 0000000000000000000000000000000000000000..e8ac63eef913019c5e878f2a5b898df9d4e99442 GIT binary patch literal 45516 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 mr`^2KQOPSeh?wNV2 +#include +#include +#include +#include "jwt/string_view.hpp" + +using string_view = jwt::basic_string_view; + +void basic_cons() +{ + // Default construction + string_view sv{}; + assert (sv.length() == 0 && "Size must be zero for default constructor"); + + // Construction from string literal + string_view sv2{"Arun Muralidharan"}; + assert (sv2.length() == strlen("Arun Muralidharan") && "Lengths must match"); + + const char* haystack = "some really big data with infinite objects...."; + + // Construct using part of data + string_view sv3{haystack, 4}; + assert (sv3.length() == 4 && "Partial construction is not ok"); + assert (sv3.to_string() == "some" && "Partial strings are not equal"); + + return; +} + +void iterator_test() +{ + string_view sv{"Arun Muralidharan"}; + for (auto c : sv) std::cout << c; + std::cout << std::endl; + return; +} + +void str_operations() +{ + string_view sv{"Arun Muralidharan"}; + string_view tmp = sv; + sv.remove_prefix(5); + assert (sv.to_string() == "Muralidharan" && "Remove prefix failed"); + + sv = tmp; + sv.remove_suffix(strlen("Muralidharan")); + assert (sv.to_string() == "Arun " && "Remove suffix failed"); + + sv=tmp; + { + std::unique_ptr dst{new char[32]}; + sv.copy(dst.get(), 6, 0); + dst[6] = '\0'; + assert (strlen(dst.get()) == 6 && "Copy Failed-1"); + assert (std::string{dst.get()} == "Arun M" && "Copy Failed-2"); + + sv.copy(dst.get(), 8, 4); + dst[8] = '\0'; + assert (strlen(dst.get()) == 8 && "Middle copy failed-1"); + assert (std::string{dst.get()} == " Muralid" && "Middle copy failed-2"); + } + + { + auto ss1 = sv.substr(0, 4); + assert (ss1.to_string() == "Arun" && "Substr failed - 1"); + + auto ss2 = sv.substr(1, 3); + assert (ss2.to_string() == "run" && "Substr failed - 2"); + + auto ss3 = sv.substr(0); + assert (ss3.length() == sv.length() && "Substr failed - 3"); + } + + return; +} + +void find_oper() +{ + string_view sv{"Arun Muralidharan"}; + auto pos = sv.find("Arun", 0, 4); + assert (pos == 0 && "Arun not found in sv"); + + pos = sv.find("arun", 0, 4); + assert (pos == string_view::npos && "arun is not there in sv"); + + sv = "This has a, in it."; + pos = sv.find_first_of(",", 0, 1); + assert (pos != string_view::npos); + assert (pos == 10 && "Comma not found at correct place"); + + pos = sv.find_first_of(",", 10, 1); + assert (pos != string_view::npos); + assert (pos == 10 && "Comma not found at correct place"); + + pos = sv.find_first_of(":", 10, 1); + assert (pos == string_view::npos); + + pos = sv.find_last_of(",", 5, 1); + assert (pos == string_view::npos); + + pos = sv.find_last_of(",", sv.length() - 1, 1); + assert (pos != string_view::npos); + assert (pos == 10 && "Comma not found at correct place"); + + pos = sv.find_first_of(".", 0, 1); + assert (pos == sv.length() - 1 && "Dot not found at correct place"); + + pos = sv.find_last_of(".", sv.length() - 2, 1); + assert (pos == string_view::npos); + + pos = sv.find_last_of(".", sv.length() - 1, 1); + assert (pos == sv.length() - 1); + + sv = "Some string :<> with some ??? pattern --**"; + + pos = sv.rfind("???", sv.length() - 1, 3); + assert (pos != string_view::npos && "??? not found"); + assert (pos == 26 && "??? not found at the correct place"); + + sv = "ATCGTTCACGRRRTCGGGGACGTC"; + + pos = sv.find_first_not_of("ATCG"); + assert (pos != string_view::npos); + assert (pos == 10); + + return; +} + +void conversions() +{ + auto c2sv = [](int num) -> string_view { + switch (num) { + case 1: return "one"; + case 2: return "two"; + case 3: return "three"; + default: return "many"; + }; + }; + + auto res = c2sv(2); + assert (res.to_string() == "two"); + + auto s2sv = [](std::string s) { + return s; + }; + + s2sv(static_cast(res)); +} + +void comparisons() +{ + string_view s1{"Apple"}; + string_view s2{"Orange"}; + + assert (s1 != s2 && "Two string views are not equal"); + assert (s2 > s1 && "Orange is lexicographically bigger than Apple"); + + s2 = "Apples"; + assert (s2 > s1 && "Because Apples is plural"); +} + +int main() { + basic_cons(); + iterator_test(); + str_operations(); + find_oper(); + conversions(); + comparisons(); + return 0; +}; diff --git a/externals/cpp-jwt/tests/CMakeLists.txt b/externals/cpp-jwt/tests/CMakeLists.txt new file mode 100755 index 000000000..54196519d --- /dev/null +++ b/externals/cpp-jwt/tests/CMakeLists.txt @@ -0,0 +1,69 @@ +set(CERT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/certs") +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -DCERT_ROOT_DIR=\"\\\"${CERT_ROOT_DIR}\\\"\"") + +add_executable(test_jwt_object test_jwt_object.cc) +target_link_libraries(test_jwt_object GTest::GTest GTest::Main ${PROJECT_NAME}) +target_include_directories(test_jwt_object PRIVATE ${GTEST_INCLUDE_DIRS} + ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_object + COMMAND ./test_jwt_object + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_encode test_jwt_encode.cc) +target_link_libraries(test_jwt_encode GTest::GTest GTest::Main ${PROJECT_NAME}) +target_include_directories(test_jwt_encode PRIVATE ${GTEST_INCLUDE_DIRS} + ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_encode + COMMAND ./test_jwt_encode + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_decode test_jwt_decode.cc) +target_link_libraries(test_jwt_decode GTest::GTest GTest::Main ${PROJECT_NAME}) +target_include_directories(test_jwt_decode PRIVATE ${GTEST_INCLUDE_DIRS} + ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_decode + COMMAND ./test_jwt_decode + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_decode_verifiy test_jwt_decode_verifiy.cc) +target_link_libraries(test_jwt_decode_verifiy GTest::GTest GTest::Main + ${PROJECT_NAME}) +target_include_directories(test_jwt_decode_verifiy + PRIVATE ${GTEST_INCLUDE_DIRS} ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_decode_verifiy + COMMAND ./test_jwt_decode_verifiy + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_decode_verifiy_with_exception + test_jwt_decode_verifiy_with_exception.cc) +target_link_libraries(test_jwt_decode_verifiy_with_exception GTest::GTest GTest::Main + ${PROJECT_NAME}) +target_include_directories(test_jwt_decode_verifiy_with_exception + PRIVATE ${GTEST_INCLUDE_DIRS} ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_decode_verifiy_with_exception + COMMAND ./test_jwt_decode_verifiy_with_exception + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_rsa test_jwt_rsa.cc) +target_link_libraries(test_jwt_rsa GTest::GTest GTest::Main ${PROJECT_NAME}) +target_include_directories(test_jwt_rsa PRIVATE ${GTEST_INCLUDE_DIRS} + ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_rsa + COMMAND ./test_jwt_rsa + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(test_jwt_es test_jwt_es.cc) +target_link_libraries(test_jwt_es GTest::GTest GTest::Main ${PROJECT_NAME}) +target_include_directories(test_jwt_es PRIVATE ${GTEST_INCLUDE_DIRS} + ${GTest_INCLUDE_DIRS}) +add_test( + NAME test_jwt_es + COMMAND ./test_jwt_es + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/externals/cpp-jwt/tests/certs/ec_certs/ec384_priv.pem b/externals/cpp-jwt/tests/certs/ec_certs/ec384_priv.pem new file mode 100755 index 000000000..f704c4b72 --- /dev/null +++ b/externals/cpp-jwt/tests/certs/ec_certs/ec384_priv.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBeLCgapjZmvTatMHaYX3A02+0Ys3Tr8kda+E9DFnmCSiCOEig519fT +13edeU8YdDugBwYFK4EEACKhZANiAASibEL3JxzwCRdLBZCm7WQ3kWaDL+wP8omo +3e2VJmZQRnfDdzopgl8r3s8w5JlBpR17J0Gir8g6CVBA6PzMuq5urkilppSINDnR +4mDv0+9e4uJVQf3xwEv+jywNUH+wbPM= +-----END EC PRIVATE KEY----- diff --git a/externals/cpp-jwt/tests/certs/ec_certs/ec384_pub.pem b/externals/cpp-jwt/tests/certs/ec_certs/ec384_pub.pem new file mode 100755 index 000000000..13e9199ed --- /dev/null +++ b/externals/cpp-jwt/tests/certs/ec_certs/ec384_pub.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEomxC9ycc8AkXSwWQpu1kN5Fmgy/sD/KJ +qN3tlSZmUEZ3w3c6KYJfK97PMOSZQaUdeydBoq/IOglQQOj8zLqubq5IpaaUiDQ5 +0eJg79PvXuLiVUH98cBL/o8sDVB/sGzz +-----END PUBLIC KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_priv.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_priv.pem new file mode 100755 index 000000000..0f58120a3 --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_priv.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m +iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 +lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq +Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH +TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P +45aLU54rj011gG8U/6E1USh5nMnPkr+d3oLfkhfS3Zs3kJVdyFQWZpQxiTaI92Fd +2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT +n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp +wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G +z/9vAQ+sOlvsb4mJP1tJIBx9Lb5loVREwCRy2Ha9tnWdDNar8EYkOn8si4snPT+E +3ZCy8mlcZyUkZeiS/HdtydxZfoiwrSRYamd1diQpPhWCeRteQ802a7ds0Y2YzgfF +UaYjNuRQm7zA//hwbXS7ELPyNMU15N00bajlG0tUOQKBgQDnLy01l20OneW6A2cI +DIDyYhy5O7uulsaEtJReUlcjEDMkin8b767q2VZHb//3ZH+ipnRYByUUyYUhdOs2 +DYRGGeAebnH8wpTT4FCYxUsIUpDfB7RwfdBONgaKewTJz/FPswy1Ye0b5H2c6vVi +m2FZ33HQcoZ3wvFFqyGVnMzpOwKBgQDXxL95yoxUGKa8vMzcE3Cn01szh0dFq0sq +cFpM+HWLVr84CItuG9H6L0KaStEEIOiJsxOVpcXfFFhsJvOGhMA4DQTwH4WuXmXp +1PoVMDlV65PYqvhzwL4+QhvZO2bsrEunITXOmU7CI6kilnAN3LuP4HbqZgoX9lqP +I31VYzLupQKBgGEYck9w0s/xxxtR9ILv5XRnepLdoJzaHHR991aKFKjYU/KD7JDK +INfoAhGs23+HCQhCCtkx3wQVA0Ii/erM0II0ueluD5fODX3TV2ZibnoHW2sgrEsW +vFcs36BnvIIaQMptc+f2QgSV+Z/fGsKYadG6Q+39O7au/HB7SHayzWkjAoGBAMgt +Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K +TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA ++wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV +mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX +LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK +tEOtzsP5GMDYVlEp1jYSjzQ= +-----END PRIVATE KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_pub.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_pub.pem new file mode 100755 index 000000000..01f59bf42 --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa256_pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g +6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe +K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t +BwIDAQAB +-----END PUBLIC KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_priv.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_priv.pem new file mode 100755 index 000000000..12994932b --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_priv.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +nfictCHvTZXlDWYlp5Uf8IDXrd4= +-----END PRIVATE KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_pub.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_pub.pem new file mode 100755 index 000000000..78b1d31f4 --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa384_pub.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAybi/3guc70NLoGecAdyA +o0dJYLRXCYFdx7Iaq20REXb5SsyqSjGV/KEr8sdyFhr/PasKb1XbV7G8/gdtxU+S +l5dGI9h8mymIQ7VzbyEhwQqjzdyCKoz2levKPgjsTDN93IQiNfSOfc4qUux5viWQ +aPWt3bzpvOZXHcMUbzEz/jahKZPU/hMq/N3DroE6xFYB91fb74qHOC5RGMPjskUY +KOjCtj+v+sZqznuckf2x0a2r5X++gsqGZT8dHaX9smyrpBbV9Y/g1VZ+CO8XFdRi +HWth98FnlbEeHsrXbho9CE2GEACWvRRApMn1uSpghpENbkefpbYCIN7tq6X4lKvS +YQZARiLkD1ELHcmWCX+cpIlfxihrKpVnRR4iLJNl33VtOBIPaMvtifojkLxsi3dt +yJkcEFQI/kXbKW8Kdw5Fd+UOcsQf8PKk7k9LgFaH8GZRFhbwT0bLjjy0UIV5NDHc +SozydV2uQh+ON6BsygCSvME8V4lu9z74Rq3T7eP25pC+Q0mN+1n7QGQL4zKshkKN +h6Rx+QCP0hVGNosL6MoLzseluapTqnHrQEHTbG1Jm+WL/ces3thH7PySyWKJcUQ/ +iWBD0FFkya0KTvTfWvBg/WPdqBHLIQh6ie/7lOFyE56FbRYuRU0cF8+NykA/6HN2 +47pFtw1PSgQ68Dqtv9bZMFUCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_priv.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_priv.pem new file mode 100755 index 000000000..9e72c836c --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_priv.pem @@ -0,0 +1,100 @@ +-----BEGIN PRIVATE KEY----- +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 +EtoiU3WKndSOrpIVOqOQTQU54puQoOE= +-----END PRIVATE KEY----- diff --git a/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_pub.pem b/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_pub.pem new file mode 100755 index 000000000..f9162df02 --- /dev/null +++ b/externals/cpp-jwt/tests/certs/rsa_certs/rsa512_pub.pem @@ -0,0 +1,25 @@ +-----BEGIN PUBLIC KEY----- +MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAs0XMckvY06KW80N/Fz5r +BJUN3BlsUoViZYjpZy5UbhNAt96tdnoMMXJw6vd9+/DWwBEgpq7cRUsWq1qRkqSL +z8dxTQ8OGsxauFVTWRTX3ZMQ/YtZJDGa1+snkxhsLxnvpXx/KPgYU1WTqB46GV+i +twsZ69S0nq+cNMjIFiz6TJMNVNwI5yEdYUildxb0YmGu/ZBQMihcrYesAJSQezNu +10YvioSIQUASHjfVcFMMLtZRYvi7idIfejb7iW2rPlO7sF4ZNhTaSoszB03BvnNZ +jikORBnDz71AgMKqW/Itlq/uMuMb0XlPCiLZgS83oLfSvMQQspeOks3yv5w3Hv+s +EFXrSZyyt3SPxHuc3gggSqLcfzCV4PG/p2H7y5Q6i9VHPaA89ZQXlKWQC46sripP +OtkSAkyJU9NJ/WkYNnnXzQiKA3kfN2mLCOzpv62SGoUrEssycTuVLUKTzM76LaYQ +/lGbpbSrTGvZEmm3OJjjFr5r2JSPmzGw+5dOUpJ+i3r9+um+iUKKpOkTISYyCXY1 +7w9yf13W4Gms7l5XJU+pRIlBlb3H2QFN2u9wp7mEXYOJzGNGpA3oHFKUmn40jlz5 +09dNpGaG4SXzJG8Uku+mWrjWWsnh9VcZQhgLKVobJsmmSIkqcpJCityl21LAk7Do +UB0GeMCFOjzPhZ0ncD7RVBwq762A6rcowuuY8Lwbrp9auNgzzhHHr+c01aFvqZbU +T4oaxSFInyrocsyCqJYQEG3sDK6NMxNIPs9NIyC+IWPbqvxESOGJK6rLmh2WCaD0 +irCvboW5yxfxEaCuu3WWUFFEecR0KvHRQig/dFb2Jyo4fypzqdSRtAu0R7/qOn6p +LsEO9XseQh7uRzdagwb9CjdBB8nKB5zVQ79gswBDY2+gDl1UphywtT+ISr4kLUm3 +tvOFQZjj1SNaZdLTDQbxO9GWjwaijIQbAdMU0f9kE7FHIR3vPEMLfwNgpo6hEyuk +TBfBTT1cq8eHQt0WbDLDYFcuA1fFa9XGrZPmmGCoV8AFKQaUNRcIbfRWnykPs9oJ +Ki60gKbhoIStIq/DkV/B8OPTMRTbFranSJDaO2P6O0A5Y7Rezhjnqyb0oiJsKg74 ++/TcgL/tZB+YB3AG3gdElVnu9cWuR2FpY35L1WAk9zA46Z4dxQyL7nNpQDg5pX4Q +8sXK2x3Ov1VmYy1TmUxpv1eLTMxts0yXYDQFGcDS4VuDfZZNkIXMOMEo4y5fQw11 +OOcElpvyVMUkYl0LgJvIqbUCzCulkrruKKPPdHrtJvhImysgT46lbjKPGDT4D/6q +It4UqV9B969PNbqfqFORnmgaH3oF3xxbAQfcasfUxCPxChYpOPs25ldtAASAAqDO +RwIDAQAB +-----END PUBLIC KEY----- diff --git a/externals/cpp-jwt/tests/compile.txt b/externals/cpp-jwt/tests/compile.txt new file mode 100755 index 000000000..d70f51f64 --- /dev/null +++ b/externals/cpp-jwt/tests/compile.txt @@ -0,0 +1 @@ + g++ -std=c++14 -I /usr/local/Cellar/openssl/1.0.2j/include/ -I /Users/amuralid/dev_test/cpp-jwt/include/ -o test_jwt_encode test_jwt_encode.cc -L /usr/local/Cellar//openssl/1.0.2j/lib/ -lssl -lcrypto -lgtest diff --git a/externals/cpp-jwt/tests/test_jwt_decode.cc b/externals/cpp-jwt/tests/test_jwt_decode.cc new file mode 100755 index 000000000..215d312ac --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_decode.cc @@ -0,0 +1,188 @@ +#include +#include "gtest/gtest.h" +#include "jwt/jwt.hpp" + +TEST (DecodeTest, InvalidFinalDotForNoneAlg) +{ + using namespace jwt::params; + const char* inv_enc_str = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ"; + + std::error_code ec; + auto obj = jwt::decode(inv_enc_str, algorithms({"none", "HS256"}), ec); + + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::SignatureFormatError)); +} + +TEST (DecodeTest, DecodeNoneAlgSign) +{ + using namespace jwt::params; + const char* enc_str = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjo0NTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none"}), ec, verify(true)); + EXPECT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::AlgorithmErrc::NoneAlgorithmUsed)); + + std::cout << obj.payload() << std::endl; + + EXPECT_FALSE (obj.has_claim("iss")); + EXPECT_FALSE (obj.has_claim("ISS")); + + EXPECT_TRUE (obj.has_claim("aud")); + EXPECT_TRUE (obj.has_claim("exp")); + + EXPECT_EQ (obj.payload().get_claim_value("exp"), static_cast(4513863371)); +} + +TEST (DecodeTest, DecodeWrongAlgo) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret(""), verify(true)); + EXPECT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::InvalidAlgorithm)); +} + +TEST (DecodeTest, DecodeInvalidHeader) +{ + using namespace jwt::params; + + const char* enc_str = + "ehbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret(""), verify(true)); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::JsonParseError)); + +} + +TEST (DecodeTest, DecodeEmptyHeader) +{ + using namespace jwt::params; + + const char* enc_str = + ".eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret(""), verify(true)); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::JsonParseError)); + +} + +TEST (DecodeTest, DecodeInvalidPayload) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyfhuWcikiJyaWZ0LmlvIiwiZXhwIsexNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none"}), ec, verify(true)); + ASSERT_TRUE (ec); + + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::JsonParseError)); +} + +TEST (DecodeTest, DecodeHS256) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none", "HS256"}), ec, verify(false), secret("secret")); + ASSERT_FALSE (ec); + + EXPECT_TRUE (obj.has_claim("iss")); + EXPECT_TRUE (obj.payload().has_claim_with_value("iss", "arun.muralidharan")); + + //Case sensitive search + EXPECT_FALSE (obj.has_claim("IAT")); + EXPECT_TRUE (obj.payload().has_claim_with_value(jwt::registered_claims::issued_at, 1513862371)); + + EXPECT_FALSE (obj.payload().has_claim_with_value(jwt::registered_claims::issued_at, 1513862372)); +} + +TEST (DecodeTest, SecretKeyNotPassed) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none", "HS256"}), ec, verify(true)); + + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::KeyNotPresent)); +} + +TEST (DecodeTest, DecodeHS384) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9." + "eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ." + "cGN4FZCe9Y2c1dA-jP71IXGnYbJRc4OaUTa5m7N7ybF5h6wBwxWQ-pdcxYchjDBL"; + + const jwt::string_view key = "0123456789abcdefghijklmnopqrstuvwxyz"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none", "HS384"}), ec, verify(false), secret(key)); + ASSERT_FALSE (ec); + + EXPECT_TRUE (obj.has_claim("sub")); + EXPECT_TRUE (obj.payload().has_claim_with_value("sub", "nothing much")); +} + +TEST (DecodeTest, DecodeHS512) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9." + "eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ." + "vQ-1JSFN1kPjUI3URP6AFK5z8V7xLhyhw-76QWhQg9Xcy-IgrJ-bCTYLBjgaprrcEWwpSnBQnP3QnIxYK0HEaQ"; + + const jwt::string_view key = "00112233445566778899"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none", "HS384", "HS512"}), ec, verify(false), secret(key)); + + ASSERT_FALSE (ec); + + EXPECT_TRUE (obj.has_claim("sub")); + EXPECT_TRUE (obj.payload().has_claim_with_value("sub", "nothing much")); +} + +TEST (DecodeTest, TypHeaderMiss) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzI1NiJ9." + "eyJleHAiOjE1MzM0NjE1NTMsImlhdCI6MTUxMzg2MjM3MSwiaWQiOiJhLWItYy1kLWUtZi0xLTItMyIsImlzcyI6ImFydW4ubXVyYWxpZGhhcmFuIiwic3ViIjoiYWRtaW4ifQ." + "pMWBLSWl1p4V958lfe_6ZhvgFMOQv9Eq5mlndVKFKkA"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"none", "HS256"}), ec, verify(false)); + std::cout << "Decode header: " << obj.header() << std::endl; + + EXPECT_FALSE (ec); +} + diff --git a/externals/cpp-jwt/tests/test_jwt_decode_verifiy.cc b/externals/cpp-jwt/tests/test_jwt_decode_verifiy.cc new file mode 100755 index 000000000..489d1ca3a --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_decode_verifiy.cc @@ -0,0 +1,196 @@ +#include +#include +#include + +#include "jwt/jwt.hpp" +#include "gtest/gtest.h" + +TEST (DecodeVerify, BeforeExpiryTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10}) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), verify(true)); + ASSERT_FALSE (ec); +} + +TEST (DecodeVerify, AfterExpiryTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), verify(true)); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::TokenExpired)); +} + +TEST (DecodeVerify, AfterExpiryWithLeeway) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), verify(true), leeway(2)); + ASSERT_FALSE (ec); +} + +TEST (DecodeVerify, ValidIssuerTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "test") + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), issuer("arun.muralidharan")); + ASSERT_FALSE (ec); +} + +TEST (DecodeVerify, InvalidIssuerTest_1) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), issuer("arun.muralidharan")); + ASSERT_TRUE (ec); + + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::InvalidIssuer)); +} + +TEST (DecodeVerify, InvalidIssuerTest_2) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim("iss", "arun.muralidharan"); + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), issuer("arun.murali")); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::InvalidIssuer)); +} + +TEST (DecodeVerify, NotImmatureSignatureTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() - std::chrono::seconds{10}); + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret")); + ASSERT_FALSE (ec); +} + +TEST (DecodeVerify, ImmatureSignatureTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() + std::chrono::seconds{10}); + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret")); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::ImmatureSignature)); +} + +TEST (DecodeVerify, ImmatureSignatureTestWithLeeway) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() + std::chrono::seconds{10}); + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), leeway(10)); + ASSERT_FALSE (ec); +} + +TEST (DecodeVerify, InvalidAudienceTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})}; + + std::error_code ec; + auto enc_str = obj.signature(ec); + ASSERT_FALSE (ec); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), aud("ww")); + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::InvalidAudience)); +} + +TEST (DecodeVerify, InvalidIATTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})}; + + obj.add_claim("iat", "what?"); + auto enc_str = obj.signature(); + + std::error_code ec; + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, secret("secret"), validate_iat(true)); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::TypeConversionError)); +} + +TEST (DecodeVerify, InvalidSignatureTest) +{ + using namespace jwt::params; + + std::error_code ec; + auto dec_obj = jwt::decode("", algorithms({"HS256"}), ec, secret("secret"), validate_iat(true)); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::SignatureFormatError)); + + ec.clear(); + dec_obj = jwt::decode("abcdsdfhbsdhjfbsdj.", algorithms({"HS256"}), ec, secret("secret"), validate_iat(true)); + EXPECT_EQ (ec.value(), static_cast(jwt::DecodeErrc::SignatureFormatError)); +} + diff --git a/externals/cpp-jwt/tests/test_jwt_decode_verifiy_with_exception.cc b/externals/cpp-jwt/tests/test_jwt_decode_verifiy_with_exception.cc new file mode 100755 index 000000000..4a9a9a2f4 --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_decode_verifiy_with_exception.cc @@ -0,0 +1,178 @@ +#include +#include +#include + +#include "jwt/jwt.hpp" +#include "gtest/gtest.h" + +TEST (DecodeVerifyExp, BeforeExpiryTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10}) + ; + + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), verify(true)); +} + +TEST (DecodeVerifyExp, AfterExpiryTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), verify(true)), + jwt::TokenExpiredError); + +} + +TEST (DecodeVerifyExp, AfterExpiryWithLeeway) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1}) + ; + + auto enc_str = obj.signature(); + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), verify(true), leeway(2)); + (void)dec_obj; +} + +TEST (DecodeVerifyExp, ValidIssuerTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "test") + ; + + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), issuer("arun.muralidharan")); + (void)dec_obj; +} + +TEST (DecodeVerifyExp, InvalidIssuerTest_1) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), issuer("arun.muralidharan")), + jwt::InvalidIssuerError); + +} + +TEST (DecodeVerifyExp, InvalidIssuerTest_2) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim("iss", "arun.muralidharan"); + + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), issuer("arun.murali")), + jwt::InvalidIssuerError); +} + +TEST (DecodeVerifyExp, NotImmatureSignatureTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() - std::chrono::seconds{10}); + + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret")); + (void)dec_obj; +} + +TEST (DecodeVerifyExp, ImmatureSignatureTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() + std::chrono::seconds{10}); + + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret")), + jwt::ImmatureSignatureError); +} + +TEST (DecodeVerifyExp, ImmatureSignatureTestWithLeeway) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}})}; + obj.add_claim(jwt::registered_claims::not_before, std::chrono::system_clock::now() + std::chrono::seconds{10}); + + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), leeway(10)); + (void)dec_obj; +} + +TEST (DecodeVerifyExp, InvalidAudienceTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})}; + + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), aud("ww")), + jwt::InvalidAudienceError); +} + +TEST (DecodeVerifyExp, InvalidSignatureTest) +{ + using namespace jwt::params; + + const char* inv_enc_str = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ"; + + EXPECT_THROW (jwt::decode(inv_enc_str, algorithms({"none", "HS256"})), + jwt::SignatureFormatError); +} + +TEST (DecodeVerifyExp, KeyNotPresentTest) +{ + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"none", "HS256"}), verify(true)), + jwt::KeyNotPresentError); +} + +TEST (DecodeVerifyExp, InvalidSubjectTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})}; + + auto enc_str = obj.signature(); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"HS256"}), secret("secret"), sub("TEST")), + jwt::InvalidSubjectError); +} + diff --git a/externals/cpp-jwt/tests/test_jwt_encode.cc b/externals/cpp-jwt/tests/test_jwt_encode.cc new file mode 100755 index 000000000..29bd9b951 --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_encode.cc @@ -0,0 +1,314 @@ +#include +#include +#include +#include "gtest/gtest.h" +#include "jwt/jwt.hpp" + +TEST (EncodeTest, TestRemoveClaim) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "admin") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + .add_claim("exp", std::chrono::system_clock::now()); + + EXPECT_TRUE (obj.has_claim(jwt::registered_claims::expiration)); + + obj.remove_claim("exp"); + EXPECT_FALSE (obj.has_claim(jwt::registered_claims::expiration)); + + obj.remove_claim(jwt::registered_claims::subject); + EXPECT_FALSE (obj.has_claim("sub")); +} + +TEST (EncodeTest, TestRemoveTypHeader) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "admin") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + .add_claim("exp", std::chrono::system_clock::now()); + + EXPECT_TRUE (obj.header().has_header("typ")); + obj.header().remove_header("typ"); + EXPECT_FALSE (obj.header().has_header("typ")); + + std::cout << "Header: " << obj.header() << '\n'; + std::cout << "Signature: " << obj.signature() << '\n'; +} + +TEST (EncodeTest, StrEncodeHS256_1) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("sub", "admin") + .add_claim("id", "a-b-c-d-e-f-1-2-3") + .add_claim("iat", 1513862371) + ; + + std::cout << "Header: " << obj.header() << std::endl; + std::cout << "Payload: "<< obj.payload() << std::endl; + + std::string enc_str = obj.signature(); + + std::cout << "Signature: " << enc_str << std::endl; + + EXPECT_EQ (enc_str, expected_sign); +} + +TEST (EncodeTest, StrEncodeHS256_2) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + jwt::jwt_object obj{algorithm("HS256"), + secret("secret"), + payload( + { + {"iss", "arun.muralidharan"}, + {"sub", "admin"}, + {"id", "a-b-c-d-e-f-1-2-3"} + }) + }; + + obj.add_claim("iat", 1513862371); + + std::string enc_str = obj.signature(); + EXPECT_EQ (enc_str, expected_sign); +} + + +TEST (EncodeTest, StrEncodeNONE) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + jwt::jwt_object obj{algorithm("none")}; + + obj.add_claim("aud", "rift.io") + .add_claim("exp", 1513863371) + .add_claim("sub", "nothing much") + ; + + std::cout << "Header: " << obj.header() << std::endl; + std::cout << "Payload: " << obj.payload() << std::endl; + + std::string enc_str = obj.signature(); + + EXPECT_EQ (enc_str, expected_sign); +} + +TEST (EncodeTest, StrEncodeHS256WithKey) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ." + "W6t7mUX6ZJwOVTsVhHSKyBSwi0wnibobdsk456wSmJg"; + + jwt::jwt_object obj{algorithm(jwt::algorithm::HS256), + secret("0123456789abcdefghijklmnopqrstuvwxyz"), + payload( + { + {"aud", "rift.io"}, + {"sub", "nothing much"} + }) + }; + obj.add_claim("exp", 1513863371); + + std::string enc_str = obj.signature(); + + EXPECT_EQ (expected_sign, enc_str); +} + +TEST (EncodeTest, StrEncodeHS384WithKey) +{ + + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9." + "eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ." + "cGN4FZCe9Y2c1dA-jP71IXGnYbJRc4OaUTa5m7N7ybF5h6wBwxWQ-pdcxYchjDBL"; + + jwt::jwt_object obj{algorithm(jwt::algorithm::HS384), + secret("0123456789abcdefghijklmnopqrstuvwxyz"), + payload( + { + {"aud", "rift.io"}, + {"sub", "nothing much"} + }) + }; + obj.add_claim("exp", 1513863371); + + std::string enc_str = obj.signature(); + + EXPECT_EQ (expected_sign, enc_str); +} + +TEST (EncodeTest, StrEncodeHS512WithKey) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9." + "eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ." + "vQ-1JSFN1kPjUI3URP6AFK5z8V7xLhyhw-76QWhQg9Xcy-IgrJ-bCTYLBjgaprrcEWwpSnBQnP3QnIxYK0HEaQ"; + + jwt::string_view key = "00112233445566778899"; + + std::map p; + p["aud"] = "rift.io"; + p["sub"] = "nothing much"; + + jwt::jwt_object obj{algorithm(jwt::algorithm::HS512), + secret(key), + payload(std::move(p)) + }; + obj.add_claim("exp", 1513863371); + + std::string enc_str = obj.signature(); + + EXPECT_EQ (enc_str, expected_sign); +} + +TEST (EncodeTest, StrEncodeChangeAlg) +{ + using namespace jwt::params; + + const char* expected_none_sign = + "eyJhbGciOiJOT05FIiwidHlwIjoiSldUIn0.eyJhdWQiOiJyaWZ0LmlvIiwiZXhwIjoxNTEzODYzMzcxLCJzdWIiOiJub3RoaW5nIG11Y2gifQ."; + + jwt::string_view key = "00112233445566778899"; + + std::map p; + p["aud"] = "rift.io"; + p["sub"] = "nothing much"; + + jwt::jwt_object obj{algorithm(jwt::algorithm::HS512), + secret(key), + payload(std::move(p)) + }; + obj.add_claim("exp", 1513863371); + + obj.header().algo("none"); + std::string enc_str = obj.signature(); + + EXPECT_EQ (expected_none_sign, enc_str); +} + +TEST (EncodeTest, StrEncodeNoKey) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm(jwt::algorithm::HS512), + payload({{"iss", "arn-ml"}}) + }; + + std::error_code ec; + std::string enc_str = obj.signature(ec); + + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::AlgorithmErrc::KeyNotFoundErr)); +} + +TEST (EncodeTest, StrEncodeNoneAlgWithKey) +{ + using namespace jwt::params; + + const jwt::string_view secret1 = "abcdefghijklmnopqrstuvwxyz"; + const jwt::string_view secret2 = "0123456789qwertybabe"; + + jwt::jwt_object obj{algorithm("none"), + payload({{"iss", "arn-ml"}}), + secret(secret1)}; + + std::error_code ec; + std::string enc_str1 = obj.signature(ec); + ASSERT_FALSE (ec); + + obj.secret(secret2); + std::string enc_str2 = obj.signature(ec); + ASSERT_FALSE (ec); + + EXPECT_EQ (enc_str1, enc_str2); +} + +TEST (EncodeTest, OverwriteClaimsTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("none"), + payload({ + {"iss", "arn-ml"}, + {"x-pld1", "data1"}, + {"x-pld2", "data2"}, + {"x-pld3", "123"} + }) + }; + + bool ret = obj.payload().add_claim("x-pld1", "1data"); + EXPECT_FALSE (ret); + + ret = obj.payload().add_claim("x-pld1", "1data", true/*overwrite*/); + EXPECT_TRUE (ret); + + EXPECT_TRUE (obj.payload().has_claim_with_value("x-pld1", "1data")); +} + +TEST (EncodeTest, HeaderParamTest) +{ + using namespace jwt::params; + + jwt::jwt_object obj{ + headers({ + {"alg", "none"}, + {"typ", "jwt"}, + }), + payload({ + {"iss", "arun.muralidharan"}, + {"sub", "nsfw"}, + {"x-pld", "not my ex"} + }) + }; + + bool ret = obj.header().add_header("kid", 1234567); + EXPECT_TRUE (ret); + + ret = obj.header().add_header("crit", std::array{ {"exp"} }); + EXPECT_TRUE (ret); + + std::cout << obj.header() << std::endl; + + std::error_code ec; + auto enc_str = obj.signature(); + + auto dec_obj = jwt::decode(enc_str, algorithms({"none"}), ec, verify(true)); + EXPECT_EQ (ec.value(), static_cast(jwt::AlgorithmErrc::NoneAlgorithmUsed)); + + std::cout << dec_obj.header() << std::endl; +} + diff --git a/externals/cpp-jwt/tests/test_jwt_es.cc b/externals/cpp-jwt/tests/test_jwt_es.cc new file mode 100755 index 000000000..7a3474328 --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_es.cc @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "jwt/jwt.hpp" + +#define EC384_PUB_KEY CERT_ROOT_DIR "/ec_certs/ec384_pub.pem" +#define EC384_PRIV_KEY CERT_ROOT_DIR "/ec_certs/ec384_priv.pem" + +std::string read_from_file(const std::string& path) +{ + std::string contents; + std::ifstream is{path, std::ifstream::binary}; + + if (is) { + // get length of file: + is.seekg (0, is.end); + auto length = is.tellg(); + is.seekg (0, is.beg); + contents.resize(length); + + is.read(&contents[0], length); + if (!is) { + is.close(); + return {}; + } + } + + is.close(); + return contents; +} + +TEST (ESAlgo, ES256EncodingDecodingTest) +{ + using namespace jwt::params; + + std::string key = read_from_file(EC384_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("ES256"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862371) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + EXPECT_FALSE (ec); + + key = read_from_file(EC384_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"ES256"}), ec, verify(false), secret(key)); + EXPECT_FALSE (ec); + + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::ES256); + EXPECT_TRUE (dec_obj.has_claim("iss")); + EXPECT_TRUE (dec_obj.has_claim("aud")); + EXPECT_TRUE (dec_obj.has_claim("exp")); + + EXPECT_FALSE (dec_obj.has_claim("sub")); +} + +TEST (ESAlgo, ES384EncodingDecodingTest) +{ + using namespace jwt::params; + + std::string key = read_from_file(EC384_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("ES384"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862371) + ; + + auto enc_str = obj.signature(); + + key = read_from_file(EC384_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"ES384"}), verify(false), secret(key)); + + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::ES384); +} + +TEST (ESAlgo, ES512EncodingDecodingTest) +{ + using namespace jwt::params; + + std::string key = read_from_file(EC384_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("ES512"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862371) + ; + + auto enc_str = obj.signature(); + + key = read_from_file(EC384_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"ES512"}), verify(false), secret(key)); + + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::ES512); +} + +TEST (ESAlgo, ES384EncodingDecodingValidTest) +{ + using namespace jwt::params; + + std::string key = read_from_file(EC384_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("ES384"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 4682665886) // Expires on Sunday, May 22, 2118 12:31:26 PM GMT + ; + + auto enc_str = obj.signature(); + + key = read_from_file(EC384_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"ES384"}), verify(true), secret(key)); + + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::ES384); + EXPECT_TRUE (dec_obj.has_claim("exp")); + EXPECT_TRUE (obj.payload().has_claim_with_value("exp", 4682665886)); + + std::map keystore{{"arun.muralidharan", key}}; + + auto l = [&keystore](const jwt::jwt_payload& payload){ + auto iss = payload.get_claim_value("iss"); + return keystore[iss]; + }; + auto dec_obj2 = jwt::decode(enc_str, algorithms({"ES384"}), verify(true), secret(l)); + EXPECT_EQ (dec_obj2.header().algo(), jwt::algorithm::ES384); +} + diff --git a/externals/cpp-jwt/tests/test_jwt_object.cc b/externals/cpp-jwt/tests/test_jwt_object.cc new file mode 100755 index 000000000..ac9cdad07 --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_object.cc @@ -0,0 +1,30 @@ +#include "gtest/gtest.h" +#include "jwt/jwt.hpp" + +namespace { + +struct Wrapper +{ + // The std::move here is required to resolve to the move ctor + // rather than to the universal reference ctor. + Wrapper(jwt::jwt_object&& obj) : object{std::move(obj)} {} + jwt::jwt_object object; +}; + +} // END namespace + +TEST (ObjectTest, MoveConstructor) +{ + using namespace jwt::params; + + jwt::jwt_object obj{algorithm("HS256"), secret("secret")}; + + obj.add_claim("iss", "arun.muralidharan"); + + auto wrapper = Wrapper{std::move(obj)}; + + EXPECT_EQ(wrapper.object.header().algo(), jwt::algorithm::HS256); + EXPECT_EQ(wrapper.object.secret(), "secret"); + EXPECT_TRUE(wrapper.object.payload().has_claim_with_value("iss", "arun.muralidharan")); +} + diff --git a/externals/cpp-jwt/tests/test_jwt_rsa.cc b/externals/cpp-jwt/tests/test_jwt_rsa.cc new file mode 100755 index 000000000..de46f8c1b --- /dev/null +++ b/externals/cpp-jwt/tests/test_jwt_rsa.cc @@ -0,0 +1,145 @@ +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "jwt/jwt.hpp" + +#define RSA256_PUB_KEY CERT_ROOT_DIR "/rsa_certs/rsa256_pub.pem" +#define RSA256_PRIV_KEY CERT_ROOT_DIR "/rsa_certs/rsa256_priv.pem" +#define RSA384_PUB_KEY CERT_ROOT_DIR "/rsa_certs/rsa384_pub.pem" +#define RSA384_PRIV_KEY CERT_ROOT_DIR "/rsa_certs/rsa384_priv.pem" +#define RSA512_PUB_KEY CERT_ROOT_DIR "/rsa_certs/rsa512_pub.pem" +#define RSA512_PRIV_KEY CERT_ROOT_DIR "/rsa_certs/rsa512_priv.pem" + +std::string read_from_file(const std::string& path) +{ + std::string contents; + std::ifstream is{path, std::ifstream::binary}; + + if (is) { + // get length of file: + is.seekg (0, is.end); + auto length = is.tellg(); + is.seekg (0, is.beg); + contents.resize(length); + + is.read(&contents[0], length); + if (!is) { + is.close(); + return {}; + } + } + + is.close(); + return contents; +} + +TEST (RSAAlgo, RSA256EncodingDecodingTest) +{ + using namespace jwt::params; + + const char* expected_sign = + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhbGwiLCJleHAiOjE1MTM4NjIzNzEsImlzcyI6ImFydW4ubXVyYWxpZGhhcmFuIn0.jr-Nrny0yGFuIUH8zHLuxpGH5aClwQVin2As2ISsgclu-9IDi1cVCtloIUNRb_ock6X7X41FtGMA_lt_T9wGyLmMzNf4Vu7OPBGfzjEdCHKD8OgcvI0Z4qw7_TFuXEuNSnbwkYFZ9S2g8uPzO0raVk4aIuczo58btwEDrsoE7TNBMTHjfL92zZ90YcFqW5WZKn9Y_dF1rb5UXARF6YSzzVjaNC86FWUl86wwo9cir0nxVPD4zKol_x2xyiP6n4n-sUX0_dM_-KMSfDqdr34quq3ZxcP5vjT-8FWb4t_IWHBmLrNsjS1so9a_5u7vcSBX1llX9Vgztv0zB7B8rEkFTw"; + + std::string key = read_from_file(RSA256_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("RS256"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862371) + ; + + std::error_code ec; + auto enc_str = obj.signature(ec); + EXPECT_FALSE (ec); + + EXPECT_EQ (enc_str, expected_sign); + + //Decode + key = read_from_file(RSA256_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"RS256"}), ec, verify(false), secret(key)); + EXPECT_FALSE (ec); +} + +TEST (RSAAlgo, RSA384EncodingDecodingTest) +{ + using namespace jwt::params; + + std::string key = read_from_file(RSA384_PRIV_KEY); + ASSERT_TRUE (key.length()); + + const char* expected_str = + "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhbGwiLCJleHAiOjE1MTM4NjIzNzIsImlzcyI6ImFydW4ubXVyYWxpZGhhcmFuIn0.iXQyGTmHAjdfXXgcMZn31xqv05h8Qoa3GGlSF5-42kPkd6iLPWzxky15FFW8qkvO-DiXDpOM4BoDANYCKNTSOToyuhCZ6dn_WH8RQzU6KOqRccYe2Fgvo7XnrgE_iHIMYPejc2kAUh1xLpE31WCU2P1afo2KN_-DV7kCmDJY6qpFtCctbbPNOhv6XbYpQlTblZeYDh1HVO--KWuhYl17kgjj3W-3fEoQjgaiprZ_JsTxRTN05aGT_AY15-FW0jPgPPBw5FnIX6P-j18F3BrG-lji7BuNrvyCUT3ZX35yBkBv9Ri5B3SLALy2bD0qGGE_G9_Orfm9yU9oQySLMO1qLiMbKLakLB5kMSy049C2Pdx9Nz47hqQWOHOWNRGwwTkKAwjeu1dTjv14QOmLcefM6GoXoCMZaFcmEqr63CgyLrnlsVS6vLkazyWcKD6eg51vPa8Rnn1V5u1EgNNnT6nU6iZ9_POJcf9_s-7HNpAXtlckia-OIrdLG-5cm93h1rAfVois43m0EwNtTr_DZ2JDtM9BifaS5MsktztUjrh1hjF5vDLBQc8vAYX0YbWOx_0NTn0aRYzOZ9kIhFxkaY320h8AS_7iFa5sA-ygeJdR-EvdlUZcoRzPzQFkrtatK-UE_VlSisUCsqoxHefx799aNjqz4FDLcyQRekdmVMb8Ew8"; + + jwt::jwt_object obj{algorithm("RS384"), secret(key)}; + + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862372) + ; + + auto enc_str = obj.signature(); + EXPECT_EQ (enc_str, expected_str); + + //Decode + key = read_from_file(RSA384_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"none", "HS384", "RS384"}), verify(false), secret(key)); + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::RS384); +} + +TEST (RSAAlgo, RSA512EncodingDecodingTest) +{ + using namespace jwt::params; + + const char* expected_str = + "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhbGwiLCJleHAiOjE1MTM4NjIzNzIsImlzcyI6ImFydW4ubXVyYWxpZGhhcmFuIn0.ZNkLnf565-NFfxkbosJra1CJEgCLFf0jmgb7Q8eZrzxIrE4C4dOjpGD13f0jm2FqidUxAvFVrHI2ahhZi4Bu65qQtV4mVVftiD0qTaYzh26ql0MFqTKYEeKtU0kFXAzH7f9689z7mQ2n8aw7H8WHrfe17ub19Xyj-MirCECcWjcuGWBhsdz0y-dKy_GJYnpf8mHvmQAjkH5ynUV5NXHIBDO6eKssxX36Ow9_KYZ1HrCCUT_B-TQfNrnHAJgCydO-cX9iaAxV5aKvOdMGopHz14fX4oI9qH4aBzcroRbs77UsJZ-CMoRnUoXQP7DPORvEEUOQepANu9gqmymfJin8oEDotlj7eoJkFD3j64dkMT2bnRe8k2akPgUiDTeIrvNBuOIMDJtekoVpTo0fytveeDVPpDli9uX6DkJW1GGFLSRR-J-I8WbKRMKadmKOpDn3LF71hOo2mcXAsSwleFi9xB39bLBKJcqL_DtBoZBt0QSqMs6nRVj1U-3vYtuaa_eM3TfxhWWPZULaGVaVfpefRGdqtjrU0z5oO_vjviYujXK5_vM8zTroLVEaOyJYCeh2h_5N5LaOlf8BDu2PF3epNuCmM7G2PWEH7aPn5o-vvKTg_SM32jJXbXp2gkplEdQIWFh3jtjcRe9wNa9aPJE3I1hn1ZbqiAGUzBLWYUYpvstWXGbmxOoh6FkNJERb2hOIZgGLMvwWZXUU3GICcI5DMFOdDsuANpLg5FygsQ68JpuqKrUxu1Yh55--GHuDI7tqdHsPhPUzTmZrSvRog0w07dUAZCIBsGsSLX3wViobWbpVuY4pB7KXGIfdXgLfLgcERe_CxtnoPGF36zsqBflSXcqXwJ4qRK6BpTvKyUXf6pWEWOnuKomk8aENbT6nTr7naRJb5L3J4zhE-5O_Yetw9aCTzy9vN8a22n0JHXeroAwTpLR_wsQwDPwN-K99JVUKwR-FvOkJhE7_wwbUXmjiacKjXrwQ0OWnhXigQRLfdHG2OyH6_It5dpBmBOyWx2X-tfQ6Wz-_2bKCALl487Amq56hhNJhbQuJFIR59RylVAWKmfeeno2qcTZgrI_mO3PJCCUxBn5hK81HJuOtZ4YmeDHPvLW8Tiv5KqfRMWJKhyFthB74FvUINiEn0jvbuLR3YuyTgpf22lohT4-mHq5FrEd3plGvj0fVI_zeGhAFBhQYMW-MAJo7oylTOMtSZ1JHHuvBPR6FvMTgaPTAum6Dsl-I4_O_OKgtgovefBgwh4TOm_vsJmjVYFRr0Eo3OqsfNw3OwSKnuv5I76thh6DN879UZiyJG_7lcz_L6d0g4fGCvdM45zgQp3U3l8fJN1MRYCx5mxJAYeVlnCpmqueuww"; + + std::string key = read_from_file(RSA512_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("RS512"), secret(key)}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862372) + ; + + auto enc_str = obj.signature(); + EXPECT_EQ (enc_str, expected_str); + + + key = read_from_file(RSA512_PUB_KEY); + ASSERT_TRUE (key.length()); + + auto dec_obj = jwt::decode(enc_str, algorithms({"none", "HS384", "RS512"}), verify(false), secret(key)); + EXPECT_EQ (dec_obj.header().algo(), jwt::algorithm::RS512); +} + +TEST (RSAAlgo, NoSpecificAlgo) +{ + using namespace jwt::params; + + std::string key = read_from_file(RSA512_PRIV_KEY); + ASSERT_TRUE (key.length()); + + jwt::jwt_object obj{algorithm("RS512"), secret(key)}; + obj.add_claim("iss", "arun.muralidharan") + .add_claim("aud", "all") + .add_claim("exp", 1513862372) + ; + + auto enc_str = obj.signature(); + key = read_from_file(RSA512_PUB_KEY); + ASSERT_TRUE (key.length()); + + EXPECT_THROW (jwt::decode(enc_str, algorithms({"none", "HS384", "RS384"}), verify(true), secret(key)), + jwt::InvalidAlgorithmError); +} + diff --git a/externals/cpp-jwt/vcpkg.json b/externals/cpp-jwt/vcpkg.json new file mode 100755 index 000000000..a8a3bedc5 --- /dev/null +++ b/externals/cpp-jwt/vcpkg.json @@ -0,0 +1,11 @@ +{ + "name": "cpp-jwt", + "version": "1.5", + "description": "JSON Web Token library for C++", + "homepage": "https://github.com/arun11299/cpp-jwt", + "dependencies": [ + "nlohmann-json", + "openssl", + "gtest" + ] +} diff --git a/externals/enet/CMakeLists.txt b/externals/enet/CMakeLists.txt new file mode 100755 index 000000000..d3d4aa8de --- /dev/null +++ b/externals/enet/CMakeLists.txt @@ -0,0 +1,94 @@ +cmake_minimum_required(VERSION 2.6) + +project(enet) + +# The "configure" step. +include(CheckFunctionExists) +include(CheckStructHasMember) +include(CheckTypeSize) +check_function_exists("fcntl" HAS_FCNTL) +check_function_exists("poll" HAS_POLL) +check_function_exists("getaddrinfo" HAS_GETADDRINFO) +check_function_exists("getnameinfo" HAS_GETNAMEINFO) +check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R) +check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R) +check_function_exists("inet_pton" HAS_INET_PTON) +check_function_exists("inet_ntop" HAS_INET_NTOP) +check_struct_has_member("struct msghdr" "msg_flags" "sys/types.h;sys/socket.h" HAS_MSGHDR_FLAGS) +set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h" "sys/socket.h") +check_type_size("socklen_t" HAS_SOCKLEN_T BUILTIN_TYPES_ONLY) +unset(CMAKE_EXTRA_INCLUDE_FILES) +if(MSVC) + add_definitions(-W3) +else() + add_definitions(-Wno-error) +endif() + +if(HAS_FCNTL) + add_definitions(-DHAS_FCNTL=1) +endif() +if(HAS_POLL) + add_definitions(-DHAS_POLL=1) +endif() +if(HAS_GETNAMEINFO) + add_definitions(-DHAS_GETNAMEINFO=1) +endif() +if(HAS_GETADDRINFO) + add_definitions(-DHAS_GETADDRINFO=1) +endif() +if(HAS_GETHOSTBYNAME_R) + add_definitions(-DHAS_GETHOSTBYNAME_R=1) +endif() +if(HAS_GETHOSTBYADDR_R) + add_definitions(-DHAS_GETHOSTBYADDR_R=1) +endif() +if(HAS_INET_PTON) + add_definitions(-DHAS_INET_PTON=1) +endif() +if(HAS_INET_NTOP) + add_definitions(-DHAS_INET_NTOP=1) +endif() +if(HAS_MSGHDR_FLAGS) + add_definitions(-DHAS_MSGHDR_FLAGS=1) +endif() +if(HAS_SOCKLEN_T) + add_definitions(-DHAS_SOCKLEN_T=1) +endif() + +include_directories(${PROJECT_SOURCE_DIR}/include) + +set(INCLUDE_FILES_PREFIX include/enet) +set(INCLUDE_FILES + ${INCLUDE_FILES_PREFIX}/callbacks.h + ${INCLUDE_FILES_PREFIX}/enet.h + ${INCLUDE_FILES_PREFIX}/list.h + ${INCLUDE_FILES_PREFIX}/protocol.h + ${INCLUDE_FILES_PREFIX}/time.h + ${INCLUDE_FILES_PREFIX}/types.h + ${INCLUDE_FILES_PREFIX}/unix.h + ${INCLUDE_FILES_PREFIX}/utility.h + ${INCLUDE_FILES_PREFIX}/win32.h +) + +set(SOURCE_FILES + callbacks.c + compress.c + host.c + list.c + packet.c + peer.c + protocol.c + unix.c + win32.c) + +source_group(include FILES ${INCLUDE_FILES}) +source_group(source FILES ${SOURCE_FILES}) + +add_library(enet STATIC + ${INCLUDE_FILES} + ${SOURCE_FILES} +) + +if (MINGW) + target_link_libraries(enet winmm ws2_32) +endif() diff --git a/externals/enet/ChangeLog b/externals/enet/ChangeLog new file mode 100755 index 000000000..663c7b731 --- /dev/null +++ b/externals/enet/ChangeLog @@ -0,0 +1,179 @@ +* use getaddrinfo and getnameinfo where available + +ENet 1.3.13 (April 30, 2015): + +* miscellaneous bug fixes +* added premake and cmake support +* miscellaneous documentation cleanups + +ENet 1.3.12 (April 24, 2014): + +* added maximumPacketSize and maximumWaitingData fields to ENetHost to limit the amount of +data waiting to be delivered on a peer (beware that the default maximumPacketSize is +32MB and should be set higher if desired as should maximumWaitingData) + +ENet 1.3.11 (December 26, 2013): + +* allow an ENetHost to connect to itself +* fixed possible bug with disconnect notifications during connect attempts +* fixed some preprocessor definition bugs + +ENet 1.3.10 (October 23, 2013); + +* doubled maximum reliable window size +* fixed RCVTIMEO/SNDTIMEO socket options and also added NODELAY + +ENet 1.3.9 (August 19, 2013): + +* added duplicatePeers option to ENetHost which can limit the number of peers from duplicate IPs +* added enet_socket_get_option() and ENET_SOCKOPT_ERROR +* added enet_host_random_seed() platform stub + +ENet 1.3.8 (June 2, 2013): + +* added enet_linked_version() for checking the linked version +* added enet_socket_get_address() for querying the local address of a socket +* silenced some debugging prints unless ENET_DEBUG is defined during compilation +* handle EINTR in enet_socket_wait() so that enet_host_service() doesn't propagate errors from signals +* optimized enet_host_bandwidth_throttle() to be less expensive for large numbers of peers + +ENet 1.3.7 (March 6, 2013): + +* added ENET_PACKET_FLAG_SENT to indicate that a packet is being freed because it has been sent +* added userData field to ENetPacket +* changed how random seed is generated on Windows to avoid import warnings +* fixed case where disconnects could be generated with no preceding connect event + +ENet 1.3.6 (December 11, 2012): + +* added support for intercept callback in ENetHost that can be used to process raw packets before ENet +* added enet_socket_shutdown() for issuing shutdown on a socket +* fixed enet_socket_connect() to not error on non-blocking connects +* fixed bug in MTU negotiation during connections + +ENet 1.3.5 (July 31, 2012): + +* fixed bug in unreliable packet fragment queuing + +ENet 1.3.4 (May 29, 2012): + +* added enet_peer_ping_interval() for configuring per-peer ping intervals +* added enet_peer_timeout() for configuring per-peer timeouts +* added protocol packet size limits + +ENet 1.3.3 (June 28, 2011): + +* fixed bug with simultaneous disconnects not dispatching events + +ENet 1.3.2 (May 31, 2011): + +* added support for unreliable packet fragmenting via the packet flag +ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT +* fixed regression in unreliable packet queuing +* added check against received port to limit some forms of IP-spoofing + +ENet 1.3.1 (February 10, 2011): + +* fixed bug in tracking of reliable data in transit +* reliable data window size now scales with the throttle +* fixed bug in fragment length calculation when checksums are used + +ENet 1.3.0 (June 5, 2010): + +* enet_host_create() now requires the channel limit to be specified as +a parameter +* enet_host_connect() now accepts a data parameter which is supplied +to the receiving receiving host in the event data field for a connect event +* added an adaptive order-2 PPM range coder as a built-in compressor option +which can be set with enet_host_compress_with_range_coder() +* added support for packet compression configurable with a callback +* improved session number handling to not rely on the packet checksum +field, saving 4 bytes per packet unless the checksum option is used +* removed the dependence on the rand callback for session number handling + +Caveats: This version is not protocol compatible with the 1.2 series or +earlier. The enet_host_connect and enet_host_create API functions require +supplying additional parameters. + +ENet 1.2.5 (June 28, 2011): + +* fixed bug with simultaneous disconnects not dispatching events + +ENet 1.2.4 (May 31, 2011): + +* fixed regression in unreliable packet queuing +* added check against received port to limit some forms of IP-spoofing + +ENet 1.2.3 (February 10, 2011): + +* fixed bug in tracking reliable data in transit + +ENet 1.2.2 (June 5, 2010): + +* checksum functionality is now enabled by setting a checksum callback +inside ENetHost instead of being a configure script option +* added totalSentData, totalSentPackets, totalReceivedData, and +totalReceivedPackets counters inside ENetHost for getting usage +statistics +* added enet_host_channel_limit() for limiting the maximum number of +channels allowed by connected peers +* now uses dispatch queues for event dispatch rather than potentially +unscalable array walking +* added no_memory callback that is called when a malloc attempt fails, +such that if no_memory returns rather than aborts (the default behavior), +then the error is propagated to the return value of the API calls +* now uses packed attribute for protocol structures on platforms with +strange alignment rules +* improved autoconf build system contributed by Nathan Brink allowing +for easier building as a shared library + +Caveats: If you were using the compile-time option that enabled checksums, +make sure to set the checksum callback inside ENetHost to enet_crc32 to +regain the old behavior. The ENetCallbacks structure has added new fields, +so make sure to clear the structure to zero before use if +using enet_initialize_with_callbacks(). + +ENet 1.2.1 (November 12, 2009): + +* fixed bug that could cause disconnect events to be dropped +* added thin wrapper around select() for portable usage +* added ENET_SOCKOPT_REUSEADDR socket option +* factored enet_socket_bind()/enet_socket_listen() out of enet_socket_create() +* added contributed Code::Blocks build file + +ENet 1.2 (February 12, 2008): + +* fixed bug in VERIFY_CONNECT acknowledgement that could cause connect +attempts to occasionally timeout +* fixed acknowledgements to check both the outgoing and sent queues +when removing acknowledged packets +* fixed accidental bit rot in the MSVC project file +* revised sequence number overflow handling to address some possible +disconnect bugs +* added enet_host_check_events() for getting only local queued events +* factored out socket option setting into enet_socket_set_option() so +that socket options are now set separately from enet_socket_create() + +Caveats: While this release is superficially protocol compatible with 1.1, +differences in the sequence number overflow handling can potentially cause +random disconnects. + +ENet 1.1 (June 6, 2007): + +* optional CRC32 just in case someone needs a stronger checksum than UDP +provides (--enable-crc32 configure option) +* the size of packet headers are half the size they used to be (so less +overhead when sending small packets) +* enet_peer_disconnect_later() that waits till all queued outgoing +packets get sent before issuing an actual disconnect +* freeCallback field in individual packets for notification of when a +packet is about to be freed +* ENET_PACKET_FLAG_NO_ALLOCATE for supplying pre-allocated data to a +packet (can be used in concert with freeCallback to support some custom +allocation schemes that the normal memory allocation callbacks would +normally not allow) +* enet_address_get_host_ip() for printing address numbers +* promoted the enet_socket_*() functions to be part of the API now +* a few stability/crash fixes + + diff --git a/externals/enet/Doxyfile b/externals/enet/Doxyfile new file mode 100755 index 000000000..597ef1af1 --- /dev/null +++ b/externals/enet/Doxyfile @@ -0,0 +1,2303 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "ENet" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = v1.3.13 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Reliable UDP networking library" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = YES + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = DoxygenLayout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c *.h *.dox + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = ${CMAKE_CURRENT_SOURCE_DIR} + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = NO + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 1 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 118 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 240 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 0 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = YES + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /Documentation + + */ + +/** +@page Downloads Downloads + +You can retrieve the source to ENet by downloading it in either .tar.gz form +or accessing the github distribution directly. + +The most recent stable release (1.3.13) can be downloaded here. +The last release that is protocol compatible with the 1.2 series or earlier (1.2.5) can be downloaded here. + +You can find the most recent ENet source at the github repository. + +*/ + +/** +@page MailingList Mailing List + +The enet-discuss list is for discussion of ENet, including bug reports or feature requests. + +*/ + +/** +@page IRCChannel IRC Channel + +Join the \#enet channel on the freenode IRC network (irc.freenode.net) for real-time discussion about the ENet library. + +*/ + diff --git a/externals/enet/docs/tutorial.dox b/externals/enet/docs/tutorial.dox new file mode 100755 index 000000000..e91eae825 --- /dev/null +++ b/externals/enet/docs/tutorial.dox @@ -0,0 +1,366 @@ +/** +@page Tutorial Tutorial + +@ref Initialization + +@ref CreateServer + +@ref CreateClient + +@ref ManageHost + +@ref SendingPacket + +@ref Disconnecting + +@ref Connecting + +@section Initialization Initialization + +You should include the file when using ENet. Do not +include without the directory prefix, as this may cause +file name conflicts on some systems. + +Before using ENet, you must call enet_initialize() to initialize the +library. Upon program exit, you should call enet_deinitialize() so +that the library may clean up any used resources. + +@code +#include + +int +main (int argc, char ** argv) +{ + if (enet_initialize () != 0) + { + fprintf (stderr, "An error occurred while initializing ENet.\n"); + return EXIT_FAILURE; + } + atexit (enet_deinitialize); + ... + ... + ... +} +@endcode + +@section CreateServer Creating an ENet server + +Servers in ENet are constructed with enet_host_create(). You must +specify an address on which to receive data and new connections, as +well as the maximum allowable numbers of connected peers. You may +optionally specify the incoming and outgoing bandwidth of the server +in bytes per second so that ENet may try to statically manage +bandwidth resources among connected peers in addition to its dynamic +throttling algorithm; specifying 0 for these two options will cause +ENet to rely entirely upon its dynamic throttling algorithm to manage +bandwidth. + +When done with a host, the host may be destroyed with +enet_host_destroy(). All connected peers to the host will be reset, +and the resources used by the host will be freed. + +@code + ENetAddress address; + ENetHost * server; + + /* Bind the server to the default localhost. */ + /* A specific host address can be specified by */ + /* enet_address_set_host (& address, "x.x.x.x"); */ + + address.host = ENET_HOST_ANY; + /* Bind the server to port 1234. */ + address.port = 1234; + + server = enet_host_create (& address /* the address to bind the server host to */, + 32 /* allow up to 32 clients and/or outgoing connections */, + 2 /* allow up to 2 channels to be used, 0 and 1 */, + 0 /* assume any amount of incoming bandwidth */, + 0 /* assume any amount of outgoing bandwidth */); + if (server == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet server host.\n"); + exit (EXIT_FAILURE); + } + ... + ... + ... + enet_host_destroy(server); +@endcode + +@section CreateClient Creating an ENet client + +Clients in ENet are similarly constructed with enet_host_create() when +no address is specified to bind the host to. Bandwidth may be +specified for the client host as in the above example. The peer count +controls the maximum number of connections to other server hosts that +may be simultaneously open. + +@code + ENetHost * client; + + client = enet_host_create (NULL /* create a client host */, + 1 /* only allow 1 outgoing connection */, + 2 /* allow up 2 channels to be used, 0 and 1 */, + 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, + 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); + + if (client == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet client host.\n"); + exit (EXIT_FAILURE); + } + ... + ... + ... + enet_host_destroy(client); +@endcode + +@section ManageHost Managing an ENet host + +ENet uses a polled event model to notify the programmer of significant +events. ENet hosts are polled for events with enet_host_service(), +where an optional timeout value in milliseconds may be specified to +control how long ENet will poll; if a timeout of 0 is specified, +enet_host_service() will return immediately if there are no events to +dispatch. enet_host_service() will return 1 if an event was dispatched +within the specified timeout. + +Beware that most processing of the network with the ENet stack is done +inside enet_host_service(). Both hosts that make up the sides of a connection +must regularly call this function to ensure packets are actually sent and +received. A common symptom of not actively calling enet_host_service() +on both ends is that one side receives events while the other does not. +The best way to schedule this activity to ensure adequate service is, for +example, to call enet_host_service() with a 0 timeout (meaning non-blocking) +at the beginning of every frame in a game loop. + +Currently there are only four types of significant events in ENet: + +An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred +within the specified time limit. enet_host_service() will return 0 +with this event. + +An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client +host has connected to the server host or when an attempt to establish a +connection with a foreign host has succeeded. Only the "peer" field of the +event structure is valid for this event and contains the newly connected peer. + +An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received +from a connected peer. The "peer" field contains the peer the packet was +received from, "channelID" is the channel on which the packet was sent, and +"packet" is the packet that was sent. The packet contained in the "packet" +field must be destroyed with enet_packet_destroy() when you are done +inspecting its contents. + +An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer +has either explicitly disconnected or timed out. Only the "peer" field of the +event structure is valid for this event and contains the peer that +disconnected. Only the "data" field of the peer is still valid on a +disconnect event and must be explicitly reset. + +@code + ENetEvent event; + + /* Wait up to 1000 milliseconds for an event. */ + while (enet_host_service (client, & event, 1000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf ("A new client connected from %x:%u.\n", + event.peer -> address.host, + event.peer -> address.port); + + /* Store any relevant client information here. */ + event.peer -> data = "Client information"; + + break; + + case ENET_EVENT_TYPE_RECEIVE: + printf ("A packet of length %u containing %s was received from %s on channel %u.\n", + event.packet -> dataLength, + event.packet -> data, + event.peer -> data, + event.channelID); + + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy (event.packet); + + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("%s disconnected.\n", event.peer -> data); + + /* Reset the peer's client information. */ + + event.peer -> data = NULL; + } + } + ... + ... + ... +@endcode + +@section SendingPacket Sending a packet to an ENet peer + +Packets in ENet are created with enet_packet_create(), where the size +of the packet must be specified. Optionally, initial data may be +specified to copy into the packet. + +Certain flags may also be supplied to enet_packet_create() to control +various packet features: + +ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable +delivery. A reliable packet is guaranteed to be delivered, and a +number of retry attempts will be made until an acknowledgement is +received from the foreign host the packet is sent to. If a certain +number of retry attempts is reached without any acknowledgement, ENet +will assume the peer has disconnected and forcefully reset the +connection. If this flag is not specified, the packet is assumed an +unreliable packet, and no retry attempts will be made nor +acknowledgements generated. + +A packet may be resized (extended or truncated) with +enet_packet_resize(). + +A packet is sent to a foreign host with +enet_peer_send(). enet_peer_send() accepts a channel id over which to +send the packet to a given peer. Once the packet is handed over to +ENet with enet_peer_send(), ENet will handle its deallocation and +enet_packet_destroy() should not be used upon it. + +One may also use enet_host_broadcast() to send a packet to all +connected peers on a given host over a specified channel id, as with +enet_peer_send(). + +Queued packets will be sent on a call to enet_host_service(). +Alternatively, enet_host_flush() will send out queued packets without +dispatching any events. + +@code + /* Create a reliable packet of size 7 containing "packet\0" */ + ENetPacket * packet = enet_packet_create ("packet", + strlen ("packet") + 1, + ENET_PACKET_FLAG_RELIABLE); + + /* Extend the packet so and append the string "foo", so it now */ + /* contains "packetfoo\0" */ + enet_packet_resize (packet, strlen ("packetfoo") + 1); + strcpy (& packet -> data [strlen ("packet")], "foo"); + + /* Send the packet to the peer over channel id 0. */ + /* One could also broadcast the packet by */ + /* enet_host_broadcast (host, 0, packet); */ + enet_peer_send (peer, 0, packet); + ... + ... + ... + /* One could just use enet_host_service() instead. */ + enet_host_flush (host); +@endcode + +@section Disconnecting Disconnecting an ENet peer + +Peers may be gently disconnected with enet_peer_disconnect(). A +disconnect request will be sent to the foreign host, and ENet will +wait for an acknowledgement from the foreign host before finally +disconnecting. An event of type ENET_EVENT_TYPE_DISCONNECT will be +generated once the disconnection succeeds. Normally timeouts apply to +the disconnect acknowledgement, and so if no acknowledgement is +received after a length of time the peer will be forcefully +disconnected. + +enet_peer_reset() will forcefully disconnect a peer. The foreign host +will get no notification of a disconnect and will time out on the +foreign host. No event is generated. + +@code + ENetEvent event; + + enet_peer_disconnect (peer, 0); + + /* Allow up to 3 seconds for the disconnect to succeed + * and drop any packets received packets. + */ + while (enet_host_service (client, & event, 3000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy (event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + puts ("Disconnection succeeded."); + return; + ... + ... + ... + } + } + + /* We've arrived here, so the disconnect attempt didn't */ + /* succeed yet. Force the connection down. */ + enet_peer_reset (peer); + ... + ... + ... +@endcode + +@section Connecting Connecting to an ENet host + +A connection to a foreign host is initiated with enet_host_connect(). +It accepts the address of a foreign host to connect to, and the number +of channels that should be allocated for communication. If N channels +are allocated for use, their channel ids will be numbered 0 through +N-1. A peer representing the connection attempt is returned, or NULL +if there were no available peers over which to initiate the +connection. When the connection attempt succeeds, an event of type +ENET_EVENT_TYPE_CONNECT will be generated. If the connection attempt +times out or otherwise fails, an event of type +ENET_EVENT_TYPE_DISCONNECT will be generated. + +@code + ENetAddress address; + ENetEvent event; + ENetPeer *peer; + + /* Connect to some.server.net:1234. */ + enet_address_set_host (& address, "some.server.net"); + address.port = 1234; + + /* Initiate the connection, allocating the two channels 0 and 1. */ + peer = enet_host_connect (client, & address, 2, 0); + + if (peer == NULL) + { + fprintf (stderr, + "No available peers for initiating an ENet connection.\n"); + exit (EXIT_FAILURE); + } + + /* Wait up to 5 seconds for the connection attempt to succeed. */ + if (enet_host_service (client, & event, 5000) > 0 && + event.type == ENET_EVENT_TYPE_CONNECT) + { + puts ("Connection to some.server.net:1234 succeeded."); + ... + ... + ... + } + else + { + /* Either the 5 seconds are up or a disconnect event was */ + /* received. Reset the peer in the event the 5 seconds */ + /* had run out without any significant event. */ + enet_peer_reset (peer); + + puts ("Connection to some.server.net:1234 failed."); + } + ... + ... + ... +@endcode +*/ diff --git a/externals/enet/enet.dsp b/externals/enet/enet.dsp new file mode 100755 index 000000000..dce4537e5 --- /dev/null +++ b/externals/enet/enet.dsp @@ -0,0 +1,168 @@ +# Microsoft Developer Studio Project File - Name="enet" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=enet - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "enet.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "enet.mak" CFG="enet - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "enet - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "enet - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "enet - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /O2 /I "include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "enet - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /G6 /MTd /W3 /ZI /Od /I "include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "enet - Win32 Release" +# Name "enet - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\host.c +# End Source File +# Begin Source File + +SOURCE=.\list.c +# End Source File +# Begin Source File + +SOURCE=.\callbacks.c +# End Source File +# Begin Source File + +SOURCE=.\compress.c +# End Source File +# Begin Source File + +SOURCE=.\packet.c +# End Source File +# Begin Source File + +SOURCE=.\peer.c +# End Source File +# Begin Source File + +SOURCE=.\protocol.c +# End Source File +# Begin Source File + +SOURCE=.\unix.c +# End Source File +# Begin Source File + +SOURCE=.\win32.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\include\enet\enet.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\list.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\callbacks.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\protocol.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\time.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\types.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\unix.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\utility.h +# End Source File +# Begin Source File + +SOURCE=.\include\enet\win32.h +# End Source File +# End Group +# End Target +# End Project diff --git a/externals/enet/enet_dll.cbp b/externals/enet/enet_dll.cbp new file mode 100755 index 000000000..961274c76 --- /dev/null +++ b/externals/enet/enet_dll.cbp @@ -0,0 +1,86 @@ + + + + + + diff --git a/externals/enet/host.c b/externals/enet/host.c new file mode 100755 index 000000000..3be6c0922 --- /dev/null +++ b/externals/enet/host.c @@ -0,0 +1,492 @@ +/** + @file host.c + @brief ENet host management functions +*/ +#define ENET_BUILDING_LIB 1 +#include +#include "enet/enet.h" + +/** @defgroup host ENet host functions + @{ +*/ + +/** Creates a host for communicating to peers. + + @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. + @param peerCount the maximum number of peers that should be allocated for the host. + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT + @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + + @returns the host on success and NULL on failure + + @remarks ENet will strategically drop packets on specific sides of a connection between hosts + to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine + the window size of a connection which limits the amount of reliable packets that may be in transit + at any given time. +*/ +ENetHost * +enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + ENetHost * host; + ENetPeer * currentPeer; + + if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) + return NULL; + + host = (ENetHost *) enet_malloc (sizeof (ENetHost)); + if (host == NULL) + return NULL; + memset (host, 0, sizeof (ENetHost)); + + host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); + if (host -> peers == NULL) + { + enet_free (host); + + return NULL; + } + memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); + + host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); + if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) + { + if (host -> socket != ENET_SOCKET_NULL) + enet_socket_destroy (host -> socket); + + enet_free (host -> peers); + enet_free (host); + + return NULL; + } + + enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); + + if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0) + host -> address = * address; + + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> randomSeed = (enet_uint32) (size_t) host; + host -> randomSeed += enet_host_random_seed (); + host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); + host -> channelLimit = channelLimit; + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> bandwidthThrottleEpoch = 0; + host -> recalculateBandwidthLimits = 0; + host -> mtu = ENET_HOST_DEFAULT_MTU; + host -> peerCount = peerCount; + host -> commandCount = 0; + host -> bufferCount = 0; + host -> checksum = NULL; + host -> receivedAddress.host = ENET_HOST_ANY; + host -> receivedAddress.port = 0; + host -> receivedData = NULL; + host -> receivedDataLength = 0; + + host -> totalSentData = 0; + host -> totalSentPackets = 0; + host -> totalReceivedData = 0; + host -> totalReceivedPackets = 0; + + host -> connectedPeers = 0; + host -> bandwidthLimitedPeers = 0; + host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; + host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; + host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; + + host -> compressor.context = NULL; + host -> compressor.compress = NULL; + host -> compressor.decompress = NULL; + host -> compressor.destroy = NULL; + + host -> intercept = NULL; + + enet_list_clear (& host -> dispatchQueue); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + currentPeer -> host = host; + currentPeer -> incomingPeerID = currentPeer - host -> peers; + currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; + currentPeer -> data = NULL; + + enet_list_clear (& currentPeer -> acknowledgements); + enet_list_clear (& currentPeer -> sentReliableCommands); + enet_list_clear (& currentPeer -> sentUnreliableCommands); + enet_list_clear (& currentPeer -> outgoingReliableCommands); + enet_list_clear (& currentPeer -> outgoingUnreliableCommands); + enet_list_clear (& currentPeer -> dispatchedCommands); + + enet_peer_reset (currentPeer); + } + + return host; +} + +/** Destroys the host and all resources associated with it. + @param host pointer to the host to destroy +*/ +void +enet_host_destroy (ENetHost * host) +{ + ENetPeer * currentPeer; + + if (host == NULL) + return; + + enet_socket_destroy (host -> socket); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + enet_peer_reset (currentPeer); + } + + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + enet_free (host -> peers); + enet_free (host); +} + +/** Initiates a connection to a foreign host. + @param host host seeking the connection + @param address destination for the connection + @param channelCount number of channels to allocate + @param data user data supplied to the receiving host + @returns a peer representing the foreign host on success, NULL on failure + @remarks The peer returned will have not completed the connection until enet_host_service() + notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. +*/ +ENetPeer * +enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) +{ + ENetPeer * currentPeer; + ENetChannel * channel; + ENetProtocol command; + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + else + if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (currentPeer -> channels == NULL) + return NULL; + currentPeer -> channelCount = channelCount; + currentPeer -> state = ENET_PEER_STATE_CONNECTING; + currentPeer -> address = * address; + currentPeer -> connectID = ++ host -> randomSeed; + + if (host -> outgoingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (host -> outgoingBandwidth / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + command.connect.incomingSessionID = currentPeer -> incomingSessionID; + command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; + command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); + command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); + command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + command.connect.connectID = currentPeer -> connectID; + command.connect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); + + return currentPeer; +} + +/** Queues a packet to be sent to all peers associated with the host. + @param host host on which to broadcast the packet + @param channelID channel on which to broadcast + @param packet packet to broadcast +*/ +void +enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) +{ + ENetPeer * currentPeer; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + enet_peer_send (currentPeer, channelID, packet); + } + + if (packet -> referenceCount == 0) + enet_packet_destroy (packet); +} + +/** Sets the packet compressor the host should use to compress and decompress packets. + @param host host to enable or disable compression for + @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled +*/ +void +enet_host_compress (ENetHost * host, const ENetCompressor * compressor) +{ + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + if (compressor) + host -> compressor = * compressor; + else + host -> compressor.context = NULL; +} + +/** Limits the maximum allowed channels of future incoming connections. + @param host host to limit + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT +*/ +void +enet_host_channel_limit (ENetHost * host, size_t channelLimit) +{ + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> channelLimit = channelLimit; +} + + +/** Adjusts the bandwidth limits of a host. + @param host host to adjust + @param incomingBandwidth new incoming bandwidth + @param outgoingBandwidth new outgoing bandwidth + @remarks the incoming and outgoing bandwidth parameters are identical in function to those + specified in enet_host_create(). +*/ +void +enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> recalculateBandwidthLimits = 1; +} + +void +enet_host_bandwidth_throttle (ENetHost * host) +{ + enet_uint32 timeCurrent = enet_time_get (), + elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, + peersRemaining = (enet_uint32) host -> connectedPeers, + dataTotal = ~0, + bandwidth = ~0, + throttle = 0, + bandwidthLimit = 0; + int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0; + ENetPeer * peer; + ENetProtocol command; + + if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + return; + + host -> bandwidthThrottleEpoch = timeCurrent; + + if (peersRemaining == 0) + return; + + if (host -> outgoingBandwidth != 0) + { + dataTotal = 0; + bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + dataTotal += peer -> outgoingDataTotal; + } + } + + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + + if (dataTotal <= bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + enet_uint32 peerBandwidth; + + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidth == 0 || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; + if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) + continue; + + peer -> packetThrottleLimit = (peerBandwidth * + ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; + + if (peer -> packetThrottleLimit == 0) + peer -> packetThrottleLimit = 1; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> outgoingBandwidthThrottleEpoch = timeCurrent; + + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peerBandwidth; + dataTotal -= peerBandwidth; + } + } + + if (peersRemaining > 0) + { + if (dataTotal <= bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peer -> packetThrottleLimit = throttle; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + } + } + + if (host -> recalculateBandwidthLimits) + { + host -> recalculateBandwidthLimits = 0; + + peersRemaining = (enet_uint32) host -> connectedPeers; + bandwidth = host -> incomingBandwidth; + needsAdjustment = 1; + + if (bandwidth == 0) + bandwidthLimit = 0; + else + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + bandwidthLimit = bandwidth / peersRemaining; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidthThrottleEpoch == timeCurrent) + continue; + + if (peer -> outgoingBandwidth > 0 && + peer -> outgoingBandwidth >= bandwidthLimit) + continue; + + peer -> incomingBandwidthThrottleEpoch = timeCurrent; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peer -> outgoingBandwidth; + } + } + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + + if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); + else + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + } + } +} + +/** @} */ diff --git a/externals/enet/include/enet/callbacks.h b/externals/enet/include/enet/callbacks.h new file mode 100755 index 000000000..340a4a989 --- /dev/null +++ b/externals/enet/include/enet/callbacks.h @@ -0,0 +1,27 @@ +/** + @file callbacks.h + @brief ENet callbacks +*/ +#ifndef __ENET_CALLBACKS_H__ +#define __ENET_CALLBACKS_H__ + +#include + +typedef struct _ENetCallbacks +{ + void * (ENET_CALLBACK * malloc) (size_t size); + void (ENET_CALLBACK * free) (void * memory); + void (ENET_CALLBACK * no_memory) (void); +} ENetCallbacks; + +/** @defgroup callbacks ENet internal callbacks + @{ + @ingroup private +*/ +extern void * enet_malloc (size_t); +extern void enet_free (void *); + +/** @} */ + +#endif /* __ENET_CALLBACKS_H__ */ + diff --git a/externals/enet/include/enet/enet.h b/externals/enet/include/enet/enet.h new file mode 100755 index 000000000..c76245381 --- /dev/null +++ b/externals/enet/include/enet/enet.h @@ -0,0 +1,607 @@ +/** + @file enet.h + @brief ENet public header file +*/ +#ifndef __ENET_ENET_H__ +#define __ENET_ENET_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#ifdef _WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif + +#include "enet/types.h" +#include "enet/protocol.h" +#include "enet/list.h" +#include "enet/callbacks.h" + +#define ENET_VERSION_MAJOR 1 +#define ENET_VERSION_MINOR 3 +#define ENET_VERSION_PATCH 13 +#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) +#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF) +#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF) +#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF) +#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) + +typedef enet_uint32 ENetVersion; + +struct _ENetHost; +struct _ENetEvent; +struct _ENetPacket; + +typedef enum _ENetSocketType +{ + ENET_SOCKET_TYPE_STREAM = 1, + ENET_SOCKET_TYPE_DATAGRAM = 2 +} ENetSocketType; + +typedef enum _ENetSocketWait +{ + ENET_SOCKET_WAIT_NONE = 0, + ENET_SOCKET_WAIT_SEND = (1 << 0), + ENET_SOCKET_WAIT_RECEIVE = (1 << 1), + ENET_SOCKET_WAIT_INTERRUPT = (1 << 2) +} ENetSocketWait; + +typedef enum _ENetSocketOption +{ + ENET_SOCKOPT_NONBLOCK = 1, + ENET_SOCKOPT_BROADCAST = 2, + ENET_SOCKOPT_RCVBUF = 3, + ENET_SOCKOPT_SNDBUF = 4, + ENET_SOCKOPT_REUSEADDR = 5, + ENET_SOCKOPT_RCVTIMEO = 6, + ENET_SOCKOPT_SNDTIMEO = 7, + ENET_SOCKOPT_ERROR = 8, + ENET_SOCKOPT_NODELAY = 9 +} ENetSocketOption; + +typedef enum _ENetSocketShutdown +{ + ENET_SOCKET_SHUTDOWN_READ = 0, + ENET_SOCKET_SHUTDOWN_WRITE = 1, + ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 +} ENetSocketShutdown; + +#define ENET_HOST_ANY 0 +#define ENET_HOST_BROADCAST 0xFFFFFFFFU +#define ENET_PORT_ANY 0 + +/** + * Portable internet address structure. + * + * The host must be specified in network byte-order, and the port must be in host + * byte-order. The constant ENET_HOST_ANY may be used to specify the default + * server host. The constant ENET_HOST_BROADCAST may be used to specify the + * broadcast address (255.255.255.255). This makes sense for enet_host_connect, + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. + */ +typedef struct _ENetAddress +{ + enet_uint32 host; + enet_uint16 port; +} ENetAddress; + +/** + * Packet flag bit constants. + * + * The host must be specified in network byte-order, and the port must be in + * host byte-order. The constant ENET_HOST_ANY may be used to specify the + * default server host. + + @sa ENetPacket +*/ +typedef enum _ENetPacketFlag +{ + /** packet must be received by the target peer and resend attempts should be + * made until the packet is delivered */ + ENET_PACKET_FLAG_RELIABLE = (1 << 0), + /** packet will not be sequenced with other packets + * not supported for reliable packets + */ + ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), + /** packet will not allocate data, and user must supply it instead */ + ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), + /** packet will be fragmented using unreliable (instead of reliable) sends + * if it exceeds the MTU */ + ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), + + /** whether the packet has been sent from all queues it has been entered into */ + ENET_PACKET_FLAG_SENT = (1<<8) +} ENetPacketFlag; + +typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *); + +/** + * ENet packet structure. + * + * An ENet data packet that may be sent to or received from a peer. The shown + * fields should only be read and never modified. The data field contains the + * allocated data for the packet. The dataLength fields specifies the length + * of the allocated data. The flags field is either 0 (specifying no flags), + * or a bitwise-or of any combination of the following flags: + * + * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer + * and resend attempts should be made until the packet is delivered + * + * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets + * (not supported for reliable packets) + * + * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead + * + * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable + * (instead of reliable) sends if it exceeds the MTU + * + * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into + @sa ENetPacketFlag + */ +typedef struct _ENetPacket +{ + size_t referenceCount; /**< internal use only */ + enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ + enet_uint8 * data; /**< allocated data for packet */ + size_t dataLength; /**< length of data */ + ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ + void * userData; /**< application private data, may be freely modified */ +} ENetPacket; + +typedef struct _ENetAcknowledgement +{ + ENetListNode acknowledgementList; + enet_uint32 sentTime; + ENetProtocol command; +} ENetAcknowledgement; + +typedef struct _ENetOutgoingCommand +{ + ENetListNode outgoingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + enet_uint32 sentTime; + enet_uint32 roundTripTimeout; + enet_uint32 roundTripTimeoutLimit; + enet_uint32 fragmentOffset; + enet_uint16 fragmentLength; + enet_uint16 sendAttempts; + ENetProtocol command; + ENetPacket * packet; +} ENetOutgoingCommand; + +typedef struct _ENetIncomingCommand +{ + ENetListNode incomingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + ENetProtocol command; + enet_uint32 fragmentCount; + enet_uint32 fragmentsRemaining; + enet_uint32 * fragments; + ENetPacket * packet; +} ENetIncomingCommand; + +typedef enum _ENetPeerState +{ + ENET_PEER_STATE_DISCONNECTED = 0, + ENET_PEER_STATE_CONNECTING = 1, + ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, + ENET_PEER_STATE_CONNECTION_PENDING = 3, + ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, + ENET_PEER_STATE_CONNECTED = 5, + ENET_PEER_STATE_DISCONNECT_LATER = 6, + ENET_PEER_STATE_DISCONNECTING = 7, + ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, + ENET_PEER_STATE_ZOMBIE = 9 +} ENetPeerState; + +#ifndef ENET_BUFFER_MAXIMUM +#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) +#endif + +enum +{ + ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, + ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, + ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, + ENET_HOST_DEFAULT_MTU = 1400, + ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, + ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, + + ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, + ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, + ENET_PEER_PACKET_THROTTLE_SCALE = 32, + ENET_PEER_PACKET_THROTTLE_COUNTER = 7, + ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, + ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, + ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, + ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), + ENET_PEER_PACKET_LOSS_INTERVAL = 10000, + ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, + ENET_PEER_TIMEOUT_LIMIT = 32, + ENET_PEER_TIMEOUT_MINIMUM = 5000, + ENET_PEER_TIMEOUT_MAXIMUM = 30000, + ENET_PEER_PING_INTERVAL = 500, + ENET_PEER_UNSEQUENCED_WINDOWS = 64, + ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, + ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, + ENET_PEER_RELIABLE_WINDOWS = 16, + ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, + ENET_PEER_FREE_RELIABLE_WINDOWS = 8 +}; + +typedef struct _ENetChannel +{ + enet_uint16 outgoingReliableSequenceNumber; + enet_uint16 outgoingUnreliableSequenceNumber; + enet_uint16 usedReliableWindows; + enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS]; + enet_uint16 incomingReliableSequenceNumber; + enet_uint16 incomingUnreliableSequenceNumber; + ENetList incomingReliableCommands; + ENetList incomingUnreliableCommands; +} ENetChannel; + +/** + * An ENet peer which data packets may be sent or received from. + * + * No fields should be modified unless otherwise specified. + */ +typedef struct _ENetPeer +{ + ENetListNode dispatchList; + struct _ENetHost * host; + enet_uint16 outgoingPeerID; + enet_uint16 incomingPeerID; + enet_uint32 connectID; + enet_uint8 outgoingSessionID; + enet_uint8 incomingSessionID; + ENetAddress address; /**< Internet address of the peer */ + void * data; /**< Application private data, may be freely modified */ + ENetPeerState state; + ENetChannel * channels; + size_t channelCount; /**< Number of channels allocated for communication with peer */ + enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ + enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ + enet_uint32 incomingBandwidthThrottleEpoch; + enet_uint32 outgoingBandwidthThrottleEpoch; + enet_uint32 incomingDataTotal; + enet_uint32 outgoingDataTotal; + enet_uint32 lastSendTime; + enet_uint32 lastReceiveTime; + enet_uint32 nextTimeout; + enet_uint32 earliestTimeout; + enet_uint32 packetLossEpoch; + enet_uint32 packetsSent; + enet_uint32 packetsLost; + enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ + enet_uint32 packetLossVariance; + enet_uint32 packetThrottle; + enet_uint32 packetThrottleLimit; + enet_uint32 packetThrottleCounter; + enet_uint32 packetThrottleEpoch; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 packetThrottleInterval; + enet_uint32 pingInterval; + enet_uint32 timeoutLimit; + enet_uint32 timeoutMinimum; + enet_uint32 timeoutMaximum; + enet_uint32 lastRoundTripTime; + enet_uint32 lowestRoundTripTime; + enet_uint32 lastRoundTripTimeVariance; + enet_uint32 highestRoundTripTimeVariance; + enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ + enet_uint32 roundTripTimeVariance; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 reliableDataInTransit; + enet_uint16 outgoingReliableSequenceNumber; + ENetList acknowledgements; + ENetList sentReliableCommands; + ENetList sentUnreliableCommands; + ENetList outgoingReliableCommands; + ENetList outgoingUnreliableCommands; + ENetList dispatchedCommands; + int needsDispatch; + enet_uint16 incomingUnsequencedGroup; + enet_uint16 outgoingUnsequencedGroup; + enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; + enet_uint32 eventData; + size_t totalWaitingData; +} ENetPeer; + +/** An ENet packet compressor for compressing UDP packets before socket sends or receives. + */ +typedef struct _ENetCompressor +{ + /** Context data for the compressor. Must be non-NULL. */ + void * context; + /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ + void (ENET_CALLBACK * destroy) (void * context); +} ENetCompressor; + +/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ +typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount); + +/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ +typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event); + +/** An ENet host for communicating with peers. + * + * No fields should be modified unless otherwise stated. + + @sa enet_host_create() + @sa enet_host_destroy() + @sa enet_host_connect() + @sa enet_host_service() + @sa enet_host_flush() + @sa enet_host_broadcast() + @sa enet_host_compress() + @sa enet_host_compress_with_range_coder() + @sa enet_host_channel_limit() + @sa enet_host_bandwidth_limit() + @sa enet_host_bandwidth_throttle() + */ +typedef struct _ENetHost +{ + ENetSocket socket; + ENetAddress address; /**< Internet address of the host */ + enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ + enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ + enet_uint32 bandwidthThrottleEpoch; + enet_uint32 mtu; + enet_uint32 randomSeed; + int recalculateBandwidthLimits; + ENetPeer * peers; /**< array of peers allocated for this host */ + size_t peerCount; /**< number of peers allocated for this host */ + size_t channelLimit; /**< maximum number of channels allowed for connected peers */ + enet_uint32 serviceTime; + ENetList dispatchQueue; + int continueSending; + size_t packetSize; + enet_uint16 headerFlags; + ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; + size_t commandCount; + ENetBuffer buffers [ENET_BUFFER_MAXIMUM]; + size_t bufferCount; + ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ + ENetCompressor compressor; + enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU]; + ENetAddress receivedAddress; + enet_uint8 * receivedData; + size_t receivedDataLength; + enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ + ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ + size_t connectedPeers; + size_t bandwidthLimitedPeers; + size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */ + size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */ + size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */ +} ENetHost; + +/** + * An ENet event type, as specified in @ref ENetEvent. + */ +typedef enum _ENetEventType +{ + /** no event occurred within the specified time limit */ + ENET_EVENT_TYPE_NONE = 0, + + /** a connection request initiated by enet_host_connect has completed. + * The peer field contains the peer which successfully connected. + */ + ENET_EVENT_TYPE_CONNECT = 1, + + /** a peer has disconnected. This event is generated on a successful + * completion of a disconnect initiated by enet_peer_disconnect, if + * a peer has timed out, or if a connection request intialized by + * enet_host_connect has timed out. The peer field contains the peer + * which disconnected. The data field contains user supplied data + * describing the disconnection, or 0, if none is available. + */ + ENET_EVENT_TYPE_DISCONNECT = 2, + + /** a packet has been received from a peer. The peer field specifies the + * peer which sent the packet. The channelID field specifies the channel + * number upon which the packet was received. The packet field contains + * the packet that was received; this packet must be destroyed with + * enet_packet_destroy after use. + */ + ENET_EVENT_TYPE_RECEIVE = 3 +} ENetEventType; + +/** + * An ENet event as returned by enet_host_service(). + + @sa enet_host_service + */ +typedef struct _ENetEvent +{ + ENetEventType type; /**< type of the event */ + ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ + enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ + enet_uint32 data; /**< data associated with the event, if appropriate */ + ENetPacket * packet; /**< packet associated with the event, if appropriate */ +} ENetEvent; + +/** @defgroup global ENet global functions + @{ +*/ + +/** + Initializes ENet globally. Must be called prior to using any functions in + ENet. + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize (void); + +/** + Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. + + @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use + @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits); + +/** + Shuts down ENet globally. Should be called when a program that has + initialized ENet exits. +*/ +ENET_API void enet_deinitialize (void); + +/** + Gives the linked version of the ENet library. + @returns the version number +*/ +ENET_API ENetVersion enet_linked_version (void); + +/** @} */ + +/** @defgroup private ENet private implementation functions */ + +/** + Returns the wall-time in milliseconds. Its initial value is unspecified + unless otherwise set. + */ +ENET_API enet_uint32 enet_time_get (void); +/** + Sets the current wall-time in milliseconds. + */ +ENET_API void enet_time_set (enet_uint32); + +/** @defgroup socket ENet socket functions + @{ +*/ +ENET_API ENetSocket enet_socket_create (ENetSocketType); +ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *); +ENET_API int enet_socket_listen (ENetSocket, int); +ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *); +ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); +ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t); +ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32); +ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int); +ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *); +ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown); +ENET_API void enet_socket_destroy (ENetSocket); +ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); + +/** @} */ + +/** @defgroup Address ENet address functions + @{ +*/ + +/** Attempts to parse the printable form of the IP address in the parameter hostName + and sets the host field in the address parameter if successful. + @param address destination to store the parsed IP address + @param hostName IP address to parse + @retval 0 on success + @retval < 0 on failure + @returns the address of the given hostName in address on success +*/ +ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostName); + +/** Attempts to resolve the host named by the parameter hostName and sets + the host field in the address parameter if successful. + @param address destination to store resolved address + @param hostName host name to lookup + @retval 0 on success + @retval < 0 on failure + @returns the address of the given hostName in address on success +*/ +ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); + +/** Gives the printable form of the IP address specified in the address parameter. + @param address address printed + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength); + +/** Attempts to do a reverse lookup of the host field in the address parameter. + @param address address used for reverse lookup + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength); + +/** @} */ + +ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32); +ENET_API void enet_packet_destroy (ENetPacket *); +ENET_API int enet_packet_resize (ENetPacket *, size_t); +ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t); + +ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); +ENET_API void enet_host_destroy (ENetHost *); +ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32); +ENET_API int enet_host_check_events (ENetHost *, ENetEvent *); +ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32); +ENET_API void enet_host_flush (ENetHost *); +ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *); +ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *); +ENET_API int enet_host_compress_with_range_coder (ENetHost * host); +ENET_API void enet_host_channel_limit (ENetHost *, size_t); +ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); +extern void enet_host_bandwidth_throttle (ENetHost *); +extern enet_uint32 enet_host_random_seed (void); + +ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); +ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); +ENET_API void enet_peer_ping (ENetPeer *); +ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32); +ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +ENET_API void enet_peer_reset (ENetPeer *); +ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32); +ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +extern int enet_peer_throttle (ENetPeer *, enet_uint32); +extern void enet_peer_reset_queues (ENetPeer *); +extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); +extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); +extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); +extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16); +extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *); +extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *); +extern void enet_peer_on_connect (ENetPeer *); +extern void enet_peer_on_disconnect (ENetPeer *); + +ENET_API void * enet_range_coder_create (void); +ENET_API void enet_range_coder_destroy (void *); +ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t); +ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t); + +extern size_t enet_protocol_command_size (enet_uint8); + +#ifdef __cplusplus +} +#endif + +#endif /* __ENET_ENET_H__ */ + diff --git a/externals/enet/include/enet/list.h b/externals/enet/include/enet/list.h new file mode 100755 index 000000000..d7b260084 --- /dev/null +++ b/externals/enet/include/enet/list.h @@ -0,0 +1,43 @@ +/** + @file list.h + @brief ENet list management +*/ +#ifndef __ENET_LIST_H__ +#define __ENET_LIST_H__ + +#include + +typedef struct _ENetListNode +{ + struct _ENetListNode * next; + struct _ENetListNode * previous; +} ENetListNode; + +typedef ENetListNode * ENetListIterator; + +typedef struct _ENetList +{ + ENetListNode sentinel; +} ENetList; + +extern void enet_list_clear (ENetList *); + +extern ENetListIterator enet_list_insert (ENetListIterator, void *); +extern void * enet_list_remove (ENetListIterator); +extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); + +extern size_t enet_list_size (ENetList *); + +#define enet_list_begin(list) ((list) -> sentinel.next) +#define enet_list_end(list) (& (list) -> sentinel) + +#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) + +#define enet_list_next(iterator) ((iterator) -> next) +#define enet_list_previous(iterator) ((iterator) -> previous) + +#define enet_list_front(list) ((void *) (list) -> sentinel.next) +#define enet_list_back(list) ((void *) (list) -> sentinel.previous) + +#endif /* __ENET_LIST_H__ */ + diff --git a/externals/enet/include/enet/protocol.h b/externals/enet/include/enet/protocol.h new file mode 100755 index 000000000..f8c73d8a6 --- /dev/null +++ b/externals/enet/include/enet/protocol.h @@ -0,0 +1,198 @@ +/** + @file protocol.h + @brief ENet protocol +*/ +#ifndef __ENET_PROTOCOL_H__ +#define __ENET_PROTOCOL_H__ + +#include "enet/types.h" + +enum +{ + ENET_PROTOCOL_MINIMUM_MTU = 576, + ENET_PROTOCOL_MAXIMUM_MTU = 4096, + ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, + ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, + ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, + ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, + ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, + ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 +}; + +typedef enum _ENetProtocolCommand +{ + ENET_PROTOCOL_COMMAND_NONE = 0, + ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, + ENET_PROTOCOL_COMMAND_CONNECT = 2, + ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, + ENET_PROTOCOL_COMMAND_DISCONNECT = 4, + ENET_PROTOCOL_COMMAND_PING = 5, + ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, + ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, + ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, + ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, + ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, + ENET_PROTOCOL_COMMAND_COUNT = 13, + + ENET_PROTOCOL_COMMAND_MASK = 0x0F +} ENetProtocolCommand; + +typedef enum _ENetProtocolFlag +{ + ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), + ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), + + ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), + ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, + + ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), + ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 +} ENetProtocolFlag; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#define ENET_PACKED +#elif defined(__GNUC__) || defined(__clang__) +#define ENET_PACKED __attribute__ ((packed)) +#else +#define ENET_PACKED +#endif + +typedef struct _ENetProtocolHeader +{ + enet_uint16 peerID; + enet_uint16 sentTime; +} ENET_PACKED ENetProtocolHeader; + +typedef struct _ENetProtocolCommandHeader +{ + enet_uint8 command; + enet_uint8 channelID; + enet_uint16 reliableSequenceNumber; +} ENET_PACKED ENetProtocolCommandHeader; + +typedef struct _ENetProtocolAcknowledge +{ + ENetProtocolCommandHeader header; + enet_uint16 receivedReliableSequenceNumber; + enet_uint16 receivedSentTime; +} ENET_PACKED ENetProtocolAcknowledge; + +typedef struct _ENetProtocolConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; + enet_uint32 data; +} ENET_PACKED ENetProtocolConnect; + +typedef struct _ENetProtocolVerifyConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; +} ENET_PACKED ENetProtocolVerifyConnect; + +typedef struct _ENetProtocolBandwidthLimit +{ + ENetProtocolCommandHeader header; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; +} ENET_PACKED ENetProtocolBandwidthLimit; + +typedef struct _ENetProtocolThrottleConfigure +{ + ENetProtocolCommandHeader header; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENET_PACKED ENetProtocolThrottleConfigure; + +typedef struct _ENetProtocolDisconnect +{ + ENetProtocolCommandHeader header; + enet_uint32 data; +} ENET_PACKED ENetProtocolDisconnect; + +typedef struct _ENetProtocolPing +{ + ENetProtocolCommandHeader header; +} ENET_PACKED ENetProtocolPing; + +typedef struct _ENetProtocolSendReliable +{ + ENetProtocolCommandHeader header; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendReliable; + +typedef struct _ENetProtocolSendUnreliable +{ + ENetProtocolCommandHeader header; + enet_uint16 unreliableSequenceNumber; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnreliable; + +typedef struct _ENetProtocolSendUnsequenced +{ + ENetProtocolCommandHeader header; + enet_uint16 unsequencedGroup; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnsequenced; + +typedef struct _ENetProtocolSendFragment +{ + ENetProtocolCommandHeader header; + enet_uint16 startSequenceNumber; + enet_uint16 dataLength; + enet_uint32 fragmentCount; + enet_uint32 fragmentNumber; + enet_uint32 totalLength; + enet_uint32 fragmentOffset; +} ENET_PACKED ENetProtocolSendFragment; + +typedef union _ENetProtocol +{ + ENetProtocolCommandHeader header; + ENetProtocolAcknowledge acknowledge; + ENetProtocolConnect connect; + ENetProtocolVerifyConnect verifyConnect; + ENetProtocolDisconnect disconnect; + ENetProtocolPing ping; + ENetProtocolSendReliable sendReliable; + ENetProtocolSendUnreliable sendUnreliable; + ENetProtocolSendUnsequenced sendUnsequenced; + ENetProtocolSendFragment sendFragment; + ENetProtocolBandwidthLimit bandwidthLimit; + ENetProtocolThrottleConfigure throttleConfigure; +} ENET_PACKED ENetProtocol; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +#endif /* __ENET_PROTOCOL_H__ */ + diff --git a/externals/enet/include/enet/time.h b/externals/enet/include/enet/time.h new file mode 100755 index 000000000..c82a54603 --- /dev/null +++ b/externals/enet/include/enet/time.h @@ -0,0 +1,18 @@ +/** + @file time.h + @brief ENet time constants and macros +*/ +#ifndef __ENET_TIME_H__ +#define __ENET_TIME_H__ + +#define ENET_TIME_OVERFLOW 86400000 + +#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) +#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) + +#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) + +#endif /* __ENET_TIME_H__ */ + diff --git a/externals/enet/include/enet/types.h b/externals/enet/include/enet/types.h new file mode 100755 index 000000000..ab010a4b1 --- /dev/null +++ b/externals/enet/include/enet/types.h @@ -0,0 +1,13 @@ +/** + @file types.h + @brief type definitions for ENet +*/ +#ifndef __ENET_TYPES_H__ +#define __ENET_TYPES_H__ + +typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ +typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ +typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ + +#endif /* __ENET_TYPES_H__ */ + diff --git a/externals/enet/include/enet/unix.h b/externals/enet/include/enet/unix.h new file mode 100755 index 000000000..a59e34060 --- /dev/null +++ b/externals/enet/include/enet/unix.h @@ -0,0 +1,47 @@ +/** + @file unix.h + @brief ENet Unix header +*/ +#ifndef __ENET_UNIX_H__ +#define __ENET_UNIX_H__ + +#include +#include +#include +#include +#include +#include + +#ifdef MSG_MAXIOVLEN +#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN +#endif + +typedef int ENetSocket; + +#define ENET_SOCKET_NULL -1 + +#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ +#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ + +typedef struct +{ + void * data; + size_t dataLength; +} ENetBuffer; + +#define ENET_CALLBACK + +#define ENET_API extern + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_UNIX_H__ */ + diff --git a/externals/enet/include/enet/utility.h b/externals/enet/include/enet/utility.h new file mode 100755 index 000000000..e48a476be --- /dev/null +++ b/externals/enet/include/enet/utility.h @@ -0,0 +1,12 @@ +/** + @file utility.h + @brief ENet utility header +*/ +#ifndef __ENET_UTILITY_H__ +#define __ENET_UTILITY_H__ + +#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#endif /* __ENET_UTILITY_H__ */ + diff --git a/externals/enet/include/enet/win32.h b/externals/enet/include/enet/win32.h new file mode 100755 index 000000000..e73ca9d05 --- /dev/null +++ b/externals/enet/include/enet/win32.h @@ -0,0 +1,57 @@ +/** + @file win32.h + @brief ENet Win32 header +*/ +#ifndef __ENET_WIN32_H__ +#define __ENET_WIN32_H__ + +#ifdef _MSC_VER +#ifdef ENET_BUILDING_LIB +#pragma warning (disable: 4267) // size_t to int conversion +#pragma warning (disable: 4244) // 64bit to 32bit int +#pragma warning (disable: 4018) // signed/unsigned mismatch +#pragma warning (disable: 4146) // unary minus operator applied to unsigned type +#endif +#endif + +#include +#include + +typedef SOCKET ENetSocket; + +#define ENET_SOCKET_NULL INVALID_SOCKET + +#define ENET_HOST_TO_NET_16(value) (htons (value)) +#define ENET_HOST_TO_NET_32(value) (htonl (value)) + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) + +typedef struct +{ + size_t dataLength; + void * data; +} ENetBuffer; + +#define ENET_CALLBACK __cdecl + +#ifdef ENET_DLL +#ifdef ENET_BUILDING_LIB +#define ENET_API __declspec( dllexport ) +#else +#define ENET_API __declspec( dllimport ) +#endif /* ENET_BUILDING_LIB */ +#else /* !ENET_DLL */ +#define ENET_API extern +#endif /* ENET_DLL */ + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_WIN32_H__ */ + + diff --git a/externals/enet/libenet.pc.in b/externals/enet/libenet.pc.in new file mode 100755 index 000000000..7af85adbf --- /dev/null +++ b/externals/enet/libenet.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Low-latency UDP networking library supporting optional reliability +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lenet diff --git a/externals/enet/list.c b/externals/enet/list.c new file mode 100755 index 000000000..1c1a8dfaa --- /dev/null +++ b/externals/enet/list.c @@ -0,0 +1,75 @@ +/** + @file list.c + @brief ENet linked list functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** + @defgroup list ENet linked list utility functions + @ingroup private + @{ +*/ +void +enet_list_clear (ENetList * list) +{ + list -> sentinel.next = & list -> sentinel; + list -> sentinel.previous = & list -> sentinel; +} + +ENetListIterator +enet_list_insert (ENetListIterator position, void * data) +{ + ENetListIterator result = (ENetListIterator) data; + + result -> previous = position -> previous; + result -> next = position; + + result -> previous -> next = result; + position -> previous = result; + + return result; +} + +void * +enet_list_remove (ENetListIterator position) +{ + position -> previous -> next = position -> next; + position -> next -> previous = position -> previous; + + return position; +} + +ENetListIterator +enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast) +{ + ENetListIterator first = (ENetListIterator) dataFirst, + last = (ENetListIterator) dataLast; + + first -> previous -> next = last -> next; + last -> next -> previous = first -> previous; + + first -> previous = position -> previous; + last -> next = position; + + first -> previous -> next = first; + position -> previous = last; + + return first; +} + +size_t +enet_list_size (ENetList * list) +{ + size_t size = 0; + ENetListIterator position; + + for (position = enet_list_begin (list); + position != enet_list_end (list); + position = enet_list_next (position)) + ++ size; + + return size; +} + +/** @} */ diff --git a/externals/enet/m4/.keep b/externals/enet/m4/.keep new file mode 100755 index 000000000..e69de29bb diff --git a/externals/enet/packet.c b/externals/enet/packet.c new file mode 100755 index 000000000..5fa78b28a --- /dev/null +++ b/externals/enet/packet.c @@ -0,0 +1,165 @@ +/** + @file packet.c + @brief ENet packet management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup Packet ENet packet functions + @{ +*/ + +/** Creates a packet that may be sent to a peer. + @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL. + @param dataLength size of the data allocated for this packet + @param flags flags for this packet as described for the ENetPacket structure. + @returns the packet on success, NULL on failure +*/ +ENetPacket * +enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) +{ + ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); + if (packet == NULL) + return NULL; + + if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) + packet -> data = (enet_uint8 *) data; + else + if (dataLength <= 0) + packet -> data = NULL; + else + { + packet -> data = (enet_uint8 *) enet_malloc (dataLength); + if (packet -> data == NULL) + { + enet_free (packet); + return NULL; + } + + if (data != NULL) + memcpy (packet -> data, data, dataLength); + } + + packet -> referenceCount = 0; + packet -> flags = flags; + packet -> dataLength = dataLength; + packet -> freeCallback = NULL; + packet -> userData = NULL; + + return packet; +} + +/** Destroys the packet and deallocates its data. + @param packet packet to be destroyed +*/ +void +enet_packet_destroy (ENetPacket * packet) +{ + if (packet == NULL) + return; + + if (packet -> freeCallback != NULL) + (* packet -> freeCallback) (packet); + if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) && + packet -> data != NULL) + enet_free (packet -> data); + enet_free (packet); +} + +/** Attempts to resize the data in the packet to length specified in the + dataLength parameter + @param packet packet to resize + @param dataLength new size for the packet data + @returns 0 on success, < 0 on failure +*/ +int +enet_packet_resize (ENetPacket * packet, size_t dataLength) +{ + enet_uint8 * newData; + + if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE)) + { + packet -> dataLength = dataLength; + + return 0; + } + + newData = (enet_uint8 *) enet_malloc (dataLength); + if (newData == NULL) + return -1; + + memcpy (newData, packet -> data, packet -> dataLength); + enet_free (packet -> data); + + packet -> data = newData; + packet -> dataLength = dataLength; + + return 0; +} + +static int initializedCRC32 = 0; +static enet_uint32 crcTable [256]; + +static enet_uint32 +reflect_crc (int val, int bits) +{ + int result = 0, bit; + + for (bit = 0; bit < bits; bit ++) + { + if(val & 1) result |= 1 << (bits - 1 - bit); + val >>= 1; + } + + return result; +} + +static void +initialize_crc32 (void) +{ + int byte; + + for (byte = 0; byte < 256; ++ byte) + { + enet_uint32 crc = reflect_crc (byte, 8) << 24; + int offset; + + for(offset = 0; offset < 8; ++ offset) + { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04c11db7; + else + crc <<= 1; + } + + crcTable [byte] = reflect_crc (crc, 32); + } + + initializedCRC32 = 1; +} + +enet_uint32 +enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) +{ + enet_uint32 crc = 0xFFFFFFFF; + + if (! initializedCRC32) initialize_crc32 (); + + while (bufferCount -- > 0) + { + const enet_uint8 * data = (const enet_uint8 *) buffers -> data, + * dataEnd = & data [buffers -> dataLength]; + + while (data < dataEnd) + { + crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; + } + + ++ buffers; + } + + return ENET_HOST_TO_NET_32 (~ crc); +} + +/** @} */ diff --git a/externals/enet/peer.c b/externals/enet/peer.c new file mode 100755 index 000000000..e2d0872bd --- /dev/null +++ b/externals/enet/peer.c @@ -0,0 +1,1004 @@ +/** + @file peer.c + @brief ENet peer management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup peer ENet peer functions + @{ +*/ + +/** Configures throttle parameter for a peer. + + Unreliable packets are dropped by ENet in response to the varying conditions + of the Internet connection to the peer. The throttle represents a probability + that an unreliable packet should not be dropped and thus sent by ENet to the peer. + The lowest mean round trip time from the sending of a reliable packet to the + receipt of its acknowledgement is measured over an amount of time specified by + the interval parameter in milliseconds. If a measured round trip time happens to + be significantly less than the mean round trip time measured over the interval, + then the throttle probability is increased to allow more traffic by an amount + specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE + constant. If a measured round trip time happens to be significantly greater than + the mean round trip time measured over the interval, then the throttle probability + is decreased to limit traffic by an amount specified in the deceleration parameter, which + is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has + a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by + ENet, and so 100% of all unreliable packets will be sent. When the throttle has a + value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable + packets will be sent. Intermediate values for the throttle represent intermediate + probabilities between 0% and 100% of unreliable packets being sent. The bandwidth + limits of the local and foreign hosts are taken into account to determine a + sensible limit for the throttle probability above which it should not raise even in + the best of conditions. + + @param peer peer to configure + @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. + @param acceleration rate at which to increase the throttle probability as mean RTT declines + @param deceleration rate at which to decrease the throttle probability as mean RTT increases +*/ +void +enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) +{ + ENetProtocol command; + + peer -> packetThrottleInterval = interval; + peer -> packetThrottleAcceleration = acceleration; + peer -> packetThrottleDeceleration = deceleration; + + command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval); + command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration); + command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +int +enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) +{ + if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance) + { + peer -> packetThrottle = peer -> packetThrottleLimit; + } + else + if (rtt < peer -> lastRoundTripTime) + { + peer -> packetThrottle += peer -> packetThrottleAcceleration; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + return 1; + } + else + if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance) + { + if (peer -> packetThrottle > peer -> packetThrottleDeceleration) + peer -> packetThrottle -= peer -> packetThrottleDeceleration; + else + peer -> packetThrottle = 0; + + return -1; + } + + return 0; +} + +/** Queues a packet to be sent. + @param peer destination for the packet + @param channelID channel on which to send + @param packet packet to send + @retval 0 on success + @retval < 0 on failure +*/ +int +enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) +{ + ENetChannel * channel = & peer -> channels [channelID]; + ENetProtocol command; + size_t fragmentLength; + + if (peer -> state != ENET_PEER_STATE_CONNECTED || + channelID >= peer -> channelCount || + packet -> dataLength > peer -> host -> maximumPacketSize) + return -1; + + fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); + if (peer -> host -> checksum != NULL) + fragmentLength -= sizeof(enet_uint32); + + if (packet -> dataLength > fragmentLength) + { + enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength, + fragmentNumber, + fragmentOffset; + enet_uint8 commandNumber; + enet_uint16 startSequenceNumber; + ENetList fragments; + ENetOutgoingCommand * fragment; + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + return -1; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && + channel -> outgoingUnreliableSequenceNumber < 0xFFFF) + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); + } + else + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); + } + + enet_list_clear (& fragments); + + for (fragmentNumber = 0, + fragmentOffset = 0; + fragmentOffset < packet -> dataLength; + ++ fragmentNumber, + fragmentOffset += fragmentLength) + { + if (packet -> dataLength - fragmentOffset < fragmentLength) + fragmentLength = packet -> dataLength - fragmentOffset; + + fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (fragment == NULL) + { + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_free (fragment); + } + + return -1; + } + + fragment -> fragmentOffset = fragmentOffset; + fragment -> fragmentLength = fragmentLength; + fragment -> packet = packet; + fragment -> command.header.command = commandNumber; + fragment -> command.header.channelID = channelID; + fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; + fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); + fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount); + fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); + fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); + fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); + + enet_list_insert (enet_list_end (& fragments), fragment); + } + + packet -> referenceCount += fragmentNumber; + + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_peer_setup_outgoing_command (peer, fragment); + } + + return 0; + } + + command.header.channelID = channelID; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; + command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + + if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL) + return -1; + + return 0; +} + +/** Attempts to dequeue any incoming queued packet. + @param peer peer to dequeue packets from + @param channelID holds the channel ID of the channel the packet was received on success + @returns a pointer to the packet, or NULL if there are no available incoming queued packets +*/ +ENetPacket * +enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID) +{ + ENetIncomingCommand * incomingCommand; + ENetPacket * packet; + + if (enet_list_empty (& peer -> dispatchedCommands)) + return NULL; + + incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands)); + + if (channelID != NULL) + * channelID = incomingCommand -> command.header.channelID; + + packet = incomingCommand -> packet; + + -- packet -> referenceCount; + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + + peer -> totalWaitingData -= packet -> dataLength; + + return packet; +} + +static void +enet_peer_reset_outgoing_commands (ENetList * queue) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (queue)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue)); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + } +} + +static void +enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand) +{ + ENetListIterator currentCommand; + + for (currentCommand = startCommand; currentCommand != endCommand; ) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + enet_list_remove (& incomingCommand -> incomingCommandList); + + if (incomingCommand -> packet != NULL) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + } + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + } +} + +static void +enet_peer_reset_incoming_commands (ENetList * queue) +{ + enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue)); +} + +void +enet_peer_reset_queues (ENetPeer * peer) +{ + ENetChannel * channel; + + if (peer -> needsDispatch) + { + enet_list_remove (& peer -> dispatchList); + + peer -> needsDispatch = 0; + } + + while (! enet_list_empty (& peer -> acknowledgements)) + enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); + + enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands); + enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); + + if (peer -> channels != NULL && peer -> channelCount > 0) + { + for (channel = peer -> channels; + channel < & peer -> channels [peer -> channelCount]; + ++ channel) + { + enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands); + enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands); + } + + enet_free (peer -> channels); + } + + peer -> channels = NULL; + peer -> channelCount = 0; +} + +void +enet_peer_on_connect (ENetPeer * peer) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> incomingBandwidth != 0) + ++ peer -> host -> bandwidthLimitedPeers; + + ++ peer -> host -> connectedPeers; + } +} + +void +enet_peer_on_disconnect (ENetPeer * peer) +{ + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> incomingBandwidth != 0) + -- peer -> host -> bandwidthLimitedPeers; + + -- peer -> host -> connectedPeers; + } +} + +/** Forcefully disconnects a peer. + @param peer peer to forcefully disconnect + @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout + on its connection to the local host. +*/ +void +enet_peer_reset (ENetPeer * peer) +{ + enet_peer_on_disconnect (peer); + + peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; + peer -> connectID = 0; + + peer -> state = ENET_PEER_STATE_DISCONNECTED; + + peer -> incomingBandwidth = 0; + peer -> outgoingBandwidth = 0; + peer -> incomingBandwidthThrottleEpoch = 0; + peer -> outgoingBandwidthThrottleEpoch = 0; + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + peer -> lastSendTime = 0; + peer -> lastReceiveTime = 0; + peer -> nextTimeout = 0; + peer -> earliestTimeout = 0; + peer -> packetLossEpoch = 0; + peer -> packetsSent = 0; + peer -> packetsLost = 0; + peer -> packetLoss = 0; + peer -> packetLossVariance = 0; + peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; + peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; + peer -> packetThrottleCounter = 0; + peer -> packetThrottleEpoch = 0; + peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; + peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; + peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; + peer -> pingInterval = ENET_PEER_PING_INTERVAL; + peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; + peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lastRoundTripTimeVariance = 0; + peer -> highestRoundTripTimeVariance = 0; + peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> roundTripTimeVariance = 0; + peer -> mtu = peer -> host -> mtu; + peer -> reliableDataInTransit = 0; + peer -> outgoingReliableSequenceNumber = 0; + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + peer -> incomingUnsequencedGroup = 0; + peer -> outgoingUnsequencedGroup = 0; + peer -> eventData = 0; + peer -> totalWaitingData = 0; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + + enet_peer_reset_queues (peer); +} + +/** Sends a ping request to a peer. + @param peer destination for the ping request + @remarks ping requests factor into the mean round trip time as designated by the + roundTripTime field in the ENetPeer structure. ENet automatically pings all connected + peers at regular intervals, however, this function may be called to ensure more + frequent ping requests. +*/ +void +enet_peer_ping (ENetPeer * peer) +{ + ENetProtocol command; + + if (peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +/** Sets the interval at which pings will be sent to a peer. + + Pings are used both to monitor the liveness of the connection and also to dynamically + adjust the throttle during periods of low traffic so that the throttle has reasonable + responsiveness during traffic spikes. + + @param peer the peer to adjust + @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 +*/ +void +enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval) +{ + peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL; +} + +/** Sets the timeout parameters for a peer. + + The timeout parameter control how and when a peer will timeout from a failure to acknowledge + reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable + packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, + the timeout will be doubled until it reaches a set limit. If the timeout is thus at this + limit and reliable packets have been sent but not acknowledged within a certain minimum time + period, the peer will be disconnected. Alternatively, if reliable packets have been sent + but not acknowledged for a certain maximum time period, the peer will be disconnected regardless + of the current timeout limit value. + + @param peer the peer to adjust + @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 + @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 + @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 +*/ + +void +enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) +{ + peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM; +} + +/** Force an immediate disconnection from a peer. + @param peer peer to disconnect + @param data data describing the disconnection + @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not + guaranteed to receive the disconnect notification, and is reset immediately upon + return from this function. +*/ +void +enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED) + return; + + if (peer -> state != ENET_PEER_STATE_ZOMBIE && + peer -> state != ENET_PEER_STATE_DISCONNECTING) + { + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + enet_host_flush (peer -> host); + } + + enet_peer_reset (peer); +} + +/** Request a disconnection from a peer. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTING || + peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || + peer -> state == ENET_PEER_STATE_ZOMBIE) + return; + + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + else + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + { + enet_peer_on_disconnect (peer); + + peer -> state = ENET_PEER_STATE_DISCONNECTING; + } + else + { + enet_host_flush (peer -> host); + enet_peer_reset (peer); + } +} + +/** Request a disconnection from a peer, but only after all queued outgoing packets are sent. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) +{ + if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && + ! (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands))) + { + peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; + peer -> eventData = data; + } + else + enet_peer_disconnect (peer, data); +} + +ENetAcknowledgement * +enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime) +{ + ENetAcknowledgement * acknowledgement; + + if (command -> header.channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) + return NULL; + } + + acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement)); + if (acknowledgement == NULL) + return NULL; + + peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge); + + acknowledgement -> sentTime = sentTime; + acknowledgement -> command = * command; + + enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement); + + return acknowledgement; +} + +void +enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) +{ + ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; + + peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; + + if (outgoingCommand -> command.header.channelID == 0xFF) + { + ++ peer -> outgoingReliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + channel -> outgoingUnreliableSequenceNumber = 0; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } + + outgoingCommand -> sendAttempts = 0; + outgoingCommand -> sentTime = 0; + outgoingCommand -> roundTripTimeout = 0; + outgoingCommand -> roundTripTimeoutLimit = 0; + outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + + switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber); + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); + break; + + default: + break; + } + + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand); +} + +ENetOutgoingCommand * +enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length) +{ + ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (outgoingCommand == NULL) + return NULL; + + outgoingCommand -> command = * command; + outgoingCommand -> fragmentOffset = offset; + outgoingCommand -> fragmentLength = length; + outgoingCommand -> packet = packet; + if (packet != NULL) + ++ packet -> referenceCount; + + enet_peer_setup_outgoing_command (peer, outgoingCommand); + + return outgoingCommand; +} + +void +enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator droppedCommand, startCommand, currentCommand; + + for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> fragmentsRemaining <= 0) + { + channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; + continue; + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + else + if (droppedCommand != currentCommand) + droppedCommand = enet_list_previous (currentCommand); + } + else + { + enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + break; + + droppedCommand = enet_list_next (currentCommand); + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + } + } + + startCommand = enet_list_next (currentCommand); + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + + enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand); +} + +void +enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator currentCommand; + + for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (incomingCommand -> fragmentsRemaining > 0 || + incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1)) + break; + + channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber; + + if (incomingCommand -> fragmentCount > 0) + channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1; + } + + if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands)) + return; + + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + if (! enet_list_empty (& channel -> incomingUnreliableCommands)) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); +} + +ENetIncomingCommand * +enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount) +{ + static ENetIncomingCommand dummyCommand; + + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; + enet_uint16 reliableWindow, currentWindow; + ENetIncomingCommand * incomingCommand; + ENetListIterator currentCommand; + ENetPacket * packet = NULL; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + goto discardCommand; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + { + reliableSequenceNumber = command -> header.reliableSequenceNumber; + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + goto discardCommand; + } + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + goto discardCommand; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + goto discardCommand; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber); + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + goto discardCommand; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber) + break; + + goto discardCommand; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + currentCommand = enet_list_end (& channel -> incomingUnreliableCommands); + break; + + default: + goto discardCommand; + } + + if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData) + goto notifyError; + + packet = enet_packet_create (data, dataLength, flags); + if (packet == NULL) + goto notifyError; + + incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand)); + if (incomingCommand == NULL) + goto notifyError; + + incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber; + incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; + incomingCommand -> command = * command; + incomingCommand -> fragmentCount = fragmentCount; + incomingCommand -> fragmentsRemaining = fragmentCount; + incomingCommand -> packet = packet; + incomingCommand -> fragments = NULL; + + if (fragmentCount > 0) + { + if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32)); + if (incomingCommand -> fragments == NULL) + { + enet_free (incomingCommand); + + goto notifyError; + } + memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32)); + } + + if (packet != NULL) + { + ++ packet -> referenceCount; + + peer -> totalWaitingData += packet -> dataLength; + } + + enet_list_insert (enet_list_next (currentCommand), incomingCommand); + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + break; + + default: + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + break; + } + + return incomingCommand; + +discardCommand: + if (fragmentCount > 0) + goto notifyError; + + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return & dummyCommand; + +notifyError: + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return NULL; +} + +/** @} */ diff --git a/externals/enet/premake4.lua b/externals/enet/premake4.lua new file mode 100755 index 000000000..0e6e7adf6 --- /dev/null +++ b/externals/enet/premake4.lua @@ -0,0 +1,59 @@ +solution "enet" + configurations { "Debug", "Release" } + platforms { "x32", "x64" } + + project "enet_static" + kind "StaticLib" + language "C" + + files { "*.c" } + + includedirs { "include/" } + + configuration "Debug" + targetsuffix "d" + + defines({ "DEBUG" }) + + flags { "Symbols" } + + configuration "Release" + defines({ "NDEBUG" }) + + flags { "Optimize" } + + configuration { "Debug", "x64" } + targetsuffix "64d" + + configuration { "Release", "x64" } + targetsuffix "64" + + project "enet" + kind "SharedLib" + language "C" + + files { "*.c" } + + includedirs { "include/" } + + defines({"ENET_DLL=1" }) + + configuration "Debug" + targetsuffix "d" + + defines({ "DEBUG" }) + + flags { "Symbols" } + + configuration "Release" + defines({ "NDEBUG" }) + + flags { "Optimize" } + + configuration { "Debug", "x64" } + targetsuffix "64d" + + configuration { "Release", "x64" } + targetsuffix "64" + + \ No newline at end of file diff --git a/externals/enet/protocol.c b/externals/enet/protocol.c new file mode 100755 index 000000000..29d648732 --- /dev/null +++ b/externals/enet/protocol.c @@ -0,0 +1,1913 @@ +/** + @file protocol.c + @brief ENet protocol functions +*/ +#include +#include +#define ENET_BUILDING_LIB 1 +#include "enet/utility.h" +#include "enet/time.h" +#include "enet/enet.h" + +static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = +{ + 0, + sizeof (ENetProtocolAcknowledge), + sizeof (ENetProtocolConnect), + sizeof (ENetProtocolVerifyConnect), + sizeof (ENetProtocolDisconnect), + sizeof (ENetProtocolPing), + sizeof (ENetProtocolSendReliable), + sizeof (ENetProtocolSendUnreliable), + sizeof (ENetProtocolSendFragment), + sizeof (ENetProtocolSendUnsequenced), + sizeof (ENetProtocolBandwidthLimit), + sizeof (ENetProtocolThrottleConfigure), + sizeof (ENetProtocolSendFragment) +}; + +size_t +enet_protocol_command_size (enet_uint8 commandNumber) +{ + return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK]; +} + +static void +enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) +{ + if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER) + enet_peer_on_connect (peer); + else + enet_peer_on_disconnect (peer); + + peer -> state = state; +} + +static void +enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) +{ + enet_protocol_change_state (host, peer, state); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } +} + +static int +enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) +{ + while (! enet_list_empty (& host -> dispatchQueue)) + { + ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue)); + + peer -> needsDispatch = 0; + + switch (peer -> state) + { + case ENET_PEER_STATE_CONNECTION_PENDING: + case ENET_PEER_STATE_CONNECTION_SUCCEEDED: + enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + return 1; + + case ENET_PEER_STATE_ZOMBIE: + host -> recalculateBandwidthLimits = 1; + + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + enet_peer_reset (peer); + + return 1; + + case ENET_PEER_STATE_CONNECTED: + if (enet_list_empty (& peer -> dispatchedCommands)) + continue; + + event -> packet = enet_peer_receive (peer, & event -> channelID); + if (event -> packet == NULL) + continue; + + event -> type = ENET_EVENT_TYPE_RECEIVE; + event -> peer = peer; + + if (! enet_list_empty (& peer -> dispatchedCommands)) + { + peer -> needsDispatch = 1; + + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + } + + return 1; + + default: + break; + } + } + + return 0; +} + +static void +enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + host -> recalculateBandwidthLimits = 1; + + if (event != NULL) + { + enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + } + else + enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); +} + +static void +enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING) + host -> recalculateBandwidthLimits = 1; + + if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) + enet_peer_reset (peer); + else + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = 0; + + enet_peer_reset (peer); + } + else + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + } +} + +static void +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (& peer -> sentUnreliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + } +} + +static ENetProtocolCommand +enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetOutgoingCommand * outgoingCommand = NULL; + ENetListIterator currentCommand; + ENetProtocolCommand commandNumber; + int wasSent = 1; + + for (currentCommand = enet_list_begin (& peer -> sentReliableCommands); + currentCommand != enet_list_end (& peer -> sentReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) + { + for (currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + currentCommand != enet_list_end (& peer -> outgoingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> outgoingReliableCommands)) + return ENET_PROTOCOL_COMMAND_NONE; + + wasSent = 0; + } + + if (outgoingCommand == NULL) + return ENET_PROTOCOL_COMMAND_NONE; + + if (channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [channelID]; + enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel -> reliableWindows [reliableWindow] > 0) + { + -- channel -> reliableWindows [reliableWindow]; + if (! channel -> reliableWindows [reliableWindow]) + channel -> usedReliableWindows &= ~ (1 << reliableWindow); + } + } + + commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + if (wasSent) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + + if (enet_list_empty (& peer -> sentReliableCommands)) + return commandNumber; + + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands); + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + + return commandNumber; +} + +static ENetPeer * +enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command) +{ + enet_uint8 incomingSessionID, outgoingSessionID; + enet_uint32 mtu, windowSize; + ENetChannel * channel; + size_t channelCount, duplicatePeers = 0; + ENetPeer * currentPeer, * peer = NULL; + ENetProtocol verifyCommand; + + channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || + channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + return NULL; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + { + if (peer == NULL) + peer = currentPeer; + } + else + if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && + currentPeer -> address.host == host -> receivedAddress.host) + { + if (currentPeer -> address.port == host -> receivedAddress.port && + currentPeer -> connectID == command -> connect.connectID) + return NULL; + + ++ duplicatePeers; + } + } + + if (peer == NULL || duplicatePeers >= host -> duplicatePeers) + return NULL; + + if (channelCount > host -> channelLimit) + channelCount = host -> channelLimit; + peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (peer -> channels == NULL) + return NULL; + peer -> channelCount = channelCount; + peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; + peer -> connectID = command -> connect.connectID; + peer -> address = host -> receivedAddress; + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); + peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data); + + incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID; + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (incomingSessionID == peer -> outgoingSessionID) + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + peer -> outgoingSessionID = incomingSessionID; + + outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID; + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (outgoingSessionID == peer -> incomingSessionID) + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + peer -> incomingSessionID = outgoingSessionID; + + for (channel = peer -> channels; + channel < & peer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + peer -> mtu = mtu; + + if (host -> outgoingBandwidth == 0 && + peer -> incomingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + if (host -> outgoingBandwidth == 0 || + peer -> incomingBandwidth == 0) + peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (host -> incomingBandwidth == 0) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) + windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + verifyCommand.header.channelID = 0xFF; + verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID); + verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; + verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; + verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu); + verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); + verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval); + verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration); + verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration); + verifyCommand.verifyConnect.connectID = peer -> connectID; + + enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0); + + return peer; +} + +static int +enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 unsequencedGroup, index; + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup); + index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; + + if (unsequencedGroup < peer -> incomingUnsequencedGroup) + unsequencedGroup += 0x10000; + + if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) + return 0; + + unsequencedGroup &= 0xFFFF; + + if (unsequencedGroup - index != peer -> incomingUnsequencedGroup) + { + peer -> incomingUnsequencedGroup = unsequencedGroup - index; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + } + else + if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32))) + return 0; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL) + return -1; + + peer -> unsequencedWindow [index / 32] |= 1 << (index % 32); + + return 0; +} + +static int +enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + startSequenceNumber, + totalLength; + ENetChannel * channel; + enet_uint16 startWindow, currentWindow; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (startSequenceNumber < channel -> incomingReliableSequenceNumber) + startWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > host -> maximumPacketSize || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (startSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + ENetProtocol hostCommand = * command; + + hostCommand.header.reliableSequenceNumber = startSequenceNumber; + + startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + reliableSequenceNumber, + startSequenceNumber, + totalLength; + enet_uint16 reliableWindow, currentWindow; + ENetChannel * channel; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + reliableSequenceNumber = command -> header.reliableSequenceNumber; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + startSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > host -> maximumPacketSize || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + return 0; +} + +static int +enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + if (peer -> incomingBandwidth != 0) + -- host -> bandwidthLimitedPeers; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth); + + if (peer -> incomingBandwidth != 0) + ++ host -> bandwidthLimitedPeers; + + if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0) + peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + return 0; +} + +static int +enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration); + + return 0; +} + +static int +enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT) + return 0; + + enet_peer_reset_queues (peer); + + if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + else + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1; + + enet_peer_reset (peer); + } + else + if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT); + else + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + if (peer -> state != ENET_PEER_STATE_DISCONNECTED) + peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data); + + return 0; +} + +static int +enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 roundTripTime, + receivedSentTime, + receivedReliableSequenceNumber; + ENetProtocolCommand commandNumber; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE) + return 0; + + receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime); + receivedSentTime |= host -> serviceTime & 0xFFFF0000; + if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000)) + receivedSentTime -= 0x10000; + + if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime)) + return 0; + + peer -> lastReceiveTime = host -> serviceTime; + peer -> earliestTimeout = 0; + + roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime); + + enet_peer_throttle (peer, roundTripTime); + + peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4; + + if (roundTripTime >= peer -> roundTripTime) + { + peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8; + peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4; + } + else + { + peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8; + peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4; + } + + if (peer -> roundTripTime < peer -> lowestRoundTripTime) + peer -> lowestRoundTripTime = peer -> roundTripTime; + + if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + + if (peer -> packetThrottleEpoch == 0 || + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval) + { + peer -> lastRoundTripTime = peer -> lowestRoundTripTime; + peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance; + peer -> lowestRoundTripTime = peer -> roundTripTime; + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + peer -> packetThrottleEpoch = host -> serviceTime; + } + + receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber); + + commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID); + + switch (peer -> state) + { + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) + return -1; + + enet_protocol_notify_connect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECTING: + if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) + return -1; + + enet_protocol_notify_disconnect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECT_LATER: + if (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); + break; + + default: + break; + } + + return 0; +} + +static int +enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 mtu, windowSize; + size_t channelCount; + + if (peer -> state != ENET_PEER_STATE_CONNECTING) + return 0; + + channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration || + command -> verifyConnect.connectID != peer -> connectID) + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + return -1; + } + + enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF); + + if (channelCount < peer -> channelCount) + peer -> channelCount = channelCount; + + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID); + peer -> incomingSessionID = command -> verifyConnect.incomingSessionID; + peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID; + + mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + if (mtu < peer -> mtu) + peer -> mtu = mtu; + + windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (windowSize < peer -> windowSize) + peer -> windowSize = windowSize; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth); + + enet_protocol_notify_connect (host, peer, event); + return 0; +} + +static int +enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +{ + ENetProtocolHeader * header; + ENetProtocol * command; + ENetPeer * peer; + enet_uint8 * currentData; + size_t headerSize; + enet_uint16 peerID, flags; + enet_uint8 sessionID; + + if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime) + return 0; + + header = (ENetProtocolHeader *) host -> receivedData; + + peerID = ENET_NET_TO_HOST_16 (header -> peerID); + sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; + flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; + peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + + headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime); + if (host -> checksum != NULL) + headerSize += sizeof (enet_uint32); + + if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) + peer = NULL; + else + if (peerID >= host -> peerCount) + return 0; + else + { + peer = & host -> peers [peerID]; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ZOMBIE || + ((host -> receivedAddress.host != peer -> address.host || + host -> receivedAddress.port != peer -> address.port) && + peer -> address.host != ENET_HOST_BROADCAST) || + (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && + sessionID != peer -> incomingSessionID)) + return 0; + } + + if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) + { + size_t originalSize; + if (host -> compressor.context == NULL || host -> compressor.decompress == NULL) + return 0; + + originalSize = host -> compressor.decompress (host -> compressor.context, + host -> receivedData + headerSize, + host -> receivedDataLength - headerSize, + host -> packetData [1] + headerSize, + sizeof (host -> packetData [1]) - headerSize); + if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize) + return 0; + + memcpy (host -> packetData [1], header, headerSize); + host -> receivedData = host -> packetData [1]; + host -> receivedDataLength = headerSize + originalSize; + } + + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], + desiredChecksum = * checksum; + ENetBuffer buffer; + + * checksum = peer != NULL ? peer -> connectID : 0; + + buffer.data = host -> receivedData; + buffer.dataLength = host -> receivedDataLength; + + if (host -> checksum (& buffer, 1) != desiredChecksum) + return 0; + } + + if (peer != NULL) + { + peer -> address.host = host -> receivedAddress.host; + peer -> address.port = host -> receivedAddress.port; + peer -> incomingDataTotal += host -> receivedDataLength; + } + + currentData = host -> receivedData + headerSize; + + while (currentData < & host -> receivedData [host -> receivedDataLength]) + { + enet_uint8 commandNumber; + size_t commandSize; + + command = (ENetProtocol *) currentData; + + if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength]) + break; + + commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK; + if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) + break; + + commandSize = commandSizes [commandNumber]; + if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength]) + break; + + currentData += commandSize; + + if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT) + break; + + command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber); + + switch (commandNumber) + { + case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: + if (enet_protocol_handle_acknowledge (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_CONNECT: + if (peer != NULL) + goto commandError; + peer = enet_protocol_handle_connect (host, header, command); + if (peer == NULL) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: + if (enet_protocol_handle_verify_connect (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_DISCONNECT: + if (enet_protocol_handle_disconnect (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_PING: + if (enet_protocol_handle_ping (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (enet_protocol_handle_send_reliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + if (enet_protocol_handle_send_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: + if (enet_protocol_handle_bandwidth_limit (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: + if (enet_protocol_handle_throttle_configure (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + default: + goto commandError; + } + + if (peer != NULL && + (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) + { + enet_uint16 sentTime; + + if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) + break; + + sentTime = ENET_NET_TO_HOST_16 (header -> sentTime); + + switch (peer -> state) + { + case ENET_PEER_STATE_DISCONNECTING: + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + case ENET_PEER_STATE_DISCONNECTED: + case ENET_PEER_STATE_ZOMBIE: + break; + + case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + + default: + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + } + } + } + +commandError: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + return 0; +} + +static int +enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) +{ + int packets; + + for (packets = 0; packets < 256; ++ packets) + { + int receivedLength; + ENetBuffer buffer; + + buffer.data = host -> packetData [0]; + buffer.dataLength = sizeof (host -> packetData [0]); + + receivedLength = enet_socket_receive (host -> socket, + & host -> receivedAddress, + & buffer, + 1); + + if (receivedLength < 0) + return -1; + + if (receivedLength == 0) + return 0; + + host -> receivedData = host -> packetData [0]; + host -> receivedDataLength = receivedLength; + + host -> totalReceivedData += receivedLength; + host -> totalReceivedPackets ++; + + if (host -> intercept != NULL) + { + switch (host -> intercept (host, event)) + { + case 1: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + continue; + + case -1: + return -1; + + default: + break; + } + } + + switch (enet_protocol_handle_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + return -1; + + default: + break; + } + } + + return -1; +} + +static void +enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetAcknowledgement * acknowledgement; + ENetListIterator currentAcknowledgement; + enet_uint16 reliableSequenceNumber; + + currentAcknowledgement = enet_list_begin (& peer -> acknowledgements); + + while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements)) + { + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) + { + host -> continueSending = 1; + + break; + } + + acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; + + currentAcknowledgement = enet_list_next (currentAcknowledgement); + + buffer -> data = command; + buffer -> dataLength = sizeof (ENetProtocolAcknowledge); + + host -> packetSize += buffer -> dataLength; + + reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber); + + command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; + command -> header.channelID = acknowledgement -> command.header.channelID; + command -> header.reliableSequenceNumber = reliableSequenceNumber; + command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber; + command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime); + + if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + enet_list_remove (& acknowledgement -> acknowledgementList); + enet_free (acknowledgement); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static void +enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands)) + { + size_t commandSize; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + peer -> mtu - host -> packetSize < commandSize + outgoingCommand -> fragmentLength)) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0) + { + peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; + peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> packetThrottleCounter > peer -> packetThrottle) + { + enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber, + unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber; + for (;;) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + enet_free (outgoingCommand); + + if (currentCommand == enet_list_end (& peer -> outgoingUnreliableCommands)) + break; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber || + outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber) + break; + + currentCommand = enet_list_next (currentCommand); + } + + continue; + } + } + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + + * command = outgoingCommand -> command; + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += buffer -> dataLength; + + enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + } + else + enet_free (outgoingCommand); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && + enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); +} + +static int +enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand, insertPosition; + + currentCommand = enet_list_begin (& peer -> sentReliableCommands); + insertPosition = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout) + continue; + + if (peer -> earliestTimeout == 0 || + ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout)) + peer -> earliestTimeout = outgoingCommand -> sentTime; + + if (peer -> earliestTimeout != 0 && + (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || + (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) + { + enet_protocol_notify_disconnect (host, peer, event); + + return 1; + } + + if (outgoingCommand -> packet != NULL) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + ++ peer -> packetsLost; + + outgoingCommand -> roundTripTimeout *= 2; + + enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && + ! enet_list_empty (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + } + } + + return 0; +} + +static int +enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + ENetChannel *channel; + enet_uint16 reliableWindow; + size_t commandSize; + int windowExceeded = 0, windowWrap = 0, canPing = 1; + + currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL; + reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel != NULL) + { + if (! windowWrap && + outgoingCommand -> sendAttempts < 1 && + ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && + (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || + channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | + (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) + windowWrap = 1; + if (windowWrap) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + if (outgoingCommand -> packet != NULL) + { + if (! windowExceeded) + { + enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) + windowExceeded = 1; + } + if (windowExceeded) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + canPing = 0; + + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (channel != NULL && outgoingCommand -> sendAttempts < 1) + { + channel -> usedReliableWindows |= 1 << reliableWindow; + ++ channel -> reliableWindows [reliableWindow]; + } + + ++ outgoingCommand -> sendAttempts; + + if (outgoingCommand -> roundTripTimeout == 0) + { + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; + outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; + } + + if (enet_list_empty (& peer -> sentReliableCommands)) + peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; + + enet_list_insert (enet_list_end (& peer -> sentReliableCommands), + enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + outgoingCommand -> sentTime = host -> serviceTime; + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; + + * command = outgoingCommand -> command; + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += outgoingCommand -> fragmentLength; + + peer -> reliableDataInTransit += outgoingCommand -> fragmentLength; + } + + ++ peer -> packetsSent; + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + return canPing; +} + +static int +enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) +{ + enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; + ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; + ENetPeer * currentPeer; + int sentLength; + size_t shouldCompress = 0; + + host -> continueSending = 1; + + while (host -> continueSending) + for (host -> continueSending = 0, + currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || + currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + continue; + + host -> headerFlags = 0; + host -> commandCount = 0; + host -> bufferCount = 1; + host -> packetSize = sizeof (ENetProtocolHeader); + + if (! enet_list_empty (& currentPeer -> acknowledgements)) + enet_protocol_send_acknowledgements (host, currentPeer); + + if (checkForTimeouts != 0 && + ! enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) && + enet_protocol_check_timeouts (host, currentPeer, event) == 1) + { + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + else + continue; + } + + if ((enet_list_empty (& currentPeer -> outgoingReliableCommands) || + enet_protocol_send_reliable_outgoing_commands (host, currentPeer)) && + enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && + currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) + { + enet_peer_ping (currentPeer); + enet_protocol_send_reliable_outgoing_commands (host, currentPeer); + } + + if (! enet_list_empty (& currentPeer -> outgoingUnreliableCommands)) + enet_protocol_send_unreliable_outgoing_commands (host, currentPeer); + + if (host -> commandCount == 0) + continue; + + if (currentPeer -> packetLossEpoch == 0) + currentPeer -> packetLossEpoch = host -> serviceTime; + else + if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && + currentPeer -> packetsSent > 0) + { + enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; + +#ifdef ENET_DEBUG + printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); +#endif + + currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4; + + if (packetLoss >= currentPeer -> packetLoss) + { + currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8; + currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4; + } + else + { + currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8; + currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4; + } + + currentPeer -> packetLossEpoch = host -> serviceTime; + currentPeer -> packetsSent = 0; + currentPeer -> packetsLost = 0; + } + + host -> buffers -> data = headerData; + if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) + { + header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); + + host -> buffers -> dataLength = sizeof (ENetProtocolHeader); + } + else + host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime; + + shouldCompress = 0; + if (host -> compressor.context != NULL && host -> compressor.compress != NULL) + { + size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader), + compressedSize = host -> compressor.compress (host -> compressor.context, + & host -> buffers [1], host -> bufferCount - 1, + originalSize, + host -> packetData [1], + originalSize); + if (compressedSize > 0 && compressedSize < originalSize) + { + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; + shouldCompress = compressedSize; +#ifdef ENET_DEBUG_COMPRESS + printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); +#endif + } + } + + if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) + host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; + header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags); + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; + * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; + host -> buffers -> dataLength += sizeof (enet_uint32); + * checksum = host -> checksum (host -> buffers, host -> bufferCount); + } + + if (shouldCompress > 0) + { + host -> buffers [1].data = host -> packetData [1]; + host -> buffers [1].dataLength = shouldCompress; + host -> bufferCount = 2; + } + + currentPeer -> lastSendTime = host -> serviceTime; + + sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); + + enet_protocol_remove_sent_unreliable_commands (currentPeer); + + if (sentLength < 0) + return -1; + + host -> totalSentData += sentLength; + host -> totalSentPackets ++; + } + + return 0; +} + +/** Sends any queued packets on the host specified to its designated peers. + + @param host host to flush + @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). + @ingroup host +*/ +void +enet_host_flush (ENetHost * host) +{ + host -> serviceTime = enet_time_get (); + + enet_protocol_send_outgoing_commands (host, NULL, 0); +} + +/** Checks for any queued events on the host and dispatches one if available. + + @param host host to check for events + @param event an event structure where event details will be placed if available + @retval > 0 if an event was dispatched + @retval 0 if no events are available + @retval < 0 on failure + @ingroup host +*/ +int +enet_host_check_events (ENetHost * host, ENetEvent * event) +{ + if (event == NULL) return -1; + + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + return enet_protocol_dispatch_incoming_commands (host, event); +} + +/** Waits for events on the host specified and shuttles packets between + the host and its peers. + + @param host host to service + @param event an event structure where event details will be placed if one occurs + if event == NULL then no events will be delivered + @param timeout number of milliseconds that ENet should wait for events + @retval > 0 if an event occurred within the specified time limit + @retval 0 if no event occurred + @retval < 0 on failure + @remarks enet_host_service should be called fairly regularly for adequate performance + @ingroup host +*/ +int +enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout) +{ + enet_uint32 waitCondition; + + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error dispatching incoming packets"); +#endif + + return -1; + + default: + break; + } + } + + host -> serviceTime = enet_time_get (); + + timeout += host -> serviceTime; + + do + { + if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + enet_host_bandwidth_throttle (host); + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error sending outgoing packets"); +#endif + + return -1; + + default: + break; + } + + switch (enet_protocol_receive_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error receiving incoming packets"); +#endif + + return -1; + + default: + break; + } + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error sending outgoing packets"); +#endif + + return -1; + + default: + break; + } + + if (event != NULL) + { + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error dispatching incoming packets"); +#endif + + return -1; + + default: + break; + } + } + + if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) + return 0; + + do + { + host -> serviceTime = enet_time_get (); + + if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) + return 0; + + waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT; + + if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0) + return -1; + } + while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT); + + host -> serviceTime = enet_time_get (); + } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE); + + return 0; +} + diff --git a/externals/enet/unix.c b/externals/enet/unix.c new file mode 100755 index 000000000..c36a082a8 --- /dev/null +++ b/externals/enet/unix.c @@ -0,0 +1,616 @@ +/** + @file unix.c + @brief ENet Unix system specific functions +*/ +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +#ifdef __APPLE__ +#ifdef HAS_POLL +#undef HAS_POLL +#endif +#ifndef HAS_FCNTL +#define HAS_FCNTL 1 +#endif +#ifndef HAS_INET_PTON +#define HAS_INET_PTON 1 +#endif +#ifndef HAS_INET_NTOP +#define HAS_INET_NTOP 1 +#endif +#ifndef HAS_MSGHDR_FLAGS +#define HAS_MSGHDR_FLAGS 1 +#endif +#ifndef HAS_SOCKLEN_T +#define HAS_SOCKLEN_T 1 +#endif +#ifndef HAS_GETADDRINFO +#define HAS_GETADDRINFO 1 +#endif +#ifndef HAS_GETNAMEINFO +#define HAS_GETNAMEINFO 1 +#endif +#endif + +#ifdef HAS_FCNTL +#include +#endif + +#ifdef HAS_POLL +#include +#endif + +#ifndef HAS_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + return 0; +} + +void +enet_deinitialize (void) +{ +} + +enet_uint32 +enet_host_random_seed (void) +{ + return (enet_uint32) time (NULL); +} + +enet_uint32 +enet_time_get (void) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; +} + +int +enet_address_set_host_ip (ENetAddress * address, const char * name) +{ +#ifdef HAS_INET_PTON + if (! inet_pton (AF_INET, name, & address -> host)) +#else + if (! inet_aton (name, (struct in_addr *) & address -> host)) +#endif + return -1; + + return 0; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ +#ifdef HAS_GETADDRINFO + struct addrinfo hints, * resultList = NULL, * result = NULL; + + memset (& hints, 0, sizeof (hints)); + hints.ai_family = AF_INET; + + if (getaddrinfo (name, NULL, NULL, & resultList) != 0) + return -1; + + for (result = resultList; result != NULL; result = result -> ai_next) + { + if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in)) + { + struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr; + + address -> host = sin -> sin_addr.s_addr; + + freeaddrinfo (resultList); + + return 0; + } + } + + if (resultList != NULL) + freeaddrinfo (resultList); +#else + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYNAME_R + struct hostent hostData; + char buffer [2048]; + int errnum; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + hostEntry = gethostbyname (name); +#endif + + if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) + { + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; + } +#endif + + return enet_address_set_host_ip (address, name); +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ +#ifdef HAS_INET_NTOP + if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) +#else + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr != NULL) + { + size_t addrLen = strlen(addr); + if (addrLen >= nameLength) + return -1; + memcpy (name, addr, addrLen + 1); + } + else +#endif + return -1; + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ +#ifdef HAS_GETNAMEINFO + struct sockaddr_in sin; + int err; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD); + if (! err) + { + if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength)) + return -1; + return 0; + } + if (err != EAI_NONAME) + return -1; +#else + struct in_addr in; + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYADDR_R + struct hostent hostData; + char buffer [2048]; + int errnum; + + in.s_addr = address -> host; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); +#endif + + if (hostEntry != NULL) + { + size_t hostLen = strlen (hostEntry -> h_name); + if (hostLen >= nameLength) + return -1; + memcpy (name, hostEntry -> h_name, hostLen + 1); + return 0; + } +#endif + + return enet_address_get_host_ip (address, name, nameLength); +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)); +} + +int +enet_socket_get_address (ENetSocket socket, ENetAddress * address) +{ + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) + return -1; + + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + + return 0; +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog); +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = -1; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: +#ifdef HAS_FCNTL + result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK)); +#else + result = ioctl (socket, FIONBIO, & value); +#endif + break; + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + { + struct timeval timeVal; + timeVal.tv_sec = value / 1000; + timeVal.tv_usec = (value % 1000) * 1000; + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval)); + break; + } + + case ENET_SOCKOPT_SNDTIMEO: + { + struct timeval timeVal; + timeVal.tv_sec = value / 1000; + timeVal.tv_usec = (value % 1000) * 1000; + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval)); + break; + } + + case ENET_SOCKOPT_NODELAY: + result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == -1 ? -1 : 0; +} + +int +enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) +{ + int result = -1; + socklen_t len; + switch (option) + { + case ENET_SOCKOPT_ERROR: + len = sizeof (int); + result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len); + break; + + default: + break; + } + return result == -1 ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == -1 && errno == EINPROGRESS) + return 0; + + return result; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + int result; + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == -1) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how); +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != -1) + close (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int sentLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (sentLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + + return sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int recvLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (recvLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + +#ifdef HAS_MSGHDR_FLAGS + if (msgHdr.msg_flags & MSG_TRUNC) + return -1; +#endif + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ +#ifdef HAS_POLL + struct pollfd pollSocket; + int pollCount; + + pollSocket.fd = socket; + pollSocket.events = 0; + + if (* condition & ENET_SOCKET_WAIT_SEND) + pollSocket.events |= POLLOUT; + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + pollSocket.events |= POLLIN; + + pollCount = poll (& pollSocket, 1, timeout); + + if (pollCount < 0) + { + if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) + { + * condition = ENET_SOCKET_WAIT_INTERRUPT; + + return 0; + } + + return -1; + } + + * condition = ENET_SOCKET_WAIT_NONE; + + if (pollCount == 0) + return 0; + + if (pollSocket.revents & POLLOUT) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (pollSocket.revents & POLLIN) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#else + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + { + if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) + { + * condition = ENET_SOCKET_WAIT_INTERRUPT; + + return 0; + } + + return -1; + } + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#endif +} + +#endif + diff --git a/externals/enet/win32.c b/externals/enet/win32.c new file mode 100755 index 000000000..81175a411 --- /dev/null +++ b/externals/enet/win32.c @@ -0,0 +1,442 @@ +/** + @file win32.c + @brief ENet Win32 system specific functions +*/ +#ifdef _WIN32 + +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" +#include +#include + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + WORD versionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + + if (WSAStartup (versionRequested, & wsaData)) + return -1; + + if (LOBYTE (wsaData.wVersion) != 1|| + HIBYTE (wsaData.wVersion) != 1) + { + WSACleanup (); + + return -1; + } + + timeBeginPeriod (1); + + return 0; +} + +void +enet_deinitialize (void) +{ + timeEndPeriod (1); + + WSACleanup (); +} + +enet_uint32 +enet_host_random_seed (void) +{ + return (enet_uint32) timeGetTime (); +} + +enet_uint32 +enet_time_get (void) +{ + return (enet_uint32) timeGetTime () - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + timeBase = (enet_uint32) timeGetTime () - newTimeBase; +} + +int +enet_address_set_host_ip (ENetAddress * address, const char * name) +{ + enet_uint8 vals [4] = { 0, 0, 0, 0 }; + int i; + + for (i = 0; i < 4; ++ i) + { + const char * next = name + 1; + if (* name != '0') + { + long val = strtol (name, (char **) & next, 10); + if (val < 0 || val > 255 || next == name || next - name > 3) + return -1; + vals [i] = (enet_uint8) val; + } + + if (* next != (i < 3 ? '.' : '\0')) + return -1; + name = next + 1; + } + + memcpy (& address -> host, vals, sizeof (enet_uint32)); + return 0; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry; + + hostEntry = gethostbyname (name); + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + return enet_address_set_host_ip (address, name); + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr == NULL) + return -1; + else + { + size_t addrLen = strlen(addr); + if (addrLen >= nameLength) + return -1; + memcpy (name, addr, addrLen + 1); + } + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry; + + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); + if (hostEntry == NULL) + return enet_address_get_host_ip (address, name, nameLength); + else + { + size_t hostLen = strlen (hostEntry -> h_name); + if (hostLen >= nameLength) + return -1; + memcpy (name, hostEntry -> h_name, hostLen + 1); + } + + return 0; +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_get_address (ENetSocket socket, ENetAddress * address) +{ + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) + return -1; + + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + + return 0; +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = SOCKET_ERROR; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: + { + u_long nonBlocking = (u_long) value; + result = ioctlsocket (socket, FIONBIO, & nonBlocking); + break; + } + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_NODELAY: + result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) +{ + int result = SOCKET_ERROR, len; + switch (option) + { + case ENET_SOCKOPT_ERROR: + len = sizeof(int); + result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len); + break; + + default: + break; + } + return result == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) + return -1; + + return 0; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + SOCKET result; + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == INVALID_SOCKET) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0; +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != INVALID_SOCKET) + closesocket (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct sockaddr_in sin; + DWORD sentLength; + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + + if (WSASendTo (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & sentLength, + 0, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? sizeof (struct sockaddr_in) : 0, + NULL, + NULL) == SOCKET_ERROR) + { + if (WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + return -1; + } + + return (int) sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + INT sinLength = sizeof (struct sockaddr_in); + DWORD flags = 0, + recvLength; + struct sockaddr_in sin; + + if (WSARecvFrom (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & recvLength, + & flags, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL, + NULL, + NULL) == SOCKET_ERROR) + { + switch (WSAGetLastError ()) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + return 0; + } + + return -1; + } + + if (flags & MSG_PARTIAL) + return -1; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return (int) recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +} + +#endif + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c0677fbc4..a752a39b5 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -157,6 +157,7 @@ add_subdirectory(common) add_subdirectory(core) add_subdirectory(audio_core) add_subdirectory(video_core) +add_subdirectory(network) add_subdirectory(input_common) add_subdirectory(shader_recompiler) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 174e776c5..e52dbcee5 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -43,6 +43,7 @@ add_library(common STATIC address_space.cpp algorithm.h alignment.h + announce_multiplayer_room.h assert.cpp assert.h atomic_helpers.h diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h new file mode 100755 index 000000000..0ad9da2be --- /dev/null +++ b/src/common/announce_multiplayer_room.h @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/common_types.h" +#include "web_service/web_result.h" + +namespace AnnounceMultiplayerRoom { + +using MacAddress = std::array; + +struct GameInfo { + std::string name{""}; + u64 id{0}; +}; + +struct Member { + std::string username; + std::string nickname; + std::string display_name; + std::string avatar_url; + MacAddress mac_address; + GameInfo game; +}; + +struct RoomInformation { + std::string name; ///< Name of the server + std::string description; ///< Server description + u32 member_slots; ///< Maximum number of members in this room + u16 port; ///< The port of this room + GameInfo preferred_game; ///< Game to advertise that you want to play + std::string host_username; ///< Forum username of the host + bool enable_yuzu_mods; ///< Allow yuzu Moderators to moderate on this room +}; + +struct Room { + RoomInformation information; + + std::string id; + std::string verify_uid; ///< UID used for verification + std::string ip; + u32 net_version; + bool has_password; + + std::vector members; +}; +using RoomList = std::vector; + +/** + * A AnnounceMultiplayerRoom interface class. A backend to submit/get to/from a web service should + * implement this interface. + */ +class Backend { +public: + virtual ~Backend() = default; + + /** + * Sets the Information that gets used for the announce + * @param uid The Id of the room + * @param name The name of the room + * @param description The room description + * @param port The port of the room + * @param net_version The version of the libNetwork that gets used + * @param has_password True if the room is passowrd protected + * @param preferred_game The preferred game of the room + * @param preferred_game_id The title id of the preferred game + */ + virtual void SetRoomInformation(const std::string& name, const std::string& description, + const u16 port, const u32 max_player, const u32 net_version, + const bool has_password, const GameInfo& preferred_game) = 0; + /** + * Adds a player information to the data that gets announced + * @param nickname The nickname of the player + * @param mac_address The MAC Address of the player + * @param game_id The title id of the game the player plays + * @param game_name The name of the game the player plays + */ + virtual void AddPlayer(const Member& member) = 0; + + /** + * Updates the data in the announce service. Re-register the room when required. + * @result The result of the update attempt + */ + virtual WebService::WebResult Update() = 0; + + /** + * Registers the data in the announce service + * @result The result of the register attempt. When the result code is Success, A global Guid of + * the room which may be used for verification will be in the result's returned_data. + */ + virtual WebService::WebResult Register() = 0; + + /** + * Empties the stored players + */ + virtual void ClearPlayers() = 0; + + /** + * Get the room information from the announce service + * @result A list of all rooms the announce service has + */ + virtual RoomList GetRoomList() = 0; + + /** + * Sends a delete message to the announce service + */ + virtual void Delete() = 0; +}; + +/** + * Empty implementation of AnnounceMultiplayerRoom interface that drops all data. Used when a + * functional backend implementation is not available. + */ +class NullBackend : public Backend { +public: + ~NullBackend() = default; + void SetRoomInformation(const std::string& /*name*/, const std::string& /*description*/, + const u16 /*port*/, const u32 /*max_player*/, const u32 /*net_version*/, + const bool /*has_password*/, + const GameInfo& /*preferred_game*/) override {} + void AddPlayer(const Member& /*member*/) override {} + WebService::WebResult Update() override { + return WebService::WebResult{WebService::WebResult::Code::NoWebservice, + "WebService is missing", ""}; + } + WebService::WebResult Register() override { + return WebService::WebResult{WebService::WebResult::Code::NoWebservice, + "WebService is missing", ""}; + } + void ClearPlayers() override {} + RoomList GetRoomList() override { + return RoomList{}; + } + + void Delete() override {} +}; + +} // namespace AnnounceMultiplayerRoom diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9d988c8ad..1d2f370fd 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(core STATIC + announce_multiplayer_session.cpp + announce_multiplayer_session.h arm/arm_interface.h arm/arm_interface.cpp arm/cpu_interrupt_handler.cpp @@ -717,6 +719,11 @@ add_library(core STATIC hle/service/vi/vi_u.h hle/service/wlan/wlan.cpp hle/service/wlan/wlan.h + internal_network/network.cpp + internal_network/network.h + internal_network/network_interface.cpp + internal_network/network_interface.h + internal_network/sockets.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp @@ -744,11 +751,6 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h - network/network.cpp - network/network.h - network/network_interface.cpp - network/network_interface.h - network/sockets.h perf_stats.cpp perf_stats.h reporter.cpp @@ -783,7 +785,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp new file mode 100755 index 000000000..d73a488cf --- /dev/null +++ b/src/core/announce_multiplayer_session.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "announce_multiplayer_session.h" +#include "common/announce_multiplayer_room.h" +#include "common/assert.h" +#include "common/settings.h" +#include "network/network.h" + +#ifdef ENABLE_WEB_SERVICE +#include "web_service/announce_room_json.h" +#endif + +namespace Core { + +// Time between room is announced to web_service +static constexpr std::chrono::seconds announce_time_interval(15); + +AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_) + : room_network{room_network_} { +#ifdef ENABLE_WEB_SERVICE + backend = std::make_unique(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); +#else + backend = std::make_unique(); +#endif +} + +WebService::WebResult AnnounceMultiplayerSession::Register() { + std::shared_ptr room = room_network.GetRoom().lock(); + if (!room) { + return WebService::WebResult{WebService::WebResult::Code::LibError, + "Network is not initialized", ""}; + } + if (room->GetState() != Network::Room::State::Open) { + return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""}; + } + UpdateBackendData(room); + WebService::WebResult result = backend->Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + return result; + } + LOG_INFO(WebService, "Room has been registered"); + room->SetVerifyUID(result.returned_data); + registered = true; + return WebService::WebResult{WebService::WebResult::Code::Success, "", ""}; +} + +void AnnounceMultiplayerSession::Start() { + if (announce_multiplayer_thread) { + Stop(); + } + shutdown_event.Reset(); + announce_multiplayer_thread = + std::make_unique(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); +} + +void AnnounceMultiplayerSession::Stop() { + if (announce_multiplayer_thread) { + shutdown_event.Set(); + announce_multiplayer_thread->join(); + announce_multiplayer_thread.reset(); + backend->Delete(); + registered = false; + } +} + +AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( + std::function function) { + std::lock_guard lock(callback_mutex); + auto handle = std::make_shared>(function); + error_callbacks.insert(handle); + return handle; +} + +void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { + std::lock_guard lock(callback_mutex); + error_callbacks.erase(handle); +} + +AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { + Stop(); +} + +void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr room) { + Network::RoomInformation room_information = room->GetRoomInformation(); + std::vector memberlist = room->GetRoomMemberList(); + backend->SetRoomInformation(room_information.name, room_information.description, + room_information.port, room_information.member_slots, + Network::network_version, room->HasPassword(), + room_information.preferred_game); + backend->ClearPlayers(); + for (const auto& member : memberlist) { + backend->AddPlayer(member); + } +} + +void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { + // Invokes all current bound error callbacks. + const auto ErrorCallback = [this](WebService::WebResult result) { + std::lock_guard lock(callback_mutex); + for (auto callback : error_callbacks) { + (*callback)(result); + } + }; + + if (!registered) { + WebService::WebResult result = Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + return; + } + } + + auto update_time = std::chrono::steady_clock::now(); + std::future future; + while (!shutdown_event.WaitUntil(update_time)) { + update_time += announce_time_interval; + std::shared_ptr room = room_network.GetRoom().lock(); + if (!room) { + break; + } + if (room->GetState() != Network::Room::State::Open) { + break; + } + UpdateBackendData(room); + WebService::WebResult result = backend->Update(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + } + if (result.result_string == "404") { + registered = false; + // Needs to register the room again + WebService::WebResult register_result = Register(); + if (register_result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(register_result); + } + } + } +} + +AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { + return backend->GetRoomList(); +} + +bool AnnounceMultiplayerSession::IsRunning() const { + return announce_multiplayer_thread != nullptr; +} + +void AnnounceMultiplayerSession::UpdateCredentials() { + ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); + +#ifdef ENABLE_WEB_SERVICE + backend = std::make_unique(Settings::values.web_api_url.GetValue(), + Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); +#endif +} + +} // namespace Core diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h new file mode 100755 index 000000000..db790f7d2 --- /dev/null +++ b/src/core/announce_multiplayer_session.h @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" +#include "common/thread.h" + +namespace Network { +class Room; +class RoomNetwork; +} // namespace Network + +namespace Core { + +/** + * Instruments AnnounceMultiplayerRoom::Backend. + * Creates a thread that regularly updates the room information and submits them + * An async get of room information is also possible + */ +class AnnounceMultiplayerSession { +public: + using CallbackHandle = std::shared_ptr>; + AnnounceMultiplayerSession(Network::RoomNetwork& room_network_); + ~AnnounceMultiplayerSession(); + + /** + * Allows to bind a function that will get called if the announce encounters an error + * @param function The function that gets called + * @return A handle that can be used the unbind the function + */ + CallbackHandle BindErrorCallback(std::function function); + + /** + * Unbind a function from the error callbacks + * @param handle The handle for the function that should get unbind + */ + void UnbindErrorCallback(CallbackHandle handle); + + /** + * Registers a room to web services + * @return The result of the registration attempt. + */ + WebService::WebResult Register(); + + /** + * Starts the announce of a room to web services + */ + void Start(); + + /** + * Stops the announce to web services + */ + void Stop(); + + /** + * Returns a list of all room information the backend got + * @param func A function that gets executed when the async get finished, e.g. a signal + * @return a list of rooms received from the web service + */ + AnnounceMultiplayerRoom::RoomList GetRoomList(); + + /** + * Whether the announce session is still running + */ + bool IsRunning() const; + + /** + * Recreates the backend, updating the credentials. + * This can only be used when the announce session is not running. + */ + void UpdateCredentials(); + +private: + void UpdateBackendData(std::shared_ptr room); + void AnnounceMultiplayerLoop(); + + Common::Event shutdown_event; + std::mutex callback_mutex; + std::set error_callbacks; + std::unique_ptr announce_multiplayer_thread; + + /// Backend interface that logs fields + std::unique_ptr backend; + + std::atomic_bool registered = false; ///< Whether the room has been registered + + Network::RoomNetwork& room_network; +}; + +} // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index 7aff3f3e9..fdf843b44 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -42,14 +42,15 @@ #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" +#include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" -#include "core/network/network.h" #include "core/perf_stats.h" #include "core/reporter.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" +#include "network/network.h" #include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -130,7 +131,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, + : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} SystemResultStatus Run() { @@ -315,6 +316,17 @@ struct System::Impl { GetAndResetPerfStats(); perf_stats->BeginSystemFrame(); + std::string name = "Unknown Game"; + if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); + } + if (auto room_member = room_network.GetRoomMember().lock()) { + Network::GameInfo game_info; + game_info.name = name; + game_info.id = program_id; + room_member->SendGameInfo(game_info); + } + status = SystemResultStatus::Success; return status; } @@ -363,6 +375,11 @@ struct System::Impl { memory.Reset(); applet_manager.ClearAll(); + if (auto room_member = room_network.GetRoomMember().lock()) { + Network::GameInfo game_info{}; + room_member->SendGameInfo(game_info); + } + LOG_DEBUG(Core, "Shutdown OK"); } @@ -435,6 +452,8 @@ struct System::Impl { std::unique_ptr audio_core; Core::Memory::Memory memory; Core::HID::HIDCore hid_core; + Network::RoomNetwork room_network; + CpuManager cpu_manager; std::atomic_bool is_powered_on{}; bool exit_lock = false; @@ -880,6 +899,14 @@ const Core::Debugger& System::GetDebugger() const { return *impl->debugger; } +Network::RoomNetwork& System::GetRoomNetwork() { + return impl->room_network; +} + +const Network::RoomNetwork& System::GetRoomNetwork() const { + return impl->room_network; +} + void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { impl->execute_program_callback = std::move(callback); } diff --git a/src/core/core.h b/src/core/core.h index 48dbe40fa..18cca23a0 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -96,6 +96,10 @@ namespace Core::HID { class HIDCore; } +namespace Network { +class RoomNetwork; +} + namespace Core { class ARM_Interface; @@ -378,6 +382,12 @@ public: [[nodiscard]] Core::Debugger& GetDebugger(); [[nodiscard]] const Core::Debugger& GetDebugger() const; + /// Gets a mutable reference to the Room Network. + [[nodiscard]] Network::RoomNetwork& GetRoomNetwork(); + + /// Gets an immutable reference to the Room Network. + [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; + void SetExitLock(bool locked); [[nodiscard]] bool GetExitLock() const; diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp index 1d8f49d45..e2f4ae876 100755 --- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp @@ -103,10 +103,11 @@ void ClusteringProcessor::RemoveLowIntensityData(std::vector& data) { ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector& data, std::size_t x, std::size_t y) { - std::queue> search_points{}; + using DataPoint = Common::Point; + std::queue search_points{}; ClusteringData current_cluster = GetPixelProperties(data, x, y); SetPixel(data, x, y, 0); - search_points.emplace(x, y); + search_points.emplace({x, y}); while (!search_points.empty()) { const auto point = search_points.front(); @@ -117,8 +118,8 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(st continue; } - std::array, 4> new_points{ - Common::Point{point.x - 1, point.y}, + std::array new_points{ + DataPoint{point.x - 1, point.y}, {point.x, point.y - 1}, {point.x + 1, point.y}, {point.x, point.y + 1}, @@ -137,7 +138,7 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(st const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y); current_cluster = MergeCluster(current_cluster, cluster); SetPixel(data, new_point.x, new_point.y, 0); - search_points.emplace(new_point.x, new_point.y); + search_points.emplace({new_point.x, new_point.y}); } } diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 7055ea93e..2889973e4 100755 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -18,8 +18,8 @@ namespace { } // Anonymous namespace -#include "core/network/network.h" -#include "core/network/network_interface.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" namespace Service::NIFM { diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 3e9dc4a13..c7194731e 100755 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -13,8 +13,8 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" -#include "core/network/sockets.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index fed740d87..9ea36428d 100755 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -16,7 +16,7 @@ class System; namespace Network { class Socket; -} +} // namespace Network namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 9c0936d97..2db10ec81 100755 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index 5e9809add..c93291d3e 100755 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -7,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp new file mode 100755 index 000000000..36c43cc8f --- /dev/null +++ b/src/core/internal_network/network.cpp @@ -0,0 +1,637 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include + +#include "common/error.h" + +#ifdef _WIN32 +#include +#include +#elif YUZU_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#else +#error "Unimplemented platform" +#endif + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "core/internal_network/sockets.h" + +namespace Network { + +namespace { + +#ifdef _WIN32 + +using socklen_t = int; + +void Initialize() { + WSADATA wsa_data; + (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); +} + +void Finalize() { + WSACleanup(); +} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + +#if YUZU_UNIX + result.sin_len = sizeof(result); +#endif + + switch (static_cast(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + auto& ip = result.sin_addr.S_un.S_un_b; + ip.s_b1 = input.ip[0]; + ip.s_b2 = input.ip[1]; + ip.s_b3 = input.ip[2]; + ip.s_b4 = input.ip[3]; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +LINGER MakeLinger(bool enable, u32 linger_value) { + ASSERT(linger_value <= std::numeric_limits::max()); + + LINGER value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = static_cast(linger_value); + return value; +} + +bool EnableNonBlock(SOCKET fd, bool enable) { + u_long value = enable ? 1 : 0; + return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case WSAEBADF: + return Errno::BADF; + case WSAEINVAL: + return Errno::INVAL; + case WSAEMFILE: + return Errno::MFILE; + case WSAENOTCONN: + return Errno::NOTCONN; + case WSAEWOULDBLOCK: + return Errno::AGAIN; + case WSAECONNREFUSED: + return Errno::CONNREFUSED; + case WSAEHOSTUNREACH: + return Errno::HOSTUNREACH; + case WSAENETDOWN: + return Errno::NETDOWN; + case WSAENETUNREACH: + return Errno::NETUNREACH; + default: + return Errno::OTHER; + } +} + +#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX + +using SOCKET = int; +using WSAPOLLFD = pollfd; +using ULONG = u64; + +constexpr SOCKET INVALID_SOCKET = -1; +constexpr SOCKET SOCKET_ERROR = -1; + +constexpr int SD_RECEIVE = SHUT_RD; +constexpr int SD_SEND = SHUT_WR; +constexpr int SD_BOTH = SHUT_RDWR; + +void Initialize() {} + +void Finalize() {} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + + switch (static_cast(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { + return poll(fds, static_cast(nfds), timeout); +} + +int closesocket(SOCKET fd) { + return close(fd); +} + +linger MakeLinger(bool enable, u32 linger_value) { + linger value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = linger_value; + return value; +} + +bool EnableNonBlock(int fd, bool enable) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return false; + } + if (enable) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl(fd, F_SETFL, flags) == 0; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case EBADF: + return Errno::BADF; + case EINVAL: + return Errno::INVAL; + case EMFILE: + return Errno::MFILE; + case ENOTCONN: + return Errno::NOTCONN; + case EAGAIN: + return Errno::AGAIN; + case ECONNREFUSED: + return Errno::CONNREFUSED; + case EHOSTUNREACH: + return Errno::HOSTUNREACH; + case ENETDOWN: + return Errno::NETDOWN; + case ENETUNREACH: + return Errno::NETUNREACH; + default: + return Errno::OTHER; + } +} + +#endif + +Errno GetAndLogLastError() { +#ifdef _WIN32 + int e = WSAGetLastError(); +#else + int e = errno; +#endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } + LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); + return err; +} + +int TranslateDomain(Domain domain) { + switch (domain) { + case Domain::INET: + return AF_INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); + return 0; + } +} + +int TranslateType(Type type) { + switch (type) { + case Type::STREAM: + return SOCK_STREAM; + case Type::DGRAM: + return SOCK_DGRAM; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return 0; + } +} + +int TranslateProtocol(Protocol protocol) { + switch (protocol) { + case Protocol::TCP: + return IPPROTO_TCP; + case Protocol::UDP: + return IPPROTO_UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return 0; + } +} + +SockAddrIn TranslateToSockAddrIn(sockaddr input_) { + sockaddr_in input; + std::memcpy(&input, &input_, sizeof(input)); + + SockAddrIn result; + + switch (input.sin_family) { + case AF_INET: + result.family = Domain::INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); + result.family = Domain::INET; + break; + } + + result.portno = ntohs(input.sin_port); + + result.ip = TranslateIPv4(input.sin_addr); + + return result; +} + +short TranslatePollEvents(PollEvents events) { + short result = 0; + + if (True(events & PollEvents::In)) { + events &= ~PollEvents::In; + result |= POLLIN; + } + if (True(events & PollEvents::Pri)) { + events &= ~PollEvents::Pri; +#ifdef _WIN32 + LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); +#else + result |= POLLPRI; +#endif + } + if (True(events & PollEvents::Out)) { + events &= ~PollEvents::Out; + result |= POLLOUT; + } + + UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); + + return result; +} + +PollEvents TranslatePollRevents(short revents) { + PollEvents result{}; + const auto translate = [&result, &revents](short host, PollEvents guest) { + if ((revents & host) != 0) { + revents &= static_cast(~host); + result |= guest; + } + }; + + translate(POLLIN, PollEvents::In); + translate(POLLPRI, PollEvents::Pri); + translate(POLLOUT, PollEvents::Out); + translate(POLLERR, PollEvents::Err); + translate(POLLHUP, PollEvents::Hup); + + UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); + + return result; +} + +template +Errno SetSockOpt(SOCKET fd, int option, T value) { + const int result = + setsockopt(fd, SOL_SOCKET, option, reinterpret_cast(&value), sizeof(value)); + if (result != SOCKET_ERROR) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +} // Anonymous namespace + +NetworkInstance::NetworkInstance() { + Initialize(); +} + +NetworkInstance::~NetworkInstance() { + Finalize(); +} + +std::optional GetHostIPv4Address() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + char ip_addr[16] = {}; + ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); + return TranslateIPv4(res->ip_address); + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } +} + +std::pair Poll(std::vector& pollfds, s32 timeout) { + const size_t num = pollfds.size(); + + std::vector host_pollfds(pollfds.size()); + std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { + WSAPOLLFD result; + result.fd = fd.socket->fd; + result.events = TranslatePollEvents(fd.events); + result.revents = 0; + return result; + }); + + const int result = WSAPoll(host_pollfds.data(), static_cast(num), timeout); + if (result == 0) { + ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), + [](WSAPOLLFD fd) { return fd.revents == 0; })); + return {0, Errno::SUCCESS}; + } + + for (size_t i = 0; i < num; ++i) { + pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); + } + + if (result > 0) { + return {result, Errno::SUCCESS}; + } + + ASSERT(result == SOCKET_ERROR); + + return {-1, GetAndLogLastError()}; +} + +Socket::~Socket() { + if (fd == INVALID_SOCKET) { + return; + } + (void)closesocket(fd); + fd = INVALID_SOCKET; +} + +Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} + +Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { + fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); + if (fd != INVALID_SOCKET) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::Accept() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + const SOCKET new_socket = accept(fd, &addr, &addrlen); + + if (new_socket == INVALID_SOCKET) { + return {AcceptResult{}, GetAndLogLastError()}; + } + + AcceptResult result; + result.socket = std::make_unique(); + result.socket->fd = new_socket; + + ASSERT(addrlen == sizeof(sockaddr_in)); + result.sockaddr_in = TranslateToSockAddrIn(addr); + + return {std::move(result), Errno::SUCCESS}; +} + +Errno Socket::Connect(SockAddrIn addr_in) { + const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); + if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::GetPeerName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +std::pair Socket::GetSockName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +Errno Socket::Bind(SockAddrIn addr) { + const sockaddr addr_in = TranslateFromSockAddrIn(addr); + if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Listen(s32 backlog) { + if (listen(fd, backlog) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Shutdown(ShutdownHow how) { + int host_how = 0; + switch (how) { + case ShutdownHow::RD: + host_how = SD_RECEIVE; + break; + case ShutdownHow::WR: + host_how = SD_SEND; + break; + case ShutdownHow::RDWR: + host_how = SD_BOTH; + break; + default: + UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); + return Errno::SUCCESS; + } + if (shutdown(fd, host_how) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair Socket::Recv(int flags, std::vector& message) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + + const auto result = + recv(fd, reinterpret_cast(message.data()), static_cast(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::RecvFrom(int flags, std::vector& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + + sockaddr addr_in{}; + socklen_t addrlen = sizeof(addr_in); + socklen_t* const p_addrlen = addr ? &addrlen : nullptr; + sockaddr* const p_addr_in = addr ? &addr_in : nullptr; + + const auto result = recvfrom(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0, p_addr_in, p_addrlen); + if (result != SOCKET_ERROR) { + if (addr) { + ASSERT(addrlen == sizeof(addr_in)); + *addr = TranslateToSockAddrIn(addr_in); + } + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::Send(const std::vector& message, int flags) { + ASSERT(message.size() < static_cast(std::numeric_limits::max())); + ASSERT(flags == 0); + + const auto result = send(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair Socket::SendTo(u32 flags, const std::vector& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + const sockaddr* to = nullptr; + const int tolen = addr ? sizeof(sockaddr) : 0; + sockaddr host_addr_in; + + if (addr) { + host_addr_in = TranslateFromSockAddrIn(*addr); + to = &host_addr_in; + } + + const auto result = sendto(fd, reinterpret_cast(message.data()), + static_cast(message.size()), 0, to, tolen); + if (result != SOCKET_ERROR) { + return {static_cast(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +Errno Socket::Close() { + [[maybe_unused]] const int result = closesocket(fd); + ASSERT(result == 0); + fd = INVALID_SOCKET; + + return Errno::SUCCESS; +} + +Errno Socket::SetLinger(bool enable, u32 linger) { + return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); +} + +Errno Socket::SetReuseAddr(bool enable) { + return SetSockOpt(fd, SO_REUSEADDR, enable ? 1 : 0); +} + +Errno Socket::SetKeepAlive(bool enable) { + return SetSockOpt(fd, SO_KEEPALIVE, enable ? 1 : 0); +} + +Errno Socket::SetBroadcast(bool enable) { + return SetSockOpt(fd, SO_BROADCAST, enable ? 1 : 0); +} + +Errno Socket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno Socket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno Socket::SetSndTimeo(u32 value) { + return SetSockOpt(fd, SO_SNDTIMEO, value); +} + +Errno Socket::SetRcvTimeo(u32 value) { + return SetSockOpt(fd, SO_RCVTIMEO, value); +} + +Errno Socket::SetNonBlock(bool enable) { + if (EnableNonBlock(fd, enable)) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +bool Socket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +} // namespace Network diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h new file mode 100755 index 000000000..10e5ef10d --- /dev/null +++ b/src/core/internal_network/network.h @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" + +#ifdef _WIN32 +#include +#elif YUZU_UNIX +#include +#endif + +namespace Network { + +class Socket; + +/// Error code for network functions +enum class Errno { + SUCCESS, + BADF, + INVAL, + MFILE, + NOTCONN, + AGAIN, + CONNREFUSED, + HOSTUNREACH, + NETDOWN, + NETUNREACH, + OTHER, +}; + +/// Address families +enum class Domain { + INET, ///< Address family for IPv4 +}; + +/// Socket types +enum class Type { + STREAM, + DGRAM, + RAW, + SEQPACKET, +}; + +/// Protocol values for sockets +enum class Protocol { + ICMP, + TCP, + UDP, +}; + +/// Shutdown mode +enum class ShutdownHow { + RD, + WR, + RDWR, +}; + +/// Array of IPv4 address +using IPv4Address = std::array; + +/// Cross-platform sockaddr structure +struct SockAddrIn { + Domain family; + IPv4Address ip; + u16 portno; +}; + +/// Cross-platform poll fd structure + +enum class PollEvents : u16 { + // Using Pascal case because IN is a macro on Windows. + In = 1 << 0, + Pri = 1 << 1, + Out = 1 << 2, + Err = 1 << 3, + Hup = 1 << 4, + Nval = 1 << 5, +}; + +DECLARE_ENUM_FLAG_OPERATORS(PollEvents); + +struct PollFD { + Socket* socket; + PollEvents events; + PollEvents revents; +}; + +class NetworkInstance { +public: + explicit NetworkInstance(); + ~NetworkInstance(); +}; + +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast(bytes), static_cast(bytes >> 8), + static_cast(bytes >> 16), static_cast(bytes >> 24)}; +} +#endif + +/// @brief Returns host's IPv4 address +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional GetHostIPv4Address(); + +} // namespace Network diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp new file mode 100755 index 000000000..0f0a66160 --- /dev/null +++ b/src/core/internal_network/network_interface.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/internal_network/network_interface.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector GetAvailableNetworkInterfaces() { + std::vector adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret != ERROR_BUFFER_OVERFLOW) { + break; + } + + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } + + if (ret != NO_ERROR) { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } + + std::vector result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.emplace_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; +} + +#else + +std::vector GetAvailableNetworkInterfaces() { + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return {}; + } + + std::vector result; + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + u32 gateway{}; + + std::ifstream file{"/proc/net/route"}; + if (!file.is_open()) { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + continue; + } + + // ignore header + file.ignore(std::numeric_limits::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + u32 dest{}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + u16 flags{}; + iss >> flags; + + // flag RTF_GATEWAY (defined in ) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional GetSelectedNetworkInterface() { + const auto& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return std::nullopt; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res == network_interfaces.end()) { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return std::nullopt; + } + + return *res; +} + +} // namespace Network diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h new file mode 100755 index 000000000..9b98b6b42 --- /dev/null +++ b/src/core/internal_network/network_interface.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector GetAvailableNetworkInterfaces(); +std::optional GetSelectedNetworkInterface(); + +} // namespace Network diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h new file mode 100755 index 000000000..77e27e928 --- /dev/null +++ b/src/core/internal_network/sockets.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#if defined(_WIN32) +#elif !YUZU_UNIX +#error "Platform not implemented" +#endif + +#include "common/common_types.h" +#include "core/internal_network/network.h" + +// TODO: C++20 Replace std::vector usages with std::span + +namespace Network { + +class Socket { +public: + struct AcceptResult { + std::unique_ptr socket; + SockAddrIn sockaddr_in; + }; + + explicit Socket() = default; + ~Socket(); + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + Socket(Socket&& rhs) noexcept; + + // Avoid closing sockets implicitly + Socket& operator=(Socket&&) noexcept = delete; + + Errno Initialize(Domain domain, Type type, Protocol protocol); + + Errno Close(); + + std::pair Accept(); + + Errno Connect(SockAddrIn addr_in); + + std::pair GetPeerName(); + + std::pair GetSockName(); + + Errno Bind(SockAddrIn addr); + + Errno Listen(s32 backlog); + + Errno Shutdown(ShutdownHow how); + + std::pair Recv(int flags, std::vector& message); + + std::pair RecvFrom(int flags, std::vector& message, SockAddrIn* addr); + + std::pair Send(const std::vector& message, int flags); + + std::pair SendTo(u32 flags, const std::vector& message, const SockAddrIn* addr); + + Errno SetLinger(bool enable, u32 linger); + + Errno SetReuseAddr(bool enable); + + Errno SetKeepAlive(bool enable); + + Errno SetBroadcast(bool enable); + + Errno SetSndBuf(u32 value); + + Errno SetRcvBuf(u32 value); + + Errno SetSndTimeo(u32 value); + + Errno SetRcvTimeo(u32 value); + + Errno SetNonBlock(bool enable); + + bool IsOpened() const; + +#if defined(_WIN32) + SOCKET fd = INVALID_SOCKET; +#elif YUZU_UNIX + int fd = -1; +#endif +}; + +std::pair Poll(std::vector& poll_fds, s32 timeout); + +} // namespace Network diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h index 597f51cd3..889693e73 100755 --- a/src/input_common/helpers/udp_protocol.h +++ b/src/input_common/helpers/udp_protocol.h @@ -85,7 +85,7 @@ enum RegisterFlags : u8 { struct Version {}; /** * Requests the server to send information about what controllers are plugged into the ports - * In citra's case, we only have one controller, so for simplicity's sake, we can just send a + * In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a * request explicitly for the first controller port and leave it at that. In the future it would be * nice to make this configurable */ diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt new file mode 100755 index 000000000..382a69e2f --- /dev/null +++ b/src/network/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(network STATIC + network.cpp + network.h + packet.cpp + packet.h + room.cpp + room.h + room_member.cpp + room_member.h + verify_user.cpp + verify_user.h +) + +create_target_directory_groups(network) + +target_link_libraries(network PRIVATE common enet Boost::boost) diff --git a/src/network/network.cpp b/src/network/network.cpp new file mode 100755 index 000000000..0841e4134 --- /dev/null +++ b/src/network/network.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "enet/enet.h" +#include "network/network.h" + +namespace Network { + +RoomNetwork::RoomNetwork() { + m_room = std::make_shared(); + m_room_member = std::make_shared(); +} + +bool RoomNetwork::Init() { + if (enet_initialize() != 0) { + LOG_ERROR(Network, "Error initalizing ENet"); + return false; + } + m_room = std::make_shared(); + m_room_member = std::make_shared(); + LOG_DEBUG(Network, "initialized OK"); + return true; +} + +std::weak_ptr RoomNetwork::GetRoom() { + return m_room; +} + +std::weak_ptr RoomNetwork::GetRoomMember() { + return m_room_member; +} + +void RoomNetwork::Shutdown() { + if (m_room_member) { + if (m_room_member->IsConnected()) + m_room_member->Leave(); + m_room_member.reset(); + } + if (m_room) { + if (m_room->GetState() == Room::State::Open) + m_room->Destroy(); + m_room.reset(); + } + enet_deinitialize(); + LOG_DEBUG(Network, "shutdown OK"); +} + +} // namespace Network diff --git a/src/network/network.h b/src/network/network.h new file mode 100755 index 000000000..e4de207b2 --- /dev/null +++ b/src/network/network.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "network/room.h" +#include "network/room_member.h" + +namespace Network { + +class RoomNetwork { +public: + RoomNetwork(); + + /// Initializes and registers the network device, the room, and the room member. + bool Init(); + + /// Returns a pointer to the room handle + std::weak_ptr GetRoom(); + + /// Returns a pointer to the room member handle + std::weak_ptr GetRoomMember(); + + /// Unregisters the network device, the room, and the room member and shut them down. + void Shutdown(); + +private: + std::shared_ptr m_room_member; ///< RoomMember (Client) for network games + std::shared_ptr m_room; ///< Room (Server) for network games +}; + +} // namespace Network diff --git a/src/network/packet.cpp b/src/network/packet.cpp new file mode 100755 index 000000000..0e22f1eb4 --- /dev/null +++ b/src/network/packet.cpp @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#include "network/packet.h" + +namespace Network { + +#ifndef htonll +static u64 htonll(u64 x) { + return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32)); +} +#endif + +#ifndef ntohll +static u64 ntohll(u64 x) { + return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32)); +} +#endif + +void Packet::Append(const void* in_data, std::size_t size_in_bytes) { + if (in_data && (size_in_bytes > 0)) { + std::size_t start = data.size(); + data.resize(start + size_in_bytes); + std::memcpy(&data[start], in_data, size_in_bytes); + } +} + +void Packet::Read(void* out_data, std::size_t size_in_bytes) { + if (out_data && CheckSize(size_in_bytes)) { + std::memcpy(out_data, &data[read_pos], size_in_bytes); + read_pos += size_in_bytes; + } +} + +void Packet::Clear() { + data.clear(); + read_pos = 0; + is_valid = true; +} + +const void* Packet::GetData() const { + return !data.empty() ? &data[0] : nullptr; +} + +void Packet::IgnoreBytes(u32 length) { + read_pos += length; +} + +std::size_t Packet::GetDataSize() const { + return data.size(); +} + +bool Packet::EndOfPacket() const { + return read_pos >= data.size(); +} + +Packet::operator bool() const { + return is_valid; +} + +Packet& Packet::Read(bool& out_data) { + u8 value{}; + if (Read(value)) { + out_data = (value != 0); + } + return *this; +} + +Packet& Packet::Read(s8& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::Read(u8& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::Read(s16& out_data) { + s16 value{}; + Read(&value, sizeof(value)); + out_data = ntohs(value); + return *this; +} + +Packet& Packet::Read(u16& out_data) { + u16 value{}; + Read(&value, sizeof(value)); + out_data = ntohs(value); + return *this; +} + +Packet& Packet::Read(s32& out_data) { + s32 value{}; + Read(&value, sizeof(value)); + out_data = ntohl(value); + return *this; +} + +Packet& Packet::Read(u32& out_data) { + u32 value{}; + Read(&value, sizeof(value)); + out_data = ntohl(value); + return *this; +} + +Packet& Packet::Read(s64& out_data) { + s64 value{}; + Read(&value, sizeof(value)); + out_data = ntohll(value); + return *this; +} + +Packet& Packet::Read(u64& out_data) { + u64 value{}; + Read(&value, sizeof(value)); + out_data = ntohll(value); + return *this; +} + +Packet& Packet::Read(float& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::Read(double& out_data) { + Read(&out_data, sizeof(out_data)); + return *this; +} + +Packet& Packet::Read(char* out_data) { + // First extract string length + u32 length = 0; + Read(length); + + if ((length > 0) && CheckSize(length)) { + // Then extract characters + std::memcpy(out_data, &data[read_pos], length); + out_data[length] = '\0'; + + // Update reading position + read_pos += length; + } + + return *this; +} + +Packet& Packet::Read(std::string& out_data) { + // First extract string length + u32 length = 0; + Read(length); + + out_data.clear(); + if ((length > 0) && CheckSize(length)) { + // Then extract characters + out_data.assign(&data[read_pos], length); + + // Update reading position + read_pos += length; + } + + return *this; +} + +Packet& Packet::Write(bool in_data) { + Write(static_cast(in_data)); + return *this; +} + +Packet& Packet::Write(s8 in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::Write(u8 in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::Write(s16 in_data) { + s16 toWrite = htons(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(u16 in_data) { + u16 toWrite = htons(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(s32 in_data) { + s32 toWrite = htonl(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(u32 in_data) { + u32 toWrite = htonl(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(s64 in_data) { + s64 toWrite = htonll(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(u64 in_data) { + u64 toWrite = htonll(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::Write(float in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::Write(double in_data) { + Append(&in_data, sizeof(in_data)); + return *this; +} + +Packet& Packet::Write(const char* in_data) { + // First insert string length + u32 length = static_cast(std::strlen(in_data)); + Write(length); + + // Then insert characters + Append(in_data, length * sizeof(char)); + + return *this; +} + +Packet& Packet::Write(const std::string& in_data) { + // First insert string length + u32 length = static_cast(in_data.size()); + Write(length); + + // Then insert characters + if (length > 0) + Append(in_data.c_str(), length * sizeof(std::string::value_type)); + + return *this; +} + +bool Packet::CheckSize(std::size_t size) { + is_valid = is_valid && (read_pos + size <= data.size()); + + return is_valid; +} + +} // namespace Network diff --git a/src/network/packet.h b/src/network/packet.h new file mode 100755 index 000000000..e69217488 --- /dev/null +++ b/src/network/packet.h @@ -0,0 +1,165 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/common_types.h" + +namespace Network { + +/// A class that serializes data for network transfer. It also handles endianess +class Packet { +public: + Packet() = default; + ~Packet() = default; + + /** + * Append data to the end of the packet + * @param data Pointer to the sequence of bytes to append + * @param size_in_bytes Number of bytes to append + */ + void Append(const void* data, std::size_t size_in_bytes); + + /** + * Reads data from the current read position of the packet + * @param out_data Pointer where the data should get written to + * @param size_in_bytes Number of bytes to read + */ + void Read(void* out_data, std::size_t size_in_bytes); + + /** + * Clear the packet + * After calling Clear, the packet is empty. + */ + void Clear(); + + /** + * Ignores bytes while reading + * @param length THe number of bytes to ignore + */ + void IgnoreBytes(u32 length); + + /** + * Get a pointer to the data contained in the packet + * @return Pointer to the data + */ + const void* GetData() const; + + /** + * This function returns the number of bytes pointed to by + * what getData returns. + * @return Data size, in bytes + */ + std::size_t GetDataSize() const; + + /** + * This function is useful to know if there is some data + * left to be read, without actually reading it. + * @return True if all data was read, false otherwise + */ + bool EndOfPacket() const; + + explicit operator bool() const; + + /// Overloads of read function to read data from the packet + Packet& Read(bool& out_data); + Packet& Read(s8& out_data); + Packet& Read(u8& out_data); + Packet& Read(s16& out_data); + Packet& Read(u16& out_data); + Packet& Read(s32& out_data); + Packet& Read(u32& out_data); + Packet& Read(s64& out_data); + Packet& Read(u64& out_data); + Packet& Read(float& out_data); + Packet& Read(double& out_data); + Packet& Read(char* out_data); + Packet& Read(std::string& out_data); + template + Packet& Read(std::vector& out_data); + template + Packet& Read(std::array& out_data); + + /// Overloads of write function to write data into the packet + Packet& Write(bool in_data); + Packet& Write(s8 in_data); + Packet& Write(u8 in_data); + Packet& Write(s16 in_data); + Packet& Write(u16 in_data); + Packet& Write(s32 in_data); + Packet& Write(u32 in_data); + Packet& Write(s64 in_data); + Packet& Write(u64 in_data); + Packet& Write(float in_data); + Packet& Write(double in_data); + Packet& Write(const char* in_data); + Packet& Write(const std::string& in_data); + template + Packet& Write(const std::vector& in_data); + template + Packet& Write(const std::array& data); + +private: + /** + * Check if the packet can extract a given number of bytes + * This function updates accordingly the state of the packet. + * @param size Size to check + * @return True if size bytes can be read from the packet + */ + bool CheckSize(std::size_t size); + + // Member data + std::vector data; ///< Data stored in the packet + std::size_t read_pos = 0; ///< Current reading position in the packet + bool is_valid = true; ///< Reading state of the packet +}; + +template +Packet& Packet::Read(std::vector& out_data) { + // First extract the size + u32 size = 0; + Read(size); + out_data.resize(size); + + // Then extract the data + for (std::size_t i = 0; i < out_data.size(); ++i) { + T character; + Read(character); + out_data[i] = character; + } + return *this; +} + +template +Packet& Packet::Read(std::array& out_data) { + for (std::size_t i = 0; i < out_data.size(); ++i) { + T character; + Read(character); + out_data[i] = character; + } + return *this; +} + +template +Packet& Packet::Write(const std::vector& in_data) { + // First insert the size + Write(static_cast(in_data.size())); + + // Then insert the data + for (std::size_t i = 0; i < in_data.size(); ++i) { + Write(in_data[i]); + } + return *this; +} + +template +Packet& Packet::Write(const std::array& in_data) { + for (std::size_t i = 0; i < in_data.size(); ++i) { + Write(in_data[i]); + } + return *this; +} + +} // namespace Network diff --git a/src/network/room.cpp b/src/network/room.cpp new file mode 100755 index 000000000..3fc3a0383 --- /dev/null +++ b/src/network/room.cpp @@ -0,0 +1,1110 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "enet/enet.h" +#include "network/packet.h" +#include "network/room.h" +#include "network/verify_user.h" + +namespace Network { + +class Room::RoomImpl { +public: + // This MAC address is used to generate a 'Nintendo' like Mac address. + const MacAddress NintendoOUI; + std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress + + ENetHost* server = nullptr; ///< Network interface. + + std::atomic state{State::Closed}; ///< Current state of the room. + RoomInformation room_information; ///< Information about this room. + + std::string verify_uid; ///< A GUID which may be used for verfication. + mutable std::mutex verify_uid_mutex; ///< Mutex for verify_uid + + std::string password; ///< The password required to connect to this room. + + struct Member { + std::string nickname; ///< The nickname of the member. + std::string console_id_hash; ///< A hash of the console ID of the member. + GameInfo game_info; ///< The current game of the member + MacAddress mac_address; ///< The assigned mac address of the member. + /// Data of the user, often including authenticated forum username. + VerifyUser::UserData user_data; + ENetPeer* peer; ///< The remote peer. + }; + using MemberList = std::vector; + MemberList members; ///< Information about the members of this room + mutable std::shared_mutex member_mutex; ///< Mutex for locking the members list + + UsernameBanList username_ban_list; ///< List of banned usernames + IPBanList ip_ban_list; ///< List of banned IP addresses + mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists + + RoomImpl() + : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {} + + /// Thread that receives and dispatches network packets + std::unique_ptr room_thread; + + /// Verification backend of the room + std::unique_ptr verify_backend; + + /// Thread function that will receive and dispatch messages until the room is destroyed. + void ServerLoop(); + void StartLoop(); + + /** + * Parses and answers a room join request from a client. + * Validates the uniqueness of the username and assigns the MAC address + * that the client will use for the remainder of the connection. + */ + void HandleJoinRequest(const ENetEvent* event); + + /** + * Parses and answers a kick request from a client. + * Validates the permissions and that the given user exists and then kicks the member. + */ + void HandleModKickPacket(const ENetEvent* event); + + /** + * Parses and answers a ban request from a client. + * Validates the permissions and bans the user (by forum username or IP). + */ + void HandleModBanPacket(const ENetEvent* event); + + /** + * Parses and answers a unban request from a client. + * Validates the permissions and unbans the address. + */ + void HandleModUnbanPacket(const ENetEvent* event); + + /** + * Parses and answers a get ban list request from a client. + * Validates the permissions and returns the ban list. + */ + void HandleModGetBanListPacket(const ENetEvent* event); + + /** + * Returns whether the nickname is valid, ie. isn't already taken by someone else in the room. + */ + bool IsValidNickname(const std::string& nickname) const; + + /** + * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the + * room. + */ + bool IsValidMacAddress(const MacAddress& address) const; + + /** + * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in + * the room. + */ + bool IsValidConsoleId(const std::string& console_id_hash) const; + + /** + * Returns whether a user has mod permissions. + */ + bool HasModPermission(const ENetPeer* client) const; + + /** + * Sends a ID_ROOM_IS_FULL message telling the client that the room is full. + */ + void SendRoomIsFull(ENetPeer* client); + + /** + * Sends a ID_ROOM_NAME_COLLISION message telling the client that the name is invalid. + */ + void SendNameCollision(ENetPeer* client); + + /** + * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. + */ + void SendMacCollision(ENetPeer* client); + + /** + * Sends a IdConsoleIdCollison message telling the client that another member with the same + * console ID exists. + */ + void SendConsoleIdCollision(ENetPeer* client); + + /** + * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. + */ + void SendVersionMismatch(ENetPeer* client); + + /** + * Sends a ID_ROOM_WRONG_PASSWORD message telling the client that the password is wrong. + */ + void SendWrongPassword(ENetPeer* client); + + /** + * Notifies the member that its connection attempt was successful, + * and it is now part of the room. + */ + void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); + + /** + * Notifies the member that its connection attempt was successful, + * and it is now part of the room, and it has been granted mod permissions. + */ + void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); + + /** + * Sends a IdHostKicked message telling the client that they have been kicked. + */ + void SendUserKicked(ENetPeer* client); + + /** + * Sends a IdHostBanned message telling the client that they have been banned. + */ + void SendUserBanned(ENetPeer* client); + + /** + * Sends a IdModPermissionDenied message telling the client that they do not have mod + * permission. + */ + void SendModPermissionDenied(ENetPeer* client); + + /** + * Sends a IdModNoSuchUser message telling the client that the given user could not be found. + */ + void SendModNoSuchUser(ENetPeer* client); + + /** + * Sends the ban list in response to a client's request for getting ban list. + */ + void SendModBanListResponse(ENetPeer* client); + + /** + * Notifies the members that the room is closed, + */ + void SendCloseMessage(); + + /** + * Sends a system message to all the connected clients. + */ + void SendStatusMessage(StatusMessageTypes type, const std::string& nickname, + const std::string& username, const std::string& ip); + + /** + * Sends the information about the room, along with the list of members + * to every connected client in the room. + * The packet has the structure: + * ID_ROOM_INFORMATION + * room_name + * room_description + * member_slots: The max number of clients allowed in this room + * uid + * port + * num_members: the number of currently joined clients + * This is followed by the following three values for each member: + * nickname of that member + * mac_address of that member + * game_name of that member + */ + void BroadcastRoomInformation(); + + /** + * Generates a free MAC address to assign to a new client. + * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 + */ + MacAddress GenerateMacAddress(); + + /** + * Broadcasts this packet to all members except the sender. + * @param event The ENet event containing the data + */ + void HandleWifiPacket(const ENetEvent* event); + + /** + * Extracts a chat entry from a received ENet packet and adds it to the chat queue. + * @param event The ENet event that was received. + */ + void HandleChatPacket(const ENetEvent* event); + + /** + * Extracts the game name from a received ENet packet and broadcasts it. + * @param event The ENet event that was received. + */ + void HandleGameNamePacket(const ENetEvent* event); + + /** + * Removes the client from the members list if it was in it and announces the change + * to all other clients. + */ + void HandleClientDisconnection(ENetPeer* client); +}; + +// RoomImpl +void Room::RoomImpl::ServerLoop() { + while (state != State::Closed) { + ENetEvent event; + if (enet_host_service(server, &event, 16) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + switch (event.packet->data[0]) { + case IdJoinRequest: + HandleJoinRequest(&event); + break; + case IdSetGameInfo: + HandleGameNamePacket(&event); + break; + case IdWifiPacket: + HandleWifiPacket(&event); + break; + case IdChatMessage: + HandleChatPacket(&event); + break; + // Moderation + case IdModKick: + HandleModKickPacket(&event); + break; + case IdModBan: + HandleModBanPacket(&event); + break; + case IdModUnban: + HandleModUnbanPacket(&event); + break; + case IdModGetBanList: + HandleModGetBanListPacket(&event); + break; + } + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + HandleClientDisconnection(event.peer); + break; + case ENET_EVENT_TYPE_NONE: + case ENET_EVENT_TYPE_CONNECT: + break; + } + } + } + // Close the connection to all members: + SendCloseMessage(); +} + +void Room::RoomImpl::StartLoop() { + room_thread = std::make_unique(&Room::RoomImpl::ServerLoop, this); +} + +void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { + { + std::lock_guard lock(member_mutex); + if (members.size() >= room_information.member_slots) { + SendRoomIsFull(event->peer); + return; + } + } + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + std::string nickname; + packet.Read(nickname); + + std::string console_id_hash; + packet.Read(console_id_hash); + + MacAddress preferred_mac; + packet.Read(preferred_mac); + + u32 client_version; + packet.Read(client_version); + + std::string pass; + packet.Read(pass); + + std::string token; + packet.Read(token); + + if (pass != password) { + SendWrongPassword(event->peer); + return; + } + + if (!IsValidNickname(nickname)) { + SendNameCollision(event->peer); + return; + } + + if (preferred_mac != NoPreferredMac) { + // Verify if the preferred mac is available + if (!IsValidMacAddress(preferred_mac)) { + SendMacCollision(event->peer); + return; + } + } else { + // Assign a MAC address of this client automatically + preferred_mac = GenerateMacAddress(); + } + + if (!IsValidConsoleId(console_id_hash)) { + SendConsoleIdCollision(event->peer); + return; + } + + if (client_version != network_version) { + SendVersionMismatch(event->peer); + return; + } + + // At this point the client is ready to be added to the room. + Member member{}; + member.mac_address = preferred_mac; + member.console_id_hash = console_id_hash; + member.nickname = nickname; + member.peer = event->peer; + + std::string uid; + { + std::lock_guard lock(verify_uid_mutex); + uid = verify_uid; + } + member.user_data = verify_backend->LoadUserData(uid, token); + + std::string ip; + { + std::lock_guard lock(ban_list_mutex); + + // Check username ban + if (!member.user_data.username.empty() && + std::find(username_ban_list.begin(), username_ban_list.end(), + member.user_data.username) != username_ban_list.end()) { + + SendUserBanned(event->peer); + return; + } + + // Check IP ban + std::array ip_raw{}; + enet_address_get_host_ip(&event->peer->address, ip_raw.data(), sizeof(ip_raw) - 1); + ip = ip_raw.data(); + + if (std::find(ip_ban_list.begin(), ip_ban_list.end(), ip) != ip_ban_list.end()) { + SendUserBanned(event->peer); + return; + } + } + + // Notify everyone that the user has joined. + SendStatusMessage(IdMemberJoin, member.nickname, member.user_data.username, ip); + + { + std::lock_guard lock(member_mutex); + members.push_back(std::move(member)); + } + + // Notify everyone that the room information has changed. + BroadcastRoomInformation(); + if (HasModPermission(event->peer)) { + SendJoinSuccessAsMod(event->peer, preferred_mac); + } else { + SendJoinSuccess(event->peer, preferred_mac); + } +} + +void Room::RoomImpl::HandleModKickPacket(const ENetEvent* event) { + if (!HasModPermission(event->peer)) { + SendModPermissionDenied(event->peer); + return; + } + + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + std::string nickname; + packet.Read(nickname); + + std::string username, ip; + { + std::lock_guard lock(member_mutex); + const auto target_member = + std::find_if(members.begin(), members.end(), + [&nickname](const auto& member) { return member.nickname == nickname; }); + if (target_member == members.end()) { + SendModNoSuchUser(event->peer); + return; + } + + // Notify the kicked member + SendUserKicked(target_member->peer); + + username = target_member->user_data.username; + + std::array ip_raw{}; + enet_address_get_host_ip(&target_member->peer->address, ip_raw.data(), sizeof(ip_raw) - 1); + ip = ip_raw.data(); + + enet_peer_disconnect(target_member->peer, 0); + members.erase(target_member); + } + + // Announce the change to all clients. + SendStatusMessage(IdMemberKicked, nickname, username, ip); + BroadcastRoomInformation(); +} + +void Room::RoomImpl::HandleModBanPacket(const ENetEvent* event) { + if (!HasModPermission(event->peer)) { + SendModPermissionDenied(event->peer); + return; + } + + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + std::string nickname; + packet.Read(nickname); + + std::string username, ip; + { + std::lock_guard lock(member_mutex); + const auto target_member = + std::find_if(members.begin(), members.end(), + [&nickname](const auto& member) { return member.nickname == nickname; }); + if (target_member == members.end()) { + SendModNoSuchUser(event->peer); + return; + } + + // Notify the banned member + SendUserBanned(target_member->peer); + + nickname = target_member->nickname; + username = target_member->user_data.username; + + std::array ip_raw{}; + enet_address_get_host_ip(&target_member->peer->address, ip_raw.data(), sizeof(ip_raw) - 1); + ip = ip_raw.data(); + + enet_peer_disconnect(target_member->peer, 0); + members.erase(target_member); + } + + { + std::lock_guard lock(ban_list_mutex); + + if (!username.empty()) { + // Ban the forum username + if (std::find(username_ban_list.begin(), username_ban_list.end(), username) == + username_ban_list.end()) { + + username_ban_list.emplace_back(username); + } + } + + // Ban the member's IP as well + if (std::find(ip_ban_list.begin(), ip_ban_list.end(), ip) == ip_ban_list.end()) { + ip_ban_list.emplace_back(ip); + } + } + + // Announce the change to all clients. + SendStatusMessage(IdMemberBanned, nickname, username, ip); + BroadcastRoomInformation(); +} + +void Room::RoomImpl::HandleModUnbanPacket(const ENetEvent* event) { + if (!HasModPermission(event->peer)) { + SendModPermissionDenied(event->peer); + return; + } + + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + std::string address; + packet.Read(address); + + bool unbanned = false; + { + std::lock_guard lock(ban_list_mutex); + + auto it = std::find(username_ban_list.begin(), username_ban_list.end(), address); + if (it != username_ban_list.end()) { + unbanned = true; + username_ban_list.erase(it); + } + + it = std::find(ip_ban_list.begin(), ip_ban_list.end(), address); + if (it != ip_ban_list.end()) { + unbanned = true; + ip_ban_list.erase(it); + } + } + + if (unbanned) { + SendStatusMessage(IdAddressUnbanned, address, "", ""); + } else { + SendModNoSuchUser(event->peer); + } +} + +void Room::RoomImpl::HandleModGetBanListPacket(const ENetEvent* event) { + if (!HasModPermission(event->peer)) { + SendModPermissionDenied(event->peer); + return; + } + + SendModBanListResponse(event->peer); +} + +bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { + // A nickname is valid if it matches the regex and is not already taken by anybody else in the + // room. + const std::regex nickname_regex("^[ a-zA-Z0-9._-]{4,20}$"); + if (!std::regex_match(nickname, nickname_regex)) + return false; + + std::lock_guard lock(member_mutex); + return std::all_of(members.begin(), members.end(), + [&nickname](const auto& member) { return member.nickname != nickname; }); +} + +bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { + // A MAC address is valid if it is not already taken by anybody else in the room. + std::lock_guard lock(member_mutex); + return std::all_of(members.begin(), members.end(), + [&address](const auto& member) { return member.mac_address != address; }); +} + +bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const { + // A Console ID is valid if it is not already taken by anybody else in the room. + std::lock_guard lock(member_mutex); + return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) { + return member.console_id_hash != console_id_hash; + }); +} + +bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { + std::lock_guard lock(member_mutex); + const auto sending_member = + std::find_if(members.begin(), members.end(), + [client](const auto& member) { return member.peer == client; }); + if (sending_member == members.end()) { + return false; + } + if (room_information.enable_yuzu_mods && + sending_member->user_data.moderator) { // Community moderator + + return true; + } + if (!room_information.host_username.empty() && + sending_member->user_data.username == room_information.host_username) { // Room host + + return true; + } + return false; +} + +void Room::RoomImpl::SendNameCollision(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdNameCollision)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendMacCollision(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdMacCollision)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdConsoleIdCollision)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendWrongPassword(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdWrongPassword)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendRoomIsFull(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdRoomIsFull)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdVersionMismatch)); + packet.Write(network_version); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { + Packet packet; + packet.Write(static_cast(IdJoinSuccess)); + packet.Write(mac_address); + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { + Packet packet; + packet.Write(static_cast(IdJoinSuccessAsMod)); + packet.Write(mac_address); + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendUserKicked(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdHostKicked)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendUserBanned(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdHostBanned)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendModPermissionDenied(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdModPermissionDenied)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendModNoSuchUser(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdModNoSuchUser)); + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendModBanListResponse(ENetPeer* client) { + Packet packet; + packet.Write(static_cast(IdModBanListResponse)); + { + std::lock_guard lock(ban_list_mutex); + packet.Write(username_ban_list); + packet.Write(ip_ban_list); + } + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(client, 0, enet_packet); + enet_host_flush(server); +} + +void Room::RoomImpl::SendCloseMessage() { + Packet packet; + packet.Write(static_cast(IdCloseRoom)); + std::lock_guard lock(member_mutex); + if (!members.empty()) { + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + for (auto& member : members) { + enet_peer_send(member.peer, 0, enet_packet); + } + } + enet_host_flush(server); + for (auto& member : members) { + enet_peer_disconnect(member.peer, 0); + } +} + +void Room::RoomImpl::SendStatusMessage(StatusMessageTypes type, const std::string& nickname, + const std::string& username, const std::string& ip) { + Packet packet; + packet.Write(static_cast(IdStatusMessage)); + packet.Write(static_cast(type)); + packet.Write(nickname); + packet.Write(username); + std::lock_guard lock(member_mutex); + if (!members.empty()) { + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + for (auto& member : members) { + enet_peer_send(member.peer, 0, enet_packet); + } + } + enet_host_flush(server); + + const std::string display_name = + username.empty() ? nickname : fmt::format("{} ({})", nickname, username); + + switch (type) { + case IdMemberJoin: + LOG_INFO(Network, "[{}] {} has joined.", ip, display_name); + break; + case IdMemberLeave: + LOG_INFO(Network, "[{}] {} has left.", ip, display_name); + break; + case IdMemberKicked: + LOG_INFO(Network, "[{}] {} has been kicked.", ip, display_name); + break; + case IdMemberBanned: + LOG_INFO(Network, "[{}] {} has been banned.", ip, display_name); + break; + case IdAddressUnbanned: + LOG_INFO(Network, "{} has been unbanned.", display_name); + break; + } +} + +void Room::RoomImpl::BroadcastRoomInformation() { + Packet packet; + packet.Write(static_cast(IdRoomInformation)); + packet.Write(room_information.name); + packet.Write(room_information.description); + packet.Write(room_information.member_slots); + packet.Write(room_information.port); + packet.Write(room_information.preferred_game.name); + packet.Write(room_information.host_username); + + packet.Write(static_cast(members.size())); + { + std::lock_guard lock(member_mutex); + for (const auto& member : members) { + packet.Write(member.nickname); + packet.Write(member.mac_address); + packet.Write(member.game_info.name); + packet.Write(member.game_info.id); + packet.Write(member.user_data.username); + packet.Write(member.user_data.display_name); + packet.Write(member.user_data.avatar_url); + } + } + + ENetPacket* enet_packet = + enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(server, 0, enet_packet); + enet_host_flush(server); +} + +MacAddress Room::RoomImpl::GenerateMacAddress() { + MacAddress result_mac = + NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI + std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF + do { + for (std::size_t i = 3; i < result_mac.size(); ++i) { + result_mac[i] = dis(random_gen); + } + } while (!IsValidMacAddress(result_mac)); + return result_mac; +} + +void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { + Packet in_packet; + in_packet.Append(event->packet->data, event->packet->dataLength); + in_packet.IgnoreBytes(sizeof(u8)); // Message type + in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type + in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel + in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address + MacAddress destination_address; + in_packet.Read(destination_address); + + Packet out_packet; + out_packet.Append(event->packet->data, event->packet->dataLength); + ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + + if (destination_address == BroadcastMac) { // Send the data to everyone except the sender + std::lock_guard lock(member_mutex); + bool sent_packet = false; + for (const auto& member : members) { + if (member.peer != event->peer) { + sent_packet = true; + enet_peer_send(member.peer, 0, enet_packet); + } + } + + if (!sent_packet) { + enet_packet_destroy(enet_packet); + } + } else { // Send the data only to the destination client + std::lock_guard lock(member_mutex); + auto member = std::find_if(members.begin(), members.end(), + [destination_address](const Member& member_entry) -> bool { + return member_entry.mac_address == destination_address; + }); + if (member != members.end()) { + enet_peer_send(member->peer, 0, enet_packet); + } else { + LOG_ERROR(Network, + "Attempting to send to unknown MAC address: " + "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", + destination_address[0], destination_address[1], destination_address[2], + destination_address[3], destination_address[4], destination_address[5]); + enet_packet_destroy(enet_packet); + } + } + enet_host_flush(server); +} + +void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { + Packet in_packet; + in_packet.Append(event->packet->data, event->packet->dataLength); + + in_packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + std::string message; + in_packet.Read(message); + auto CompareNetworkAddress = [event](const Member member) -> bool { + return member.peer == event->peer; + }; + + std::lock_guard lock(member_mutex); + const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress); + if (sending_member == members.end()) { + return; // Received a chat message from a unknown sender + } + + // Limit the size of chat messages to MaxMessageSize + message.resize(std::min(static_cast(message.size()), MaxMessageSize)); + + Packet out_packet; + out_packet.Write(static_cast(IdChatMessage)); + out_packet.Write(sending_member->nickname); + out_packet.Write(sending_member->user_data.username); + out_packet.Write(message); + + ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + bool sent_packet = false; + for (const auto& member : members) { + if (member.peer != event->peer) { + sent_packet = true; + enet_peer_send(member.peer, 0, enet_packet); + } + } + + if (!sent_packet) { + enet_packet_destroy(enet_packet); + } + + enet_host_flush(server); + + if (sending_member->user_data.username.empty()) { + LOG_INFO(Network, "{}: {}", sending_member->nickname, message); + } else { + LOG_INFO(Network, "{} ({}): {}", sending_member->nickname, + sending_member->user_data.username, message); + } +} + +void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { + Packet in_packet; + in_packet.Append(event->packet->data, event->packet->dataLength); + + in_packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + GameInfo game_info; + in_packet.Read(game_info.name); + in_packet.Read(game_info.id); + + { + std::lock_guard lock(member_mutex); + auto member = std::find_if(members.begin(), members.end(), + [event](const Member& member_entry) -> bool { + return member_entry.peer == event->peer; + }); + if (member != members.end()) { + member->game_info = game_info; + + const std::string display_name = + member->user_data.username.empty() + ? member->nickname + : fmt::format("{} ({})", member->nickname, member->user_data.username); + + if (game_info.name.empty()) { + LOG_INFO(Network, "{} is not playing", display_name); + } else { + LOG_INFO(Network, "{} is playing {}", display_name, game_info.name); + } + } + } + BroadcastRoomInformation(); +} + +void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { + // Remove the client from the members list. + std::string nickname, username, ip; + { + std::lock_guard lock(member_mutex); + auto member = + std::find_if(members.begin(), members.end(), [client](const Member& member_entry) { + return member_entry.peer == client; + }); + if (member != members.end()) { + nickname = member->nickname; + username = member->user_data.username; + + std::array ip_raw{}; + enet_address_get_host_ip(&member->peer->address, ip_raw.data(), sizeof(ip_raw) - 1); + ip = ip_raw.data(); + + members.erase(member); + } + } + + // Announce the change to all clients. + enet_peer_disconnect(client, 0); + if (!nickname.empty()) + SendStatusMessage(IdMemberLeave, nickname, username, ip); + BroadcastRoomInformation(); +} + +// Room +Room::Room() : room_impl{std::make_unique()} {} + +Room::~Room() = default; + +bool Room::Create(const std::string& name, const std::string& description, + const std::string& server_address, u16 server_port, const std::string& password, + const u32 max_connections, const std::string& host_username, + const GameInfo preferred_game, + std::unique_ptr verify_backend, + const Room::BanList& ban_list, bool enable_yuzu_mods) { + ENetAddress address; + address.host = ENET_HOST_ANY; + if (!server_address.empty()) { + enet_address_set_host(&address, server_address.c_str()); + } + address.port = server_port; + + // In order to send the room is full message to the connecting client, we need to leave one + // slot open so enet won't reject the incoming connection without telling us + room_impl->server = enet_host_create(&address, max_connections + 1, NumChannels, 0, 0); + if (!room_impl->server) { + return false; + } + room_impl->state = State::Open; + + room_impl->room_information.name = name; + room_impl->room_information.description = description; + room_impl->room_information.member_slots = max_connections; + room_impl->room_information.port = server_port; + room_impl->room_information.preferred_game = preferred_game; + room_impl->room_information.host_username = host_username; + room_impl->room_information.enable_yuzu_mods = enable_yuzu_mods; + room_impl->password = password; + room_impl->verify_backend = std::move(verify_backend); + room_impl->username_ban_list = ban_list.first; + room_impl->ip_ban_list = ban_list.second; + + room_impl->StartLoop(); + return true; +} + +Room::State Room::GetState() const { + return room_impl->state; +} + +const RoomInformation& Room::GetRoomInformation() const { + return room_impl->room_information; +} + +std::string Room::GetVerifyUID() const { + std::lock_guard lock(room_impl->verify_uid_mutex); + return room_impl->verify_uid; +} + +Room::BanList Room::GetBanList() const { + std::lock_guard lock(room_impl->ban_list_mutex); + return {room_impl->username_ban_list, room_impl->ip_ban_list}; +} + +std::vector Room::GetRoomMemberList() const { + std::vector member_list; + std::lock_guard lock(room_impl->member_mutex); + for (const auto& member_impl : room_impl->members) { + Member member; + member.nickname = member_impl.nickname; + member.username = member_impl.user_data.username; + member.display_name = member_impl.user_data.display_name; + member.avatar_url = member_impl.user_data.avatar_url; + member.mac_address = member_impl.mac_address; + member.game = member_impl.game_info; + member_list.push_back(member); + } + return member_list; +} + +bool Room::HasPassword() const { + return !room_impl->password.empty(); +} + +void Room::SetVerifyUID(const std::string& uid) { + std::lock_guard lock(room_impl->verify_uid_mutex); + room_impl->verify_uid = uid; +} + +void Room::Destroy() { + room_impl->state = State::Closed; + room_impl->room_thread->join(); + room_impl->room_thread.reset(); + + if (room_impl->server) { + enet_host_destroy(room_impl->server); + } + room_impl->room_information = {}; + room_impl->server = nullptr; + { + std::lock_guard lock(room_impl->member_mutex); + room_impl->members.clear(); + } + room_impl->room_information.member_slots = 0; + room_impl->room_information.name.clear(); +} + +} // namespace Network diff --git a/src/network/room.h b/src/network/room.h new file mode 100755 index 000000000..6f7e3b5b5 --- /dev/null +++ b/src/network/room.h @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" +#include "network/verify_user.h" + +namespace Network { + +using AnnounceMultiplayerRoom::GameInfo; +using AnnounceMultiplayerRoom::MacAddress; +using AnnounceMultiplayerRoom::Member; +using AnnounceMultiplayerRoom::RoomInformation; + +constexpr u32 network_version = 1; ///< The version of this Room and RoomMember + +constexpr u16 DefaultRoomPort = 24872; + +constexpr u32 MaxMessageSize = 500; + +/// Maximum number of concurrent connections allowed to this room. +static constexpr u32 MaxConcurrentConnections = 254; + +constexpr std::size_t NumChannels = 1; // Number of channels used for the connection + +/// A special MAC address that tells the room we're joining to assign us a MAC address +/// automatically. +constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +// 802.11 broadcast MAC address +constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +// The different types of messages that can be sent. The first byte of each packet defines the type +enum RoomMessageTypes : u8 { + IdJoinRequest = 1, + IdJoinSuccess, + IdRoomInformation, + IdSetGameInfo, + IdWifiPacket, + IdChatMessage, + IdNameCollision, + IdMacCollision, + IdVersionMismatch, + IdWrongPassword, + IdCloseRoom, + IdRoomIsFull, + IdConsoleIdCollision, + IdStatusMessage, + IdHostKicked, + IdHostBanned, + /// Moderation requests + IdModKick, + IdModBan, + IdModUnban, + IdModGetBanList, + // Moderation responses + IdModBanListResponse, + IdModPermissionDenied, + IdModNoSuchUser, + IdJoinSuccessAsMod, +}; + +/// Types of system status messages +enum StatusMessageTypes : u8 { + IdMemberJoin = 1, ///< Member joining + IdMemberLeave, ///< Member leaving + IdMemberKicked, ///< A member is kicked from the room + IdMemberBanned, ///< A member is banned from the room + IdAddressUnbanned, ///< A username / ip address is unbanned from the room +}; + +/// This is what a server [person creating a server] would use. +class Room final { +public: + enum class State : u8 { + Open, ///< The room is open and ready to accept connections. + Closed, ///< The room is not opened and can not accept connections. + }; + + Room(); + ~Room(); + + /** + * Gets the current state of the room. + */ + State GetState() const; + + /** + * Gets the room information of the room. + */ + const RoomInformation& GetRoomInformation() const; + + /** + * Gets the verify UID of this room. + */ + std::string GetVerifyUID() const; + + /** + * Gets a list of the mbmers connected to the room. + */ + std::vector GetRoomMemberList() const; + + /** + * Checks if the room is password protected + */ + bool HasPassword() const; + + using UsernameBanList = std::vector; + using IPBanList = std::vector; + + using BanList = std::pair; + + /** + * Creates the socket for this room. Will bind to default address if + * server is empty string. + */ + bool Create(const std::string& name, const std::string& description = "", + const std::string& server = "", u16 server_port = DefaultRoomPort, + const std::string& password = "", + const u32 max_connections = MaxConcurrentConnections, + const std::string& host_username = "", const GameInfo = {}, + std::unique_ptr verify_backend = nullptr, + const BanList& ban_list = {}, bool enable_yuzu_mods = false); + + /** + * Sets the verification GUID of the room. + */ + void SetVerifyUID(const std::string& uid); + + /** + * Gets the ban list (including banned forum usernames and IPs) of the room. + */ + BanList GetBanList() const; + + /** + * Destroys the socket + */ + void Destroy(); + +private: + class RoomImpl; + std::unique_ptr room_impl; +}; + +} // namespace Network diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp new file mode 100755 index 000000000..e4f823e98 --- /dev/null +++ b/src/network/room_member.cpp @@ -0,0 +1,696 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "enet/enet.h" +#include "network/packet.h" +#include "network/room_member.h" + +namespace Network { + +constexpr u32 ConnectionTimeoutMs = 5000; + +class RoomMember::RoomMemberImpl { +public: + ENetHost* client = nullptr; ///< ENet network interface. + ENetPeer* server = nullptr; ///< The server peer the client is connected to + + /// Information about the clients connected to the same room as us. + MemberList member_information; + /// Information about the room we're connected to. + RoomInformation room_information; + + /// The current game name, id and version + GameInfo current_game_info; + + std::atomic state{State::Idle}; ///< Current state of the RoomMember. + void SetState(const State new_state); + void SetError(const Error new_error); + bool IsConnected() const; + + std::string nickname; ///< The nickname of this member. + + std::string username; ///< The username of this member. + mutable std::mutex username_mutex; ///< Mutex for locking username. + + MacAddress mac_address; ///< The mac_address of this member. + + std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. + /// Thread that receives and dispatches network packets + std::unique_ptr loop_thread; + std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable. + std::list send_list; ///< A list that stores all packets to send the async + + template + using CallbackSet = std::set>; + std::mutex callback_mutex; ///< The mutex used for handling callbacks + + class Callbacks { + public: + template + CallbackSet& Get(); + + private: + CallbackSet callback_set_wifi_packet; + CallbackSet callback_set_chat_messages; + CallbackSet callback_set_status_messages; + CallbackSet callback_set_room_information; + CallbackSet callback_set_state; + CallbackSet callback_set_error; + CallbackSet callback_set_ban_list; + }; + Callbacks callbacks; ///< All CallbackSets to all events + + void MemberLoop(); + + void StartLoop(); + + /** + * Sends data to the room. It will be send on channel 0 with flag RELIABLE + * @param packet The data to send + */ + void Send(Packet&& packet); + + /** + * Sends a request to the server, asking for permission to join a room with the specified + * nickname and preferred mac. + * @params nickname The desired nickname. + * @params console_id_hash A hash of the Console ID. + * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells + * @params password The password for the room + * the server to assign one for us. + */ + void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, + const MacAddress& preferred_mac = NoPreferredMac, + const std::string& password = "", const std::string& token = ""); + + /** + * Extracts a MAC Address from a received ENet packet. + * @param event The ENet event that was received. + */ + void HandleJoinPacket(const ENetEvent* event); + /** + * Extracts RoomInformation and MemberInformation from a received ENet packet. + * @param event The ENet event that was received. + */ + void HandleRoomInformationPacket(const ENetEvent* event); + + /** + * Extracts a WifiPacket from a received ENet packet. + * @param event The ENet event that was received. + */ + void HandleWifiPackets(const ENetEvent* event); + + /** + * Extracts a chat entry from a received ENet packet and adds it to the chat queue. + * @param event The ENet event that was received. + */ + void HandleChatPacket(const ENetEvent* event); + + /** + * Extracts a system message entry from a received ENet packet and adds it to the system message + * queue. + * @param event The ENet event that was received. + */ + void HandleStatusMessagePacket(const ENetEvent* event); + + /** + * Extracts a ban list request response from a received ENet packet. + * @param event The ENet event that was received. + */ + void HandleModBanListResponsePacket(const ENetEvent* event); + + /** + * Disconnects the RoomMember from the Room + */ + void Disconnect(); + + template + void Invoke(const T& data); + + template + CallbackHandle Bind(std::function callback); +}; + +// RoomMemberImpl +void RoomMember::RoomMemberImpl::SetState(const State new_state) { + if (state != new_state) { + state = new_state; + Invoke(state); + } +} + +void RoomMember::RoomMemberImpl::SetError(const Error new_error) { + Invoke(new_error); +} + +bool RoomMember::RoomMemberImpl::IsConnected() const { + return state == State::Joining || state == State::Joined || state == State::Moderator; +} + +void RoomMember::RoomMemberImpl::MemberLoop() { + // Receive packets while the connection is open + while (IsConnected()) { + std::lock_guard lock(network_mutex); + ENetEvent event; + if (enet_host_service(client, &event, 16) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + switch (event.packet->data[0]) { + case IdWifiPacket: + HandleWifiPackets(&event); + break; + case IdChatMessage: + HandleChatPacket(&event); + break; + case IdStatusMessage: + HandleStatusMessagePacket(&event); + break; + case IdRoomInformation: + HandleRoomInformationPacket(&event); + break; + case IdJoinSuccess: + case IdJoinSuccessAsMod: + // The join request was successful, we are now in the room. + // If we joined successfully, there must be at least one client in the room: us. + ASSERT_MSG(member_information.size() > 0, + "We have not yet received member information."); + HandleJoinPacket(&event); // Get the MAC Address for the client + if (event.packet->data[0] == IdJoinSuccessAsMod) { + SetState(State::Moderator); + } else { + SetState(State::Joined); + } + break; + case IdModBanListResponse: + HandleModBanListResponsePacket(&event); + break; + case IdRoomIsFull: + SetState(State::Idle); + SetError(Error::RoomIsFull); + break; + case IdNameCollision: + SetState(State::Idle); + SetError(Error::NameCollision); + break; + case IdMacCollision: + SetState(State::Idle); + SetError(Error::MacCollision); + break; + case IdConsoleIdCollision: + SetState(State::Idle); + SetError(Error::ConsoleIdCollision); + break; + case IdVersionMismatch: + SetState(State::Idle); + SetError(Error::WrongVersion); + break; + case IdWrongPassword: + SetState(State::Idle); + SetError(Error::WrongPassword); + break; + case IdCloseRoom: + SetState(State::Idle); + SetError(Error::LostConnection); + break; + case IdHostKicked: + SetState(State::Idle); + SetError(Error::HostKicked); + break; + case IdHostBanned: + SetState(State::Idle); + SetError(Error::HostBanned); + break; + case IdModPermissionDenied: + SetError(Error::PermissionDenied); + break; + case IdModNoSuchUser: + SetError(Error::NoSuchUser); + break; + } + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + if (state == State::Joined || state == State::Moderator) { + SetState(State::Idle); + SetError(Error::LostConnection); + } + break; + case ENET_EVENT_TYPE_NONE: + break; + case ENET_EVENT_TYPE_CONNECT: + // The ENET_EVENT_TYPE_CONNECT event can not possibly happen here because we're + // already connected + ASSERT_MSG(false, "Received unexpected connect event while already connected"); + break; + } + } + std::list packets; + { + std::lock_guard send_lock(send_list_mutex); + packets.swap(send_list); + } + for (const auto& packet : packets) { + ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(server, 0, enetPacket); + } + enet_host_flush(client); + } + Disconnect(); +}; + +void RoomMember::RoomMemberImpl::StartLoop() { + loop_thread = std::make_unique(&RoomMember::RoomMemberImpl::MemberLoop, this); +} + +void RoomMember::RoomMemberImpl::Send(Packet&& packet) { + std::lock_guard lock(send_list_mutex); + send_list.push_back(std::move(packet)); +} + +void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, + const std::string& console_id_hash, + const MacAddress& preferred_mac, + const std::string& password, + const std::string& token) { + Packet packet; + packet.Write(static_cast(IdJoinRequest)); + packet.Write(nickname_); + packet.Write(console_id_hash); + packet.Write(preferred_mac); + packet.Write(network_version); + packet.Write(password); + packet.Write(token); + Send(std::move(packet)); +} + +void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* event) { + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + RoomInformation info{}; + packet.Read(info.name); + packet.Read(info.description); + packet.Read(info.member_slots); + packet.Read(info.port); + packet.Read(info.preferred_game.name); + packet.Read(info.host_username); + room_information.name = info.name; + room_information.description = info.description; + room_information.member_slots = info.member_slots; + room_information.port = info.port; + room_information.preferred_game = info.preferred_game; + room_information.host_username = info.host_username; + + u32 num_members; + packet.Read(num_members); + member_information.resize(num_members); + + for (auto& member : member_information) { + packet.Read(member.nickname); + packet.Read(member.mac_address); + packet.Read(member.game_info.name); + packet.Read(member.game_info.id); + packet.Read(member.username); + packet.Read(member.display_name); + packet.Read(member.avatar_url); + + { + std::lock_guard lock(username_mutex); + if (member.nickname == nickname) { + username = member.username; + } + } + } + Invoke(room_information); +} + +void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + // Parse the MAC Address from the packet + packet.Read(mac_address); +} + +void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { + WifiPacket wifi_packet{}; + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + // Parse the WifiPacket from the packet + u8 frame_type; + packet.Read(frame_type); + WifiPacket::PacketType type = static_cast(frame_type); + + wifi_packet.type = type; + packet.Read(wifi_packet.channel); + packet.Read(wifi_packet.transmitter_address); + packet.Read(wifi_packet.destination_address); + packet.Read(wifi_packet.data); + + Invoke(wifi_packet); +} + +void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); + + ChatEntry chat_entry{}; + packet.Read(chat_entry.nickname); + packet.Read(chat_entry.username); + packet.Read(chat_entry.message); + Invoke(chat_entry); +} + +void RoomMember::RoomMemberImpl::HandleStatusMessagePacket(const ENetEvent* event) { + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); + + StatusMessageEntry status_message_entry{}; + u8 type{}; + packet.Read(type); + status_message_entry.type = static_cast(type); + packet.Read(status_message_entry.nickname); + packet.Read(status_message_entry.username); + Invoke(status_message_entry); +} + +void RoomMember::RoomMemberImpl::HandleModBanListResponsePacket(const ENetEvent* event) { + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); + + Room::BanList ban_list = {}; + packet.Read(ban_list.first); + packet.Read(ban_list.second); + Invoke(ban_list); +} + +void RoomMember::RoomMemberImpl::Disconnect() { + member_information.clear(); + room_information.member_slots = 0; + room_information.name.clear(); + + if (!server) { + return; + } + enet_peer_disconnect(server, 0); + + ENetEvent event; + while (enet_host_service(client, &event, ConnectionTimeoutMs) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(event.packet); // Ignore all incoming data + break; + case ENET_EVENT_TYPE_DISCONNECT: + server = nullptr; + return; + case ENET_EVENT_TYPE_NONE: + case ENET_EVENT_TYPE_CONNECT: + break; + } + } + // didn't disconnect gracefully force disconnect + enet_peer_reset(server); + server = nullptr; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_wifi_packet; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_state; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_error; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_room_information; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_chat_messages; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_status_messages; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_ban_list; +} + +template +void RoomMember::RoomMemberImpl::Invoke(const T& data) { + std::lock_guard lock(callback_mutex); + CallbackSet callback_set = callbacks.Get(); + for (auto const& callback : callback_set) { + (*callback)(data); + } +} + +template +RoomMember::CallbackHandle RoomMember::RoomMemberImpl::Bind( + std::function callback) { + std::lock_guard lock(callback_mutex); + CallbackHandle handle; + handle = std::make_shared>(callback); + callbacks.Get().insert(handle); + return handle; +} + +// RoomMember +RoomMember::RoomMember() : room_member_impl{std::make_unique()} {} + +RoomMember::~RoomMember() { + ASSERT_MSG(!IsConnected(), "RoomMember is being destroyed while connected"); + if (room_member_impl->loop_thread) { + Leave(); + } +} + +RoomMember::State RoomMember::GetState() const { + return room_member_impl->state; +} + +const RoomMember::MemberList& RoomMember::GetMemberInformation() const { + return room_member_impl->member_information; +} + +const std::string& RoomMember::GetNickname() const { + return room_member_impl->nickname; +} + +const std::string& RoomMember::GetUsername() const { + std::lock_guard lock(room_member_impl->username_mutex); + return room_member_impl->username; +} + +const MacAddress& RoomMember::GetMacAddress() const { + ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); + return room_member_impl->mac_address; +} + +RoomInformation RoomMember::GetRoomInformation() const { + return room_member_impl->room_information; +} + +void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, + const char* server_addr, u16 server_port, u16 client_port, + const MacAddress& preferred_mac, const std::string& password, + const std::string& token) { + // If the member is connected, kill the connection first + if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { + Leave(); + } + // If the thread isn't running but the ptr still exists, reset it + else if (room_member_impl->loop_thread) { + room_member_impl->loop_thread.reset(); + } + + if (!room_member_impl->client) { + room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); + ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client"); + } + + room_member_impl->SetState(State::Joining); + + ENetAddress address{}; + enet_address_set_host(&address, server_addr); + address.port = server_port; + room_member_impl->server = + enet_host_connect(room_member_impl->client, &address, NumChannels, 0); + + if (!room_member_impl->server) { + room_member_impl->SetState(State::Idle); + room_member_impl->SetError(Error::UnknownError); + return; + } + + ENetEvent event{}; + int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs); + if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { + room_member_impl->nickname = nick; + room_member_impl->StartLoop(); + room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); + SendGameInfo(room_member_impl->current_game_info); + } else { + enet_peer_disconnect(room_member_impl->server, 0); + room_member_impl->SetState(State::Idle); + room_member_impl->SetError(Error::CouldNotConnect); + } +} + +bool RoomMember::IsConnected() const { + return room_member_impl->IsConnected(); +} + +void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { + Packet packet; + packet.Write(static_cast(IdWifiPacket)); + packet.Write(static_cast(wifi_packet.type)); + packet.Write(wifi_packet.channel); + packet.Write(wifi_packet.transmitter_address); + packet.Write(wifi_packet.destination_address); + packet.Write(wifi_packet.data); + room_member_impl->Send(std::move(packet)); +} + +void RoomMember::SendChatMessage(const std::string& message) { + Packet packet; + packet.Write(static_cast(IdChatMessage)); + packet.Write(message); + room_member_impl->Send(std::move(packet)); +} + +void RoomMember::SendGameInfo(const GameInfo& game_info) { + room_member_impl->current_game_info = game_info; + if (!IsConnected()) + return; + + Packet packet; + packet.Write(static_cast(IdSetGameInfo)); + packet.Write(game_info.name); + packet.Write(game_info.id); + room_member_impl->Send(std::move(packet)); +} + +void RoomMember::SendModerationRequest(RoomMessageTypes type, const std::string& nickname) { + ASSERT_MSG(type == IdModKick || type == IdModBan || type == IdModUnban, + "type is not a moderation request"); + if (!IsConnected()) + return; + + Packet packet; + packet.Write(static_cast(type)); + packet.Write(nickname); + room_member_impl->Send(std::move(packet)); +} + +void RoomMember::RequestBanList() { + if (!IsConnected()) + return; + + Packet packet; + packet.Write(static_cast(IdModGetBanList)); + room_member_impl->Send(std::move(packet)); +} + +RoomMember::CallbackHandle RoomMember::BindOnStateChanged( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnError( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnWifiPacketReceived( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnRoomInformationChanged( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnChatMessageRecieved( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnStatusMessageReceived( + std::function callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle RoomMember::BindOnBanListReceived( + std::function callback) { + return room_member_impl->Bind(callback); +} + +template +void RoomMember::Unbind(CallbackHandle handle) { + std::lock_guard lock(room_member_impl->callback_mutex); + room_member_impl->callbacks.Get().erase(handle); +} + +void RoomMember::Leave() { + room_member_impl->SetState(State::Idle); + room_member_impl->loop_thread->join(); + room_member_impl->loop_thread.reset(); + + enet_host_destroy(room_member_impl->client); + room_member_impl->client = nullptr; +} + +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); + +} // namespace Network diff --git a/src/network/room_member.h b/src/network/room_member.h new file mode 100755 index 000000000..bbb7d13d4 --- /dev/null +++ b/src/network/room_member.h @@ -0,0 +1,318 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/announce_multiplayer_room.h" +#include "common/common_types.h" +#include "network/room.h" + +namespace Network { + +using AnnounceMultiplayerRoom::GameInfo; +using AnnounceMultiplayerRoom::RoomInformation; + +/// Information about the received WiFi packets. +/// Acts as our own 802.11 header. +struct WifiPacket { + enum class PacketType : u8 { + Beacon, + Data, + Authentication, + AssociationResponse, + Deauthentication, + NodeMap + }; + PacketType type; ///< The type of 802.11 frame. + std::vector data; ///< Raw 802.11 frame data, starting at the management frame header + /// for management frames. + MacAddress transmitter_address; ///< Mac address of the transmitter. + MacAddress destination_address; ///< Mac address of the receiver. + u8 channel; ///< WiFi channel where this frame was transmitted. +}; + +/// Represents a chat message. +struct ChatEntry { + std::string nickname; ///< Nickname of the client who sent this message. + /// Web services username of the client who sent this message, can be empty. + std::string username; + std::string message; ///< Body of the message. +}; + +/// Represents a system status message. +struct StatusMessageEntry { + StatusMessageTypes type; ///< Type of the message + /// Subject of the message. i.e. the user who is joining/leaving/being banned, etc. + std::string nickname; + std::string username; +}; + +/** + * This is what a client [person joining a server] would use. + * It also has to be used if you host a game yourself (You'd create both, a Room and a + * RoomMembership for yourself) + */ +class RoomMember final { +public: + enum class State : u8 { + Uninitialized, ///< Not initialized + Idle, ///< Default state (i.e. not connected) + Joining, ///< The client is attempting to join a room. + Joined, ///< The client is connected to the room and is ready to send/receive packets. + Moderator, ///< The client is connnected to the room and is granted mod permissions. + }; + + enum class Error : u8 { + // Reasons why connection was closed + LostConnection, ///< Connection closed + HostKicked, ///< Kicked by the host + + // Reasons why connection was rejected + UnknownError, ///< Some error [permissions to network device missing or something] + NameCollision, ///< Somebody is already using this name + MacCollision, ///< Somebody is already using that mac-address + ConsoleIdCollision, ///< Somebody in the room has the same Console ID + WrongVersion, ///< The room version is not the same as for this RoomMember + WrongPassword, ///< The password doesn't match the one from the Room + CouldNotConnect, ///< The room is not responding to a connection attempt + RoomIsFull, ///< Room is already at the maximum number of players + HostBanned, ///< The user is banned by the host + + // Reasons why moderation request failed + PermissionDenied, ///< The user does not have mod permissions + NoSuchUser, ///< The nickname the user attempts to kick/ban does not exist + }; + + struct MemberInformation { + std::string nickname; ///< Nickname of the member. + std::string username; ///< The web services username of the member. Can be empty. + std::string display_name; ///< The web services display name of the member. Can be empty. + std::string avatar_url; ///< Url to the member's avatar. Can be empty. + GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're + /// not playing anything. + MacAddress mac_address; ///< MAC address associated with this member. + }; + using MemberList = std::vector; + + // The handle for the callback functions + template + using CallbackHandle = std::shared_ptr>; + + /** + * Unbinds a callback function from the events. + * @param handle The connection handle to disconnect + */ + template + void Unbind(CallbackHandle handle); + + RoomMember(); + ~RoomMember(); + + /** + * Returns the status of our connection to the room. + */ + State GetState() const; + + /** + * Returns information about the members in the room we're currently connected to. + */ + const MemberList& GetMemberInformation() const; + + /** + * Returns the nickname of the RoomMember. + */ + const std::string& GetNickname() const; + + /** + * Returns the username of the RoomMember. + */ + const std::string& GetUsername() const; + + /** + * Returns the MAC address of the RoomMember. + */ + const MacAddress& GetMacAddress() const; + + /** + * Returns information about the room we're currently connected to. + */ + RoomInformation GetRoomInformation() const; + + /** + * Returns whether we're connected to a server or not. + */ + bool IsConnected() const; + + /** + * Attempts to join a room at the specified address and port, using the specified nickname. + * A console ID hash is passed in to check console ID conflicts. + * This may fail if the username or console ID is already taken. + */ + void Join(const std::string& nickname, const std::string& console_id_hash, + const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, + u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, + const std::string& password = "", const std::string& token = ""); + + /** + * Sends a WiFi packet to the room. + * @param packet The WiFi packet to send. + */ + void SendWifiPacket(const WifiPacket& packet); + + /** + * Sends a chat message to the room. + * @param message The contents of the message. + */ + void SendChatMessage(const std::string& message); + + /** + * Sends the current game info to the room. + * @param game_info The game information. + */ + void SendGameInfo(const GameInfo& game_info); + + /** + * Sends a moderation request to the room. + * @param type Moderation request type. + * @param nickname The subject of the request. (i.e. the user you want to kick/ban) + */ + void SendModerationRequest(RoomMessageTypes type, const std::string& nickname); + + /** + * Attempts to retrieve ban list from the room. + * If success, the ban list callback would be called. Otherwise an error would be emitted. + */ + void RequestBanList(); + + /** + * Binds a function to an event that will be triggered every time the State of the member + * changed. The function wil be called every time the event is triggered. The callback function + * must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnStateChanged(std::function callback); + + /** + * Binds a function to an event that will be triggered every time an error happened. The + * function wil be called every time the event is triggered. The callback function must not bind + * or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnError(std::function callback); + + /** + * Binds a function to an event that will be triggered every time a WifiPacket is received. + * The function wil be called everytime the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnWifiPacketReceived( + std::function callback); + + /** + * Binds a function to an event that will be triggered every time the RoomInformation changes. + * The function wil be called every time the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnRoomInformationChanged( + std::function callback); + + /** + * Binds a function to an event that will be triggered every time a ChatMessage is received. + * The function wil be called every time the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnChatMessageRecieved( + std::function callback); + + /** + * Binds a function to an event that will be triggered every time a StatusMessage is + * received. The function will be called every time the event is triggered. The callback + * function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnStatusMessageReceived( + std::function callback); + + /** + * Binds a function to an event that will be triggered every time a requested ban list + * received. The function will be called every time the event is triggered. The callback + * function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnBanListReceived( + std::function callback); + + /** + * Leaves the current room. + */ + void Leave(); + +private: + class RoomMemberImpl; + std::unique_ptr room_member_impl; +}; + +inline const char* GetStateStr(const RoomMember::State& s) { + switch (s) { + case RoomMember::State::Uninitialized: + return "Uninitialized"; + case RoomMember::State::Idle: + return "Idle"; + case RoomMember::State::Joining: + return "Joining"; + case RoomMember::State::Joined: + return "Joined"; + case RoomMember::State::Moderator: + return "Moderator"; + } + return "Unknown"; +} + +inline const char* GetErrorStr(const RoomMember::Error& e) { + switch (e) { + case RoomMember::Error::LostConnection: + return "LostConnection"; + case RoomMember::Error::HostKicked: + return "HostKicked"; + case RoomMember::Error::UnknownError: + return "UnknownError"; + case RoomMember::Error::NameCollision: + return "NameCollision"; + case RoomMember::Error::MacCollision: + return "MaxCollision"; + case RoomMember::Error::ConsoleIdCollision: + return "ConsoleIdCollision"; + case RoomMember::Error::WrongVersion: + return "WrongVersion"; + case RoomMember::Error::WrongPassword: + return "WrongPassword"; + case RoomMember::Error::CouldNotConnect: + return "CouldNotConnect"; + case RoomMember::Error::RoomIsFull: + return "RoomIsFull"; + case RoomMember::Error::HostBanned: + return "HostBanned"; + case RoomMember::Error::PermissionDenied: + return "PermissionDenied"; + case RoomMember::Error::NoSuchUser: + return "NoSuchUser"; + default: + return "Unknown"; + } +} + +} // namespace Network diff --git a/src/network/verify_user.cpp b/src/network/verify_user.cpp new file mode 100755 index 000000000..f84cfe59b --- /dev/null +++ b/src/network/verify_user.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "network/verify_user.h" + +namespace Network::VerifyUser { + +Backend::~Backend() = default; + +NullBackend::~NullBackend() = default; + +UserData NullBackend::LoadUserData([[maybe_unused]] const std::string& verify_uid, + [[maybe_unused]] const std::string& token) { + return {}; +} + +} // namespace Network::VerifyUser diff --git a/src/network/verify_user.h b/src/network/verify_user.h new file mode 100755 index 000000000..6fc64d8a3 --- /dev/null +++ b/src/network/verify_user.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/logging/log.h" + +namespace Network::VerifyUser { + +struct UserData { + std::string username; + std::string display_name; + std::string avatar_url; + bool moderator = false; ///< Whether the user is a yuzu Moderator. +}; + +/** + * A backend used for verifying users and loading user data. + */ +class Backend { +public: + virtual ~Backend(); + + /** + * Verifies the given token and loads the information into a UserData struct. + * @param verify_uid A GUID that may be used for verification. + * @param token A token that contains user data and verification data. The format and content is + * decided by backends. + */ + virtual UserData LoadUserData(const std::string& verify_uid, const std::string& token) = 0; +}; + +/** + * A null backend where the token is ignored. + * No verification is performed here and the function returns an empty UserData. + */ +class NullBackend final : public Backend { +public: + ~NullBackend(); + + UserData LoadUserData(const std::string& verify_uid, const std::string& token) override; +}; + +} // namespace Network::VerifyUser diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index a69ccb264..fbbcf673a 100755 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -7,7 +7,7 @@ add_executable(tests common/ring_buffer.cpp common/unique_function.cpp core/core_timing.cpp - core/network/network.cpp + core/internal_network/network.cpp tests.cpp video_core/buffer_base.cpp input_common/calibration_configuration_job.cpp diff --git a/src/tests/core/internal_network/network.cpp b/src/tests/core/internal_network/network.cpp new file mode 100755 index 000000000..164b0ff24 --- /dev/null +++ b/src/tests/core/internal_network/network.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" + +TEST_CASE("Network::Errors", "[core]") { + Network::NetworkInstance network_instance; // initialize network + + Network::Socket socks[2]; + for (Network::Socket& sock : socks) { + REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM, + Network::Protocol::TCP) == Network::Errno::SUCCESS); + } + + Network::SockAddrIn addr{ + Network::Domain::INET, + {127, 0, 0, 1}, + 1, // hopefully nobody running this test has something listening on port 1 + }; + REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED); + + std::vector message{1, 2, 3, 4}; + REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN); +} diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index ae85a72ea..753fb6e7a 100755 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -1,12 +1,16 @@ add_library(web_service STATIC + announce_room_json.cpp + announce_room_json.h telemetry_json.cpp telemetry_json.h verify_login.cpp verify_login.h + verify_user_jwt.cpp + verify_user_jwt.h web_backend.cpp web_backend.h web_result.h ) create_target_directory_groups(web_service) -target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib) +target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib cpp-jwt) diff --git a/src/web_service/announce_room_json.cpp b/src/web_service/announce_room_json.cpp new file mode 100755 index 000000000..4c3195efd --- /dev/null +++ b/src/web_service/announce_room_json.cpp @@ -0,0 +1,145 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "common/detached_tasks.h" +#include "common/logging/log.h" +#include "web_service/announce_room_json.h" +#include "web_service/web_backend.h" + +namespace AnnounceMultiplayerRoom { + +static void to_json(nlohmann::json& json, const Member& member) { + if (!member.username.empty()) { + json["username"] = member.username; + } + json["nickname"] = member.nickname; + if (!member.avatar_url.empty()) { + json["avatarUrl"] = member.avatar_url; + } + json["gameName"] = member.game.name; + json["gameId"] = member.game.id; +} + +static void from_json(const nlohmann::json& json, Member& member) { + member.nickname = json.at("nickname").get(); + member.game.name = json.at("gameName").get(); + member.game.id = json.at("gameId").get(); + try { + member.username = json.at("username").get(); + member.avatar_url = json.at("avatarUrl").get(); + } catch (const nlohmann::detail::out_of_range&) { + member.username = member.avatar_url = ""; + LOG_DEBUG(Network, "Member \'{}\' isn't authenticated", member.nickname); + } +} + +static void to_json(nlohmann::json& json, const Room& room) { + json["port"] = room.information.port; + json["name"] = room.information.name; + if (!room.information.description.empty()) { + json["description"] = room.information.description; + } + json["preferredGameName"] = room.information.preferred_game.name; + json["preferredGameId"] = room.information.preferred_game.id; + json["maxPlayers"] = room.information.member_slots; + json["netVersion"] = room.net_version; + json["hasPassword"] = room.has_password; + if (room.members.size() > 0) { + nlohmann::json member_json = room.members; + json["players"] = member_json; + } +} + +static void from_json(const nlohmann::json& json, Room& room) { + room.verify_uid = json.at("externalGuid").get(); + room.ip = json.at("address").get(); + room.information.name = json.at("name").get(); + try { + room.information.description = json.at("description").get(); + } catch (const nlohmann::detail::out_of_range&) { + room.information.description = ""; + LOG_DEBUG(Network, "Room \'{}\' doesn't contain a description", room.information.name); + } + room.information.host_username = json.at("owner").get(); + room.information.port = json.at("port").get(); + room.information.preferred_game.name = json.at("preferredGameName").get(); + room.information.preferred_game.id = json.at("preferredGameId").get(); + room.information.member_slots = json.at("maxPlayers").get(); + room.net_version = json.at("netVersion").get(); + room.has_password = json.at("hasPassword").get(); + try { + room.members = json.at("players").get>(); + } catch (const nlohmann::detail::out_of_range& e) { + LOG_DEBUG(Network, "Out of range {}", e.what()); + } +} + +} // namespace AnnounceMultiplayerRoom + +namespace WebService { + +void RoomJson::SetRoomInformation(const std::string& name, const std::string& description, + const u16 port, const u32 max_player, const u32 net_version, + const bool has_password, + const AnnounceMultiplayerRoom::GameInfo& preferred_game) { + room.information.name = name; + room.information.description = description; + room.information.port = port; + room.information.member_slots = max_player; + room.net_version = net_version; + room.has_password = has_password; + room.information.preferred_game = preferred_game; +} +void RoomJson::AddPlayer(const AnnounceMultiplayerRoom::Member& member) { + room.members.push_back(member); +} + +WebService::WebResult RoomJson::Update() { + if (room_id.empty()) { + LOG_ERROR(WebService, "Room must be registered to be updated"); + return WebService::WebResult{WebService::WebResult::Code::LibError, + "Room is not registered", ""}; + } + nlohmann::json json{{"players", room.members}}; + return client.PostJson(fmt::format("/lobby/{}", room_id), json.dump(), false); +} + +WebService::WebResult RoomJson::Register() { + nlohmann::json json = room; + auto result = client.PostJson("/lobby", json.dump(), false); + if (result.result_code != WebService::WebResult::Code::Success) { + return result; + } + auto reply_json = nlohmann::json::parse(result.returned_data); + room = reply_json.get(); + room_id = reply_json.at("id").get(); + return WebService::WebResult{WebService::WebResult::Code::Success, "", room.verify_uid}; +} + +void RoomJson::ClearPlayers() { + room.members.clear(); +} + +AnnounceMultiplayerRoom::RoomList RoomJson::GetRoomList() { + auto reply = client.GetJson("/lobby", true).returned_data; + if (reply.empty()) { + return {}; + } + return nlohmann::json::parse(reply).at("rooms").get(); +} + +void RoomJson::Delete() { + if (room_id.empty()) { + LOG_ERROR(WebService, "Room must be registered to be deleted"); + return; + } + Common::DetachedTasks::AddTask( + [host{this->host}, username{this->username}, token{this->token}, room_id{this->room_id}]() { + // create a new client here because the this->client might be destroyed. + Client{host, username, token}.DeleteJson(fmt::format("/lobby/{}", room_id), "", false); + }); +} + +} // namespace WebService diff --git a/src/web_service/announce_room_json.h b/src/web_service/announce_room_json.h new file mode 100755 index 000000000..32c08858d --- /dev/null +++ b/src/web_service/announce_room_json.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/announce_multiplayer_room.h" +#include "web_service/web_backend.h" + +namespace WebService { + +/** + * Implementation of AnnounceMultiplayerRoom::Backend that (de)serializes room information into/from + * JSON, and submits/gets it to/from the yuzu web service + */ +class RoomJson : public AnnounceMultiplayerRoom::Backend { +public: + RoomJson(const std::string& host_, const std::string& username_, const std::string& token_) + : client(host_, username_, token_), host(host_), username(username_), token(token_) {} + ~RoomJson() = default; + void SetRoomInformation(const std::string& name, const std::string& description, const u16 port, + const u32 max_player, const u32 net_version, const bool has_password, + const AnnounceMultiplayerRoom::GameInfo& preferred_game) override; + void AddPlayer(const AnnounceMultiplayerRoom::Member& member) override; + WebResult Update() override; + WebResult Register() override; + void ClearPlayers() override; + AnnounceMultiplayerRoom::RoomList GetRoomList() override; + void Delete() override; + +private: + AnnounceMultiplayerRoom::Room room; + Client client; + std::string host; + std::string username; + std::string token; + std::string room_id; +}; + +} // namespace WebService diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp new file mode 100755 index 000000000..3bff46f0a --- /dev/null +++ b/src/web_service/verify_user_jwt.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif +#include +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#include +#include "common/logging/log.h" +#include "web_service/verify_user_jwt.h" +#include "web_service/web_backend.h" +#include "web_service/web_result.h" + +namespace WebService { + +static std::string public_key; +std::string GetPublicKey(const std::string& host) { + if (public_key.empty()) { + Client client(host, "", ""); // no need for credentials here + public_key = client.GetPlain("/jwt/external/key.pem", true).returned_data; + if (public_key.empty()) { + LOG_ERROR(WebService, "Could not fetch external JWT public key, verification may fail"); + } else { + LOG_INFO(WebService, "Fetched external JWT public key (size={})", public_key.size()); + } + } + return public_key; +} + +VerifyUserJWT::VerifyUserJWT(const std::string& host) : pub_key(GetPublicKey(host)) {} + +Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& verify_uid, + const std::string& token) { + const std::string audience = fmt::format("external-{}", verify_uid); + using namespace jwt::params; + std::error_code error; + auto decoded = + jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("yuzu-core"), + aud(audience), validate_iat(true), validate_jti(true)); + if (error) { + LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", + error.category().name(), error.value(), error.message()); + return {}; + } + Network::VerifyUser::UserData user_data{}; + if (decoded.payload().has_claim("username")) { + user_data.username = decoded.payload().get_claim_value("username"); + } + if (decoded.payload().has_claim("displayName")) { + user_data.display_name = decoded.payload().get_claim_value("displayName"); + } + if (decoded.payload().has_claim("avatarUrl")) { + user_data.avatar_url = decoded.payload().get_claim_value("avatarUrl"); + } + if (decoded.payload().has_claim("roles")) { + auto roles = decoded.payload().get_claim_value>("roles"); + user_data.moderator = std::find(roles.begin(), roles.end(), "moderator") != roles.end(); + } + return user_data; +} + +} // namespace WebService diff --git a/src/web_service/verify_user_jwt.h b/src/web_service/verify_user_jwt.h new file mode 100755 index 000000000..27b0a100c --- /dev/null +++ b/src/web_service/verify_user_jwt.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "network/verify_user.h" +#include "web_service/web_backend.h" + +namespace WebService { + +std::string GetPublicKey(const std::string& host); + +class VerifyUserJWT final : public Network::VerifyUser::Backend { +public: + VerifyUserJWT(const std::string& host); + ~VerifyUserJWT() = default; + + Network::VerifyUser::UserData LoadUserData(const std::string& verify_uid, + const std::string& token) override; + +private: + std::string pub_key; +}; + +} // namespace WebService diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 57e0e7025..66873143e 100755 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -156,10 +156,36 @@ add_executable(yuzu main.cpp main.h main.ui + multiplayer/chat_room.cpp + multiplayer/chat_room.h + multiplayer/chat_room.ui + multiplayer/client_room.h + multiplayer/client_room.cpp + multiplayer/client_room.ui + multiplayer/direct_connect.cpp + multiplayer/direct_connect.h + multiplayer/direct_connect.ui + multiplayer/host_room.cpp + multiplayer/host_room.h + multiplayer/host_room.ui + multiplayer/lobby.cpp + multiplayer/lobby.h + multiplayer/lobby.ui + multiplayer/lobby_p.h + multiplayer/message.cpp + multiplayer/message.h + multiplayer/moderation_dialog.cpp + multiplayer/moderation_dialog.h + multiplayer/moderation_dialog.ui + multiplayer/state.cpp + multiplayer/state.h + multiplayer/validation.h startup_checks.cpp startup_checks.h uisettings.cpp uisettings.h + util/clickable_label.cpp + util/clickable_label.h util/controller_navigation.cpp util/controller_navigation.h util/limitable_input_dialog.cpp @@ -256,7 +282,7 @@ endif() create_target_directory_groups(yuzu) -target_link_libraries(yuzu PRIVATE common core input_common video_core) +target_link_libraries(yuzu PRIVATE common core input_common network video_core) target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) @@ -300,6 +326,10 @@ if (USE_DISCORD_PRESENCE) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) endif() +if (ENABLE_WEB_SERVICE) + target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE) +endif() + if (YUZU_USE_QT_WEB_ENGINE) target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets) target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index c841843f0..3b22102a8 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" +#include "network/network.h" #include "yuzu/configuration/config.h" namespace FS = Common::FS; @@ -794,6 +795,7 @@ void Config::ReadUIValues() { ReadPathValues(); ReadScreenshotValues(); ReadShortcutValues(); + ReadMultiplayerValues(); ReadBasicSetting(UISettings::values.single_window_mode); ReadBasicSetting(UISettings::values.fullscreen); @@ -860,6 +862,42 @@ void Config::ReadWebServiceValues() { qt_config->endGroup(); } +void Config::ReadMultiplayerValues() { + qt_config->beginGroup(QStringLiteral("Multiplayer")); + + ReadBasicSetting(UISettings::values.multiplayer_nickname); + ReadBasicSetting(UISettings::values.multiplayer_ip); + ReadBasicSetting(UISettings::values.multiplayer_port); + ReadBasicSetting(UISettings::values.multiplayer_room_nickname); + ReadBasicSetting(UISettings::values.multiplayer_room_name); + ReadBasicSetting(UISettings::values.multiplayer_room_port); + ReadBasicSetting(UISettings::values.multiplayer_host_type); + ReadBasicSetting(UISettings::values.multiplayer_port); + ReadBasicSetting(UISettings::values.multiplayer_max_player); + ReadBasicSetting(UISettings::values.multiplayer_game_id); + ReadBasicSetting(UISettings::values.multiplayer_room_description); + + // Read ban list back + int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); + UISettings::values.multiplayer_ban_list.first.resize(size); + for (int i = 0; i < size; ++i) { + qt_config->setArrayIndex(i); + UISettings::values.multiplayer_ban_list.first[i] = + ReadSetting(QStringLiteral("username")).toString().toStdString(); + } + qt_config->endArray(); + size = qt_config->beginReadArray(QStringLiteral("ip_ban_list")); + UISettings::values.multiplayer_ban_list.second.resize(size); + for (int i = 0; i < size; ++i) { + qt_config->setArrayIndex(i); + UISettings::values.multiplayer_ban_list.second[i] = + ReadSetting(QStringLiteral("ip")).toString().toStdString(); + } + qt_config->endArray(); + + qt_config->endGroup(); +} + void Config::ReadValues() { if (global) { ReadControlValues(); @@ -876,6 +914,7 @@ void Config::ReadValues() { ReadRendererValues(); ReadAudioValues(); ReadSystemValues(); + ReadMultiplayerValues(); } void Config::SavePlayerValue(std::size_t player_index) { @@ -1025,6 +1064,7 @@ void Config::SaveValues() { SaveRendererValues(); SaveAudioValues(); SaveSystemValues(); + SaveMultiplayerValues(); } void Config::SaveAudioValues() { @@ -1347,6 +1387,7 @@ void Config::SaveUIValues() { SavePathValues(); SaveScreenshotValues(); SaveShortcutValues(); + SaveMultiplayerValues(); WriteBasicSetting(UISettings::values.single_window_mode); WriteBasicSetting(UISettings::values.fullscreen); @@ -1411,6 +1452,40 @@ void Config::SaveWebServiceValues() { qt_config->endGroup(); } +void Config::SaveMultiplayerValues() { + qt_config->beginGroup(QStringLiteral("Multiplayer")); + + WriteBasicSetting(UISettings::values.multiplayer_nickname); + WriteBasicSetting(UISettings::values.multiplayer_ip); + WriteBasicSetting(UISettings::values.multiplayer_port); + WriteBasicSetting(UISettings::values.multiplayer_room_nickname); + WriteBasicSetting(UISettings::values.multiplayer_room_name); + WriteBasicSetting(UISettings::values.multiplayer_room_port); + WriteBasicSetting(UISettings::values.multiplayer_host_type); + WriteBasicSetting(UISettings::values.multiplayer_port); + WriteBasicSetting(UISettings::values.multiplayer_max_player); + WriteBasicSetting(UISettings::values.multiplayer_game_id); + WriteBasicSetting(UISettings::values.multiplayer_room_description); + + // Write ban list + qt_config->beginWriteArray(QStringLiteral("username_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { + qt_config->setArrayIndex(static_cast(i)); + WriteSetting(QStringLiteral("username"), + QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i])); + } + qt_config->endArray(); + qt_config->beginWriteArray(QStringLiteral("ip_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { + qt_config->setArrayIndex(static_cast(i)); + WriteSetting(QStringLiteral("ip"), + QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i])); + } + qt_config->endArray(); + + qt_config->endGroup(); +} + QVariant Config::ReadSetting(const QString& name) const { return qt_config->value(name); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index a71eabe8e..937b2d95b 100755 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -89,6 +89,7 @@ private: void ReadUIGamelistValues(); void ReadUILayoutValues(); void ReadWebServiceValues(); + void ReadMultiplayerValues(); void SaveValues(); void SavePlayerValue(std::size_t player_index); @@ -118,6 +119,7 @@ private: void SaveUIGamelistValues(); void SaveUILayoutValues(); void SaveWebServiceValues(); + void SaveMultiplayerValues(); /** * Reads a setting from the qt_config. diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index e99657bd6..92ef4467b 100755 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -29,9 +29,10 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, InputCommon::InputSubsystem* input_subsystem, - Core::System& system_) - : QDialog(parent), ui{std::make_unique()}, registry{registry_}, - system{system_}, audio_tab{std::make_unique(system_, this)}, + Core::System& system_, bool enable_web_config) + : QDialog(parent), ui{std::make_unique()}, + registry(registry_), system{system_}, audio_tab{std::make_unique(system_, + this)}, cpu_tab{std::make_unique(system_, this)}, debug_tab_tab{std::make_unique(system_, this)}, filesystem_tab{std::make_unique(this)}, @@ -64,6 +65,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, ui->tabWidget->addTab(ui_tab.get(), tr("Game List")); ui->tabWidget->addTab(web_tab.get(), tr("Web")); + web_tab->SetWebServiceConfigEnabled(enable_web_config); hotkeys_tab->Populate(registry); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 12cf25daf..cec1610ad 100755 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -41,7 +41,8 @@ class ConfigureDialog : public QDialog { public: explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, - InputCommon::InputSubsystem* input_subsystem, Core::System& system_); + InputCommon::InputSubsystem* input_subsystem, Core::System& system_, + bool enable_web_config = true); ~ConfigureDialog() override; void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_network.cpp b/src/yuzu/configuration/configure_network.cpp index 8ed08fa6a..ba1986eb1 100755 --- a/src/yuzu/configuration/configure_network.cpp +++ b/src/yuzu/configuration/configure_network.cpp @@ -4,7 +4,7 @@ #include #include "common/settings.h" #include "core/core.h" -#include "core/network/network_interface.h" +#include "core/internal_network/network_interface.h" #include "ui_configure_network.h" #include "yuzu/configuration/configure_network.h" diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index d779251b4..ff4bf44f4 100755 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -169,3 +169,8 @@ void ConfigureWeb::OnLoginVerified() { "correctly, and that your internet connection is working.")); } } + +void ConfigureWeb::SetWebServiceConfigEnabled(bool enabled) { + ui->label_disable_info->setVisible(!enabled); + ui->groupBoxWebConfig->setEnabled(enabled); +} diff --git a/src/yuzu/configuration/configure_web.h b/src/yuzu/configuration/configure_web.h index 9054711ea..041b51149 100755 --- a/src/yuzu/configuration/configure_web.h +++ b/src/yuzu/configuration/configure_web.h @@ -20,6 +20,7 @@ public: ~ConfigureWeb() override; void ApplyConfiguration(); + void SetWebServiceConfigEnabled(bool enabled); private: void changeEvent(QEvent* event) override; diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui index 35b4274b0..3ac3864be 100755 --- a/src/yuzu/configuration/configure_web.ui +++ b/src/yuzu/configuration/configure_web.ui @@ -112,6 +112,16 @@ + + + + Web Service configuration can only be changed when a public room isn't being hosted. + + + true + + + diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 05d309827..5bcf582bf 100755 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -499,6 +499,8 @@ void GameList::DonePopulating(const QStringList& watch_list) { } item_model->sort(tree_view->header()->sortIndicatorSection(), tree_view->header()->sortIndicatorOrder()); + + emit PopulatingCompleted(); } void GameList::PopupContextMenu(const QPoint& menu_location) { @@ -752,6 +754,10 @@ void GameList::LoadCompatibilityList() { } } +QStandardItemModel* GameList::GetModel() const { + return item_model; +} + void GameList::PopulateAsync(QVector& game_dirs) { tree_view->setEnabled(false); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index bc36d015a..9605985cc 100755 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -16,9 +16,14 @@ #include #include "common/common_types.h" +#include "core/core.h" #include "uisettings.h" #include "yuzu/compatibility_list.h" +namespace Core { +class System; +} + class ControllerNavigation; class GameListWorker; class GameListSearchField; @@ -84,6 +89,8 @@ public: void SaveInterfaceLayout(); void LoadInterfaceLayout(); + QStandardItemModel* GetModel() const; + /// Disables events from the emulated controller void UnloadController(); @@ -108,6 +115,7 @@ signals: void OpenDirectory(const QString& directory); void AddDirectory(); void ShowList(bool show); + void PopulatingCompleted(); private slots: void OnItemExpanded(const QModelIndex& item); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2d704f422..28219c0a5 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -37,6 +37,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" +#include "yuzu/multiplayer/state.h" #include "yuzu/util/controller_navigation.h" // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows @@ -137,6 +138,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/main.h" #include "yuzu/startup_checks.h" #include "yuzu/uisettings.h" +#include "yuzu/util/clickable_label.h" using namespace Common::Literals; @@ -280,6 +282,8 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); discord_rpc->Update(); + system->GetRoomNetwork().Init(); + RegisterMetaTypes(); InitializeWidgets(); @@ -471,6 +475,8 @@ GMainWindow::~GMainWindow() { delete render_window; } + system->GetRoomNetwork().Shutdown(); + #ifdef __linux__ ::close(sig_interrupt_fds[0]); ::close(sig_interrupt_fds[1]); @@ -838,6 +844,10 @@ void GMainWindow::InitializeWidgets() { } }); + multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, + ui->action_Show_Room, system->GetRoomNetwork()); + multiplayer_state->setVisible(false); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -870,6 +880,10 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label); } + // TODO (flTobi): Add the widget when multiplayer is fully implemented + // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); + // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); + tas_label = new QLabel(); tas_label->setObjectName(QStringLiteral("TASlabel")); tas_label->setFocusPolicy(Qt::NoFocus); @@ -1179,6 +1193,8 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList); + connect(game_list, &GameList::PopulatingCompleted, + [this] { multiplayer_state->UpdateGameList(game_list->GetModel()); }); connect(game_list, &GameList::OpenPerGameGeneralRequested, this, &GMainWindow::OnGameListOpenPerGameProperties); @@ -1196,6 +1212,9 @@ void GMainWindow::ConnectWidgetEvents() { connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit); connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar); + + connect(this, &GMainWindow::UpdateThemedIcons, multiplayer_state, + &MultiplayerState::UpdateThemedIcons); } void GMainWindow::ConnectMenuEvents() { @@ -1239,6 +1258,18 @@ void GMainWindow::ConnectMenuEvents() { ui->action_Reset_Window_Size_900, ui->action_Reset_Window_Size_1080}); + // Multiplayer + connect(ui->action_View_Lobby, &QAction::triggered, multiplayer_state, + &MultiplayerState::OnViewLobby); + connect(ui->action_Start_Room, &QAction::triggered, multiplayer_state, + &MultiplayerState::OnCreateRoom); + connect(ui->action_Leave_Room, &QAction::triggered, multiplayer_state, + &MultiplayerState::OnCloseRoom); + connect(ui->action_Connect_To_Room, &QAction::triggered, multiplayer_state, + &MultiplayerState::OnDirectConnectToRoom); + connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state, + &MultiplayerState::OnOpenNetworkRoom); + // Tools connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); @@ -2882,7 +2913,8 @@ void GMainWindow::OnConfigure() { const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); Settings::SetConfiguringGlobal(true); - ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system); + ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system, + !multiplayer_state->IsHostingPublicRoom()); connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, &GMainWindow::OnLanguageChanged); @@ -2939,6 +2971,11 @@ void GMainWindow::OnConfigure() { if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } + + if (!multiplayer_state->IsHostingPublicRoom()) { + multiplayer_state->UpdateCredentials(); + } + emit UpdateThemedIcons(); const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); @@ -3760,6 +3797,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { } render_window->close(); + multiplayer_state->Close(); QWidget::closeEvent(event); } @@ -3956,6 +3994,7 @@ void GMainWindow::OnLanguageChanged(const QString& locale) { UISettings::values.language = locale; LoadTranslation(); ui->retranslateUi(this); + multiplayer_state->retranslateUi(); UpdateWindowTitle(); } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8fce1b913..33e58e31c 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -11,6 +11,7 @@ #include #include +#include "common/announce_multiplayer_room.h" #include "common/common_types.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -22,6 +23,7 @@ #endif class Config; +class ClickableLabel; class EmuThread; class GameList; class GImageInfo; @@ -31,6 +33,7 @@ class MicroProfileDialog; class ProfilerWidget; class ControllerDialog; class QLabel; +class MultiplayerState; class QPushButton; class QProgressDialog; class WaitTreeWidget; @@ -354,6 +357,8 @@ private: std::unique_ptr discord_rpc; std::shared_ptr input_subsystem; + MultiplayerState* multiplayer_state = nullptr; + GRenderWindow* render_window; GameList* game_list; LoadingScreen* loading_screen; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 6ab95b9a5..cdf31b417 100755 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -154,6 +154,7 @@ + @@ -245,6 +246,43 @@ Show Status Bar + + + true + + + Browse Public Game Lobby + + + + + true + + + Create Room + + + + + false + + + Leave Room + + + + + Direct Connect to Room + + + + + false + + + Show Current Room + + true diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp new file mode 100755 index 000000000..5837b36ab --- /dev/null +++ b/src/yuzu/multiplayer/chat_room.cpp @@ -0,0 +1,491 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "core/announce_multiplayer_session.h" +#include "ui_chat_room.h" +#include "yuzu/game_list_p.h" +#include "yuzu/multiplayer/chat_room.h" +#include "yuzu/multiplayer/message.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/web_backend.h" +#endif + +class ChatMessage { +public: + explicit ChatMessage(const Network::ChatEntry& chat, Network::RoomNetwork& room_network, + QTime ts = {}) { + /// Convert the time to their default locale defined format + QLocale locale; + timestamp = locale.toString(ts.isValid() ? ts : QTime::currentTime(), QLocale::ShortFormat); + nickname = QString::fromStdString(chat.nickname); + username = QString::fromStdString(chat.username); + message = QString::fromStdString(chat.message); + + // Check for user pings + QString cur_nickname, cur_username; + if (auto room = room_network.GetRoomMember().lock()) { + cur_nickname = QString::fromStdString(room->GetNickname()); + cur_username = QString::fromStdString(room->GetUsername()); + } + + // Handle pings at the beginning and end of message + QString fixed_message = QStringLiteral(" %1 ").arg(message); + if (fixed_message.contains(QStringLiteral(" @%1 ").arg(cur_nickname)) || + (!cur_username.isEmpty() && + fixed_message.contains(QStringLiteral(" @%1 ").arg(cur_username)))) { + + contains_ping = true; + } else { + contains_ping = false; + } + } + + bool ContainsPing() const { + return contains_ping; + } + + /// Format the message using the players color + QString GetPlayerChatMessage(u16 player) const { + auto color = player_color[player % 16]; + QString name; + if (username.isEmpty() || username == nickname) { + name = nickname; + } else { + name = QStringLiteral("%1 (%2)").arg(nickname, username); + } + + QString style, text_color; + if (ContainsPing()) { + // Add a background color to these messages + style = QStringLiteral("background-color: %1").arg(QString::fromStdString(ping_color)); + // Add a font color + text_color = QStringLiteral("color='#000000'"); + } + + return QStringLiteral("[%1] <%3> %6") + .arg(timestamp, QString::fromStdString(color), name.toHtmlEscaped(), style, text_color, + message.toHtmlEscaped()); + } + +private: + static constexpr std::array player_color = { + {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222", + "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "FFFF00"}}; + static constexpr char ping_color[] = "#FFFF00"; + + QString timestamp; + QString nickname; + QString username; + QString message; + bool contains_ping; +}; + +class StatusMessage { +public: + explicit StatusMessage(const QString& msg, QTime ts = {}) { + /// Convert the time to their default locale defined format + QLocale locale; + timestamp = locale.toString(ts.isValid() ? ts : QTime::currentTime(), QLocale::ShortFormat); + message = msg; + } + + QString GetSystemChatMessage() const { + return QStringLiteral("[%1] * %3") + .arg(timestamp, QString::fromStdString(system_color), message); + } + +private: + static constexpr const char system_color[] = "#FF8C00"; + QString timestamp; + QString message; +}; + +class PlayerListItem : public QStandardItem { +public: + static const int NicknameRole = Qt::UserRole + 1; + static const int UsernameRole = Qt::UserRole + 2; + static const int AvatarUrlRole = Qt::UserRole + 3; + static const int GameNameRole = Qt::UserRole + 4; + + PlayerListItem() = default; + explicit PlayerListItem(const std::string& nickname, const std::string& username, + const std::string& avatar_url, const std::string& game_name) { + setEditable(false); + setData(QString::fromStdString(nickname), NicknameRole); + setData(QString::fromStdString(username), UsernameRole); + setData(QString::fromStdString(avatar_url), AvatarUrlRole); + if (game_name.empty()) { + setData(QObject::tr("Not playing a game"), GameNameRole); + } else { + setData(QString::fromStdString(game_name), GameNameRole); + } + } + + QVariant data(int role) const override { + if (role != Qt::DisplayRole) { + return QStandardItem::data(role); + } + QString name; + const QString nickname = data(NicknameRole).toString(); + const QString username = data(UsernameRole).toString(); + if (username.isEmpty() || username == nickname) { + name = nickname; + } else { + name = QStringLiteral("%1 (%2)").arg(nickname, username); + } + return QStringLiteral("%1\n %2").arg(name, data(GameNameRole).toString()); + } +}; + +ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { + ui->setupUi(this); + + // set the item_model for player_view + + player_list = new QStandardItemModel(ui->player_view); + ui->player_view->setModel(player_list); + ui->player_view->setContextMenuPolicy(Qt::CustomContextMenu); + // set a header to make it look better though there is only one column + player_list->insertColumns(0, 1); + player_list->setHeaderData(0, Qt::Horizontal, tr("Members")); + + ui->chat_history->document()->setMaximumBlockCount(max_chat_lines); + + // register the network structs to use in slots and signals + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + + // Connect all the widgets to the appropriate events + connect(ui->player_view, &QTreeView::customContextMenuRequested, this, + &ChatRoom::PopupContextMenu); + connect(ui->chat_message, &QLineEdit::returnPressed, this, &ChatRoom::OnSendChat); + connect(ui->chat_message, &QLineEdit::textChanged, this, &ChatRoom::OnChatTextChanged); + connect(ui->send_message, &QPushButton::clicked, this, &ChatRoom::OnSendChat); +} + +ChatRoom::~ChatRoom() = default; + +void ChatRoom::Initialize(Network::RoomNetwork* room_network_) { + room_network = room_network_; + // setup the callbacks for network updates + if (auto member = room_network->GetRoomMember().lock()) { + member->BindOnChatMessageRecieved( + [this](const Network::ChatEntry& chat) { emit ChatReceived(chat); }); + member->BindOnStatusMessageReceived( + [this](const Network::StatusMessageEntry& status_message) { + emit StatusMessageReceived(status_message); + }); + connect(this, &ChatRoom::ChatReceived, this, &ChatRoom::OnChatReceive); + connect(this, &ChatRoom::StatusMessageReceived, this, &ChatRoom::OnStatusMessageReceive); + } +} + +void ChatRoom::SetModPerms(bool is_mod) { + has_mod_perms = is_mod; +} + +void ChatRoom::RetranslateUi() { + ui->retranslateUi(this); +} + +void ChatRoom::Clear() { + ui->chat_history->clear(); + block_list.clear(); +} + +void ChatRoom::AppendStatusMessage(const QString& msg) { + ui->chat_history->append(StatusMessage(msg).GetSystemChatMessage()); +} + +void ChatRoom::AppendChatMessage(const QString& msg) { + ui->chat_history->append(msg); +} + +void ChatRoom::SendModerationRequest(Network::RoomMessageTypes type, const std::string& nickname) { + if (auto room = room_network->GetRoomMember().lock()) { + auto members = room->GetMemberInformation(); + auto it = std::find_if(members.begin(), members.end(), + [&nickname](const Network::RoomMember::MemberInformation& member) { + return member.nickname == nickname; + }); + if (it == members.end()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::NO_SUCH_USER); + return; + } + room->SendModerationRequest(type, nickname); + } +} + +bool ChatRoom::ValidateMessage(const std::string& msg) { + return !msg.empty(); +} + +void ChatRoom::OnRoomUpdate(const Network::RoomInformation& info) { + // TODO(B3N30): change title + if (auto room_member = room_network->GetRoomMember().lock()) { + SetPlayerList(room_member->GetMemberInformation()); + } +} + +void ChatRoom::Disable() { + ui->send_message->setDisabled(true); + ui->chat_message->setDisabled(true); +} + +void ChatRoom::Enable() { + ui->send_message->setEnabled(true); + ui->chat_message->setEnabled(true); +} + +void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) { + if (!ValidateMessage(chat.message)) { + return; + } + if (auto room = room_network->GetRoomMember().lock()) { + // get the id of the player + auto members = room->GetMemberInformation(); + auto it = std::find_if(members.begin(), members.end(), + [&chat](const Network::RoomMember::MemberInformation& member) { + return member.nickname == chat.nickname && + member.username == chat.username; + }); + if (it == members.end()) { + LOG_INFO(Network, "Chat message received from unknown player. Ignoring it."); + return; + } + if (block_list.count(chat.nickname)) { + LOG_INFO(Network, "Chat message received from blocked player {}. Ignoring it.", + chat.nickname); + return; + } + auto player = std::distance(members.begin(), it); + ChatMessage m(chat, *room_network); + if (m.ContainsPing()) { + emit UserPinged(); + } + AppendChatMessage(m.GetPlayerChatMessage(player)); + } +} + +void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_message) { + QString name; + if (status_message.username.empty() || status_message.username == status_message.nickname) { + name = QString::fromStdString(status_message.nickname); + } else { + name = QStringLiteral("%1 (%2)").arg(QString::fromStdString(status_message.nickname), + QString::fromStdString(status_message.username)); + } + QString message; + switch (status_message.type) { + case Network::IdMemberJoin: + message = tr("%1 has joined").arg(name); + break; + case Network::IdMemberLeave: + message = tr("%1 has left").arg(name); + break; + case Network::IdMemberKicked: + message = tr("%1 has been kicked").arg(name); + break; + case Network::IdMemberBanned: + message = tr("%1 has been banned").arg(name); + break; + case Network::IdAddressUnbanned: + message = tr("%1 has been unbanned").arg(name); + break; + } + if (!message.isEmpty()) + AppendStatusMessage(message); +} + +void ChatRoom::OnSendChat() { + if (auto room = room_network->GetRoomMember().lock()) { + if (room->GetState() != Network::RoomMember::State::Joined && + room->GetState() != Network::RoomMember::State::Moderator) { + + return; + } + auto message = ui->chat_message->text().toStdString(); + if (!ValidateMessage(message)) { + return; + } + auto nick = room->GetNickname(); + auto username = room->GetUsername(); + Network::ChatEntry chat{nick, username, message}; + + auto members = room->GetMemberInformation(); + auto it = std::find_if(members.begin(), members.end(), + [&chat](const Network::RoomMember::MemberInformation& member) { + return member.nickname == chat.nickname && + member.username == chat.username; + }); + if (it == members.end()) { + LOG_INFO(Network, "Cannot find self in the player list when sending a message."); + } + auto player = std::distance(members.begin(), it); + ChatMessage m(chat, *room_network); + room->SendChatMessage(message); + AppendChatMessage(m.GetPlayerChatMessage(player)); + ui->chat_message->clear(); + } +} + +void ChatRoom::UpdateIconDisplay() { + for (int row = 0; row < player_list->invisibleRootItem()->rowCount(); ++row) { + QStandardItem* item = player_list->invisibleRootItem()->child(row); + const std::string avatar_url = + item->data(PlayerListItem::AvatarUrlRole).toString().toStdString(); + if (icon_cache.count(avatar_url)) { + item->setData(icon_cache.at(avatar_url), Qt::DecorationRole); + } else { + item->setData(QIcon::fromTheme(QStringLiteral("no_avatar")).pixmap(48), + Qt::DecorationRole); + } + } +} + +void ChatRoom::SetPlayerList(const Network::RoomMember::MemberList& member_list) { + // TODO(B3N30): Remember which row is selected + player_list->removeRows(0, player_list->rowCount()); + for (const auto& member : member_list) { + if (member.nickname.empty()) + continue; + QStandardItem* name_item = new PlayerListItem(member.nickname, member.username, + member.avatar_url, member.game_info.name); + +#ifdef ENABLE_WEB_SERVICE + if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) { + // Start a request to get the member's avatar + const QUrl url(QString::fromStdString(member.avatar_url)); + QFuture future = QtConcurrent::run([url] { + WebService::Client client( + QStringLiteral("%1://%2").arg(url.scheme(), url.host()).toStdString(), "", ""); + auto result = client.GetImage(url.path().toStdString(), true); + if (result.returned_data.empty()) { + LOG_ERROR(WebService, "Failed to get avatar"); + } + return result.returned_data; + }); + auto* future_watcher = new QFutureWatcher(this); + connect(future_watcher, &QFutureWatcher::finished, this, + [this, future_watcher, avatar_url = member.avatar_url] { + const std::string result = future_watcher->result(); + if (result.empty()) + return; + QPixmap pixmap; + if (!pixmap.loadFromData(reinterpret_cast(result.data()), + static_cast(result.size()))) + return; + icon_cache[avatar_url] = + pixmap.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + // Update all the displayed icons with the new icon_cache + UpdateIconDisplay(); + }); + future_watcher->setFuture(future); + } +#endif + + player_list->invisibleRootItem()->appendRow(name_item); + } + UpdateIconDisplay(); + // TODO(B3N30): Restore row selection +} + +void ChatRoom::OnChatTextChanged() { + if (ui->chat_message->text().length() > static_cast(Network::MaxMessageSize)) + ui->chat_message->setText( + ui->chat_message->text().left(static_cast(Network::MaxMessageSize))); +} + +void ChatRoom::PopupContextMenu(const QPoint& menu_location) { + QModelIndex item = ui->player_view->indexAt(menu_location); + if (!item.isValid()) + return; + + std::string nickname = + player_list->item(item.row())->data(PlayerListItem::NicknameRole).toString().toStdString(); + + QMenu context_menu; + + QString username = player_list->item(item.row())->data(PlayerListItem::UsernameRole).toString(); + if (!username.isEmpty()) { + QAction* view_profile_action = context_menu.addAction(tr("View Profile")); + connect(view_profile_action, &QAction::triggered, [username] { + QDesktopServices::openUrl( + QUrl(QStringLiteral("https://community.citra-emu.org/u/%1").arg(username))); + }); + } + + std::string cur_nickname; + if (auto room = room_network->GetRoomMember().lock()) { + cur_nickname = room->GetNickname(); + } + + if (nickname != cur_nickname) { // You can't block yourself + QAction* block_action = context_menu.addAction(tr("Block Player")); + + block_action->setCheckable(true); + block_action->setChecked(block_list.count(nickname) > 0); + + connect(block_action, &QAction::triggered, [this, nickname] { + if (block_list.count(nickname)) { + block_list.erase(nickname); + } else { + QMessageBox::StandardButton result = QMessageBox::question( + this, tr("Block Player"), + tr("When you block a player, you will no longer receive chat messages from " + "them.

Are you sure you would like to block %1?") + .arg(QString::fromStdString(nickname)), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) + block_list.emplace(nickname); + } + }); + } + + if (has_mod_perms && nickname != cur_nickname) { // You can't kick or ban yourself + context_menu.addSeparator(); + + QAction* kick_action = context_menu.addAction(tr("Kick")); + QAction* ban_action = context_menu.addAction(tr("Ban")); + + connect(kick_action, &QAction::triggered, [this, nickname] { + QMessageBox::StandardButton result = + QMessageBox::question(this, tr("Kick Player"), + tr("Are you sure you would like to kick %1?") + .arg(QString::fromStdString(nickname)), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) + SendModerationRequest(Network::IdModKick, nickname); + }); + connect(ban_action, &QAction::triggered, [this, nickname] { + QMessageBox::StandardButton result = QMessageBox::question( + this, tr("Ban Player"), + tr("Are you sure you would like to kick and ban %1?\n\nThis would " + "ban both their forum username and their IP address.") + .arg(QString::fromStdString(nickname)), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) + SendModerationRequest(Network::IdModBan, nickname); + }); + } + + context_menu.exec(ui->player_view->viewport()->mapToGlobal(menu_location)); +} diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h new file mode 100755 index 000000000..01c70fad0 --- /dev/null +++ b/src/yuzu/multiplayer/chat_room.h @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "network/network.h" + +namespace Ui { +class ChatRoom; +} + +namespace Core { +class AnnounceMultiplayerSession; +} + +class ConnectionError; +class ComboBoxProxyModel; + +class ChatMessage; + +class ChatRoom : public QWidget { + Q_OBJECT + +public: + explicit ChatRoom(QWidget* parent); + void Initialize(Network::RoomNetwork* room_network); + void RetranslateUi(); + void SetPlayerList(const Network::RoomMember::MemberList& member_list); + void Clear(); + void AppendStatusMessage(const QString& msg); + ~ChatRoom(); + + void SetModPerms(bool is_mod); + void UpdateIconDisplay(); + +public slots: + void OnRoomUpdate(const Network::RoomInformation& info); + void OnChatReceive(const Network::ChatEntry&); + void OnStatusMessageReceive(const Network::StatusMessageEntry&); + void OnSendChat(); + void OnChatTextChanged(); + void PopupContextMenu(const QPoint& menu_location); + void Disable(); + void Enable(); + +signals: + void ChatReceived(const Network::ChatEntry&); + void StatusMessageReceived(const Network::StatusMessageEntry&); + void UserPinged(); + +private: + static constexpr u32 max_chat_lines = 1000; + void AppendChatMessage(const QString&); + bool ValidateMessage(const std::string&); + void SendModerationRequest(Network::RoomMessageTypes type, const std::string& nickname); + + bool has_mod_perms = false; + QStandardItemModel* player_list; + std::unique_ptr ui; + std::unordered_set block_list; + std::unordered_map icon_cache; + Network::RoomNetwork* room_network; +}; + +Q_DECLARE_METATYPE(Network::ChatEntry); +Q_DECLARE_METATYPE(Network::StatusMessageEntry); +Q_DECLARE_METATYPE(Network::RoomInformation); +Q_DECLARE_METATYPE(Network::RoomMember::State); +Q_DECLARE_METATYPE(Network::RoomMember::Error); diff --git a/src/yuzu/multiplayer/chat_room.ui b/src/yuzu/multiplayer/chat_room.ui new file mode 100755 index 000000000..f2b31b5da --- /dev/null +++ b/src/yuzu/multiplayer/chat_room.ui @@ -0,0 +1,59 @@ + + + ChatRoom + + + + 0 + 0 + 807 + 432 + + + + Room Window + + + + + + + + + + + false + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Send Chat Message + + + + + + + Send Message + + + + + + + + + + + + diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp new file mode 100755 index 000000000..a9859ed70 --- /dev/null +++ b/src/yuzu/multiplayer/client_room.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "core/announce_multiplayer_session.h" +#include "ui_client_room.h" +#include "yuzu/game_list_p.h" +#include "yuzu/multiplayer/client_room.h" +#include "yuzu/multiplayer/message.h" +#include "yuzu/multiplayer/moderation_dialog.h" +#include "yuzu/multiplayer/state.h" + +ClientRoomWindow::ClientRoomWindow(QWidget* parent, Network::RoomNetwork& room_network_) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(std::make_unique()), room_network{room_network_} { + ui->setupUi(this); + ui->chat->Initialize(&room_network); + + // setup the callbacks for network updates + if (auto member = room_network.GetRoomMember().lock()) { + member->BindOnRoomInformationChanged( + [this](const Network::RoomInformation& info) { emit RoomInformationChanged(info); }); + member->BindOnStateChanged( + [this](const Network::RoomMember::State& state) { emit StateChanged(state); }); + + connect(this, &ClientRoomWindow::RoomInformationChanged, this, + &ClientRoomWindow::OnRoomUpdate); + connect(this, &ClientRoomWindow::StateChanged, this, &::ClientRoomWindow::OnStateChange); + // Update the state + OnStateChange(member->GetState()); + } else { + // TODO (jroweboy) network was not initialized? + } + + connect(ui->disconnect, &QPushButton::clicked, this, &ClientRoomWindow::Disconnect); + ui->disconnect->setDefault(false); + ui->disconnect->setAutoDefault(false); + connect(ui->moderation, &QPushButton::clicked, [this] { + ModerationDialog dialog(room_network, this); + dialog.exec(); + }); + ui->moderation->setDefault(false); + ui->moderation->setAutoDefault(false); + connect(ui->chat, &ChatRoom::UserPinged, this, &ClientRoomWindow::ShowNotification); + UpdateView(); +} + +ClientRoomWindow::~ClientRoomWindow() = default; + +void ClientRoomWindow::SetModPerms(bool is_mod) { + ui->chat->SetModPerms(is_mod); + ui->moderation->setVisible(is_mod); + ui->moderation->setDefault(false); + ui->moderation->setAutoDefault(false); +} + +void ClientRoomWindow::RetranslateUi() { + ui->retranslateUi(this); + ui->chat->RetranslateUi(); +} + +void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) { + UpdateView(); +} + +void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { + if (state == Network::RoomMember::State::Joined || + state == Network::RoomMember::State::Moderator) { + + ui->chat->Clear(); + ui->chat->AppendStatusMessage(tr("Connected")); + SetModPerms(state == Network::RoomMember::State::Moderator); + } + UpdateView(); +} + +void ClientRoomWindow::Disconnect() { + auto parent = static_cast(parentWidget()); + if (parent->OnCloseRoom()) { + ui->chat->AppendStatusMessage(tr("Disconnected")); + close(); + } +} + +void ClientRoomWindow::UpdateView() { + if (auto member = room_network.GetRoomMember().lock()) { + if (member->IsConnected()) { + ui->chat->Enable(); + ui->disconnect->setEnabled(true); + auto memberlist = member->GetMemberInformation(); + ui->chat->SetPlayerList(memberlist); + const auto information = member->GetRoomInformation(); + setWindowTitle(QString(tr("%1 (%2/%3 members) - connected")) + .arg(QString::fromStdString(information.name)) + .arg(memberlist.size()) + .arg(information.member_slots)); + ui->description->setText(QString::fromStdString(information.description)); + return; + } + } + // TODO(B3N30): can't get RoomMember*, show error and close window + close(); +} + +void ClientRoomWindow::UpdateIconDisplay() { + ui->chat->UpdateIconDisplay(); +} diff --git a/src/yuzu/multiplayer/client_room.h b/src/yuzu/multiplayer/client_room.h new file mode 100755 index 000000000..f338e3c59 --- /dev/null +++ b/src/yuzu/multiplayer/client_room.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "yuzu/multiplayer/chat_room.h" + +namespace Ui { +class ClientRoom; +} + +class ClientRoomWindow : public QDialog { + Q_OBJECT + +public: + explicit ClientRoomWindow(QWidget* parent, Network::RoomNetwork& room_network_); + ~ClientRoomWindow(); + + void RetranslateUi(); + void UpdateIconDisplay(); + +public slots: + void OnRoomUpdate(const Network::RoomInformation&); + void OnStateChange(const Network::RoomMember::State&); + +signals: + void RoomInformationChanged(const Network::RoomInformation&); + void StateChanged(const Network::RoomMember::State&); + void ShowNotification(); + +private: + void Disconnect(); + void UpdateView(); + void SetModPerms(bool is_mod); + + QStandardItemModel* player_list; + std::unique_ptr ui; + Network::RoomNetwork& room_network; +}; diff --git a/src/yuzu/multiplayer/client_room.ui b/src/yuzu/multiplayer/client_room.ui new file mode 100755 index 000000000..97e88b502 --- /dev/null +++ b/src/yuzu/multiplayer/client_room.ui @@ -0,0 +1,80 @@ + + + ClientRoom + + + + 0 + 0 + 807 + 432 + + + + Room Window + + + + + + + + 0 + + + + + Room Description + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Moderation... + + + false + + + + + + + Leave Room + + + + + + + + + + + + + + + ChatRoom + QWidget +

3V)KxI}1{KrA)AHPrK43=zGoP13Tju!w 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%0DV@Eg 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≷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