From 90b5b06128143971928fb8e327362eb7f8137f85 Mon Sep 17 00:00:00 2001 From: Todd Kordenbrock Date: Fri, 14 Jun 2019 13:33:29 -0500 Subject: [PATCH] REPO: Transfer internal updates for 1.1906.1 to github --- CMakeLists.txt | 42 +- INSTALL.md | 77 +- NEWS.md | 44 +- README.md | 5 +- cmake/FaodelTPLs.cmake | 29 +- cmake/faodel.pc.in | 2 +- docs/ConfigurationCookbook.md | 4 +- docs/Doxyfile | 6 +- examples/CMakeLists.txt | 6 +- examples/common/data_types/nodeid_example.cpp | 6 +- .../common/data_types/resourceurl_example.cpp | 13 +- .../dirman/dirman1-preload-configuration.cpp | 2 +- examples/dirman/dirman2-runtime-define.cpp | 8 +- examples/dirman/example1.cpp | 2 +- examples/dirman/example2.cpp | 12 +- examples/dirman/example3.cpp | 6 +- examples/dirman/example4.cpp | 6 +- examples/dirman/example5.cpp | 12 +- examples/faodel-cli/basic-startstop.sh | 216 ++ examples/kelpie/README.md | 6 +- examples/kelpie/nonet/CMakeLists.txt | 2 +- examples/kelpie/nonet/dim-sum/dim-sum.cpp | 2 +- .../local-pool-basics/local-pool-basics.cpp | 2 +- .../nonet/start-finish/start-finish.cpp | 2 +- .../kelpie/nonet/using-whookie/CMakeLists.txt | 4 +- .../nonet/using-whookie/using-whookie.cpp | 26 +- examples/kelpie/prod-con/Globals.cpp | 2 +- examples/kelpie/prod-con/Globals.hh | 2 +- examples/kelpie/prod-con/prod-con.cpp | 20 +- .../simple_ldo_send_example.cpp | 6 +- .../simple_user_ldo_put_example.cpp | 6 +- examples/nnti/cpp/PingPong.cpp | 6 +- examples/opbox/CMakeLists.txt | 6 +- .../advanced/dirty_use/lingering_data.cpp | 6 +- examples/opbox/advanced/job2job/README.md | 2 +- .../opbox/advanced/job2job/job2job-client.cpp | 2 +- .../opbox/advanced/job2job/job2job-server.cpp | 2 +- .../advanced/job2job/kahuna.ibverbs.conf | 2 +- .../opbox/advanced/job2job/kahuna.mpi.conf | 2 +- .../opbox/advanced/job2job/mutrino.mpi.conf | 2 +- .../opbox/advanced/job2job/mutrino.ugni.conf | 2 +- .../message_packing_example.cpp | 6 +- .../basic/my_simple_ping/my_simple_ping.cpp | 8 +- examples/opbox/basic/rdma_ping/rdma_ping.cpp | 8 +- examples/opbox/benchmarks/CMakeLists.txt | 1 + examples/opbox/benchmarks/atomics/atomics.cpp | 8 +- .../opbox/benchmarks/connect/CMakeLists.txt | 16 + examples/opbox/benchmarks/connect/connect.cpp | 226 ++ .../benchmarks/msgplusrdma/msgplusrdma.cpp | 8 +- .../opbox/benchmarks/rdma/OpBenchmarkGet.cpp | 6 + .../opbox/benchmarks/rdma/OpBenchmarkGet.hh | 2 + .../opbox/benchmarks/rdma/OpBenchmarkPut.cpp | 6 + .../opbox/benchmarks/rdma/OpBenchmarkPut.hh | 2 + examples/opbox/benchmarks/rdma/rdma.cpp | 8 +- .../short_message/short_message.cpp | 8 +- .../opbox/collectives/rapidfire/rapidfire.cpp | 6 +- examples/opbox/collectives/ringer/ringer.cpp | 2 +- .../scatter_gather/scatter_gather.cpp | 8 +- examples/opbox/support/Globals.cpp | 2 +- examples/opbox/support/Globals.hh | 2 +- .../mpi_sync_start_advanced.cpp | 4 +- examples/whookie/CMakeLists.txt | 2 +- examples/whookie/README.md | 8 +- examples/whookie/simple/CMakeLists.txt | 10 +- examples/whookie/simple/bootstrap_example.cpp | 26 +- examples/whookie/simple/killit_example.cpp | 16 +- src/dirman/CMakeLists.txt | 2 +- src/dirman/DirMan.cpp | 52 +- src/dirman/DirMan.hh | 4 + src/dirman/README_DirMan.md | 2 +- src/dirman/common/DirectoryCache.cpp | 92 +- src/dirman/common/DirectoryCache.hh | 5 +- src/dirman/common/DirectoryOwnerCache.cpp | 2 +- src/dirman/common/DirectoryOwnerCache.hh | 2 +- src/dirman/core/DirManCoreBase.cpp | 419 +- src/dirman/core/DirManCoreBase.hh | 42 +- src/dirman/core/DirManCoreCentralized.cpp | 127 +- src/dirman/core/DirManCoreCentralized.hh | 5 +- src/dirman/core/DirManCoreStatic.cpp | 31 +- src/dirman/core/DirManCoreStatic.hh | 4 +- src/dirman/core/DirManCoreUnconfigured.cpp | 5 +- src/dirman/core/DirManCoreUnconfigured.hh | 2 + src/dirman/core/Singleton.cpp | 16 +- src/dirman/ops/OpDirManCentralized.cpp | 9 +- src/dirman/ops/OpDirManCentralized.hh | 1 + src/dirman/ops/OpDirManCentralized_Target.cpp | 3 + src/dirman/ops/msg_dirman.hh | 5 + src/faodel-common/Bootstrap.cpp | 6 +- src/faodel-common/BootstrapImplementation.cpp | 4 +- src/faodel-common/Configuration.cpp | 113 +- src/faodel-common/Configuration.hh | 7 +- src/faodel-common/DirectoryInfo.cpp | 83 +- src/faodel-common/DirectoryInfo.hh | 19 +- src/faodel-common/LoggingInterface.cpp | 18 +- src/faodel-common/NodeID.hh | 2 +- src/faodel-common/README_Common.md | 2 +- src/faodel-common/ReplyStream.cpp | 41 + src/faodel-common/ReplyStream.hh | 8 +- src/faodel-common/ResourceURL.cpp | 94 +- src/faodel-common/ResourceURL.hh | 22 +- src/faodel-common/StringHelpers.cpp | 32 + src/faodel-common/StringHelpers.hh | 2 + src/faodel-services/CMakeLists.txt | 2 +- src/faodel-services/MPISyncStart.cpp | 10 +- src/kelpie/CMakeLists.txt | 30 +- src/kelpie/Key.hh | 2 +- src/kelpie/README_Kelpie.md | 2 +- src/kelpie/common/Types.cpp | 52 +- src/kelpie/common/Types.hh | 26 +- src/kelpie/core/KelpieCoreNoNet.cpp | 12 +- src/kelpie/core/KelpieCoreNoNet.hh | 2 +- src/kelpie/core/KelpieCoreStandard.cpp | 16 +- src/kelpie/core/KelpieCoreStandard.hh | 2 +- src/kelpie/core/Singleton.cpp | 2 +- src/kelpie/ioms/IomBase.hh | 6 +- src/kelpie/ioms/IomCassandra.cpp | 432 +++ src/kelpie/ioms/IomCassandra.hh | 76 + src/kelpie/ioms/IomHDF5.cpp | 2 +- src/kelpie/ioms/IomLevelDB.cpp | 2 +- src/kelpie/ioms/IomPosixIndividualObjects.cpp | 5 +- src/kelpie/ioms/IomRegistry.cpp | 24 +- src/kelpie/ioms/IomRegistry.hh | 2 +- src/kelpie/localkv/LocalKV.cpp | 114 +- src/kelpie/localkv/LocalKV.hh | 18 +- src/kelpie/localkv/LocalKVCell.cpp | 18 +- src/kelpie/localkv/LocalKVCell.hh | 2 +- src/kelpie/localkv/LocalKVRow.cpp | 63 +- src/kelpie/localkv/LocalKVRow.hh | 2 + src/kelpie/ops/direct/OpKelpieGetBounded.cpp | 13 +- src/kelpie/ops/direct/OpKelpieGetBounded.hh | 16 +- .../ops/direct/OpKelpieGetUnbounded.cpp | 6 +- src/kelpie/ops/direct/OpKelpieList.cpp | 166 + src/kelpie/ops/direct/OpKelpieList.hh | 114 + src/kelpie/ops/direct/OpKelpieMeta.cpp | 9 +- src/kelpie/ops/direct/OpKelpiePublish.cpp | 6 +- src/kelpie/ops/direct/msg_direct.cpp | 5 + src/kelpie/ops/direct/msg_direct.hh | 25 +- src/kelpie/pools/DHTPool/DHTPool.cpp | 46 +- src/kelpie/pools/DHTPool/DHTPool.hh | 1 + src/kelpie/pools/LocalPool/LocalPool.cpp | 14 + src/kelpie/pools/LocalPool/LocalPool.hh | 1 + src/kelpie/pools/Pool.cpp | 12 + src/kelpie/pools/Pool.hh | 3 +- src/kelpie/pools/PoolBase.cpp | 2 +- src/kelpie/pools/PoolBase.hh | 3 + src/kelpie/pools/PoolRegistry.cpp | 18 +- src/kelpie/pools/PoolRegistry.hh | 2 +- .../UnconfiguredPool/UnconfiguredPool.cpp | 8 +- .../UnconfiguredPool/UnconfiguredPool.hh | 1 + src/kelpie/services/PoolServerDriver.cpp | 18 +- src/kelpie/services/PoolServerDriver.hh | 2 +- src/lunasa/CMakeLists.txt | 6 +- src/lunasa/DataObject.cpp | 38 + src/lunasa/DataObject.hh | 11 +- src/lunasa/Lunasa.cpp | 8 +- src/lunasa/README_Lunasa.md | 11 +- src/lunasa/allocators/AllocatorBase.cpp | 4 +- src/lunasa/allocators/AllocatorBase.hh | 10 +- src/lunasa/allocators/AllocatorTcmalloc.cpp | 9 +- src/lunasa/allocators/AllocatorTcmalloc.hh | 2 +- src/lunasa/common/Allocation.hh | 7 +- src/lunasa/common/DataObjectTypeRegistry.cpp | 2 +- src/lunasa/core/LunasaCoreBase.cpp | 6 +- src/lunasa/core/LunasaCoreBase.hh | 2 +- src/lunasa/core/LunasaCoreSplit.cpp | 50 +- src/lunasa/core/LunasaCoreSplit.hh | 10 +- src/lunasa/core/LunasaCoreUnconfigured.cpp | 4 +- src/lunasa/core/LunasaCoreUnconfigured.hh | 2 +- src/lunasa/core/Singleton.cpp | 14 +- src/nnti/CMakeLists.txt | 85 +- src/nnti/nnti.cpp | 2 +- src/nnti/nnti.h | 3 +- src/nnti/nntiConfig.h.in | 16 +- src/nnti/nntiProbeCereal.cmake | 15 + src/nnti/nntiProbeNetwork.cmake | 14 +- src/nnti/nntiProbeXDR.cmake | 22 +- src/nnti/nntiProcessXDR.cmake | 4 +- src/nnti/nnti_buffer.cpp | 4 +- src/nnti/nnti_buffer.hpp | 81 +- src/nnti/nnti_callback.cpp | 101 + src/nnti/nnti_callback.hpp | 87 +- src/nnti/nnti_connection.hpp | 4 +- src/nnti/nnti_datatype.hpp | 3 +- src/nnti/nnti_eq.hpp | 2 +- src/nnti/nnti_freelist.hpp | 2 +- src/nnti/nnti_op.hpp | 2 +- src/nnti/nnti_peer.hpp | 79 +- src/nnti/nnti_pid.hpp | 2 +- src/nnti/nnti_serialize.cpp | 255 ++ src/nnti/nnti_serialize.hpp | 72 + src/nnti/nnti_transport.cpp | 70 + src/nnti/nnti_transport.hpp | 40 +- src/nnti/nnti_types.h | 55 +- src/nnti/nnti_url.cpp | 2 +- src/nnti/nnti_url.hpp | 2 +- src/nnti/nnti_wid.cpp | 246 ++ src/nnti/nnti_wid.hpp | 219 +- src/nnti/nnti_wr.cpp | 162 + src/nnti/nnti_wr.hpp | 134 +- src/nnti/serializers/cereal/nnti_cereal.hpp | 6 + src/nnti/serializers/cereal/nnti_packable.hpp | 402 ++ src/nnti/serializers/xdr/nnti_packable.x | 68 +- src/nnti/serializers/xdr/nnti_xdr.hpp | 7 + src/nnti/transports/base/base_transport.cpp | 7 +- .../transports/ibverbs/ibverbs_atomic_op.hpp | 2 +- .../transports/ibverbs/ibverbs_buffer.cpp | 66 +- .../transports/ibverbs/ibverbs_buffer.hpp | 2 +- .../transports/ibverbs/ibverbs_cmd_buffer.cpp | 24 +- .../transports/ibverbs/ibverbs_cmd_msg.hpp | 2 +- .../transports/ibverbs/ibverbs_cmd_op.hpp | 28 +- .../transports/ibverbs/ibverbs_connection.hpp | 2 +- src/nnti/transports/ibverbs/ibverbs_peer.hpp | 2 +- .../transports/ibverbs/ibverbs_rdma_op.hpp | 2 +- .../transports/ibverbs/ibverbs_transport.cpp | 548 ++- .../transports/ibverbs/ibverbs_transport.hpp | 56 +- src/nnti/transports/mpi/mpi_buffer.cpp | 2 +- src/nnti/transports/mpi/mpi_buffer.hpp | 2 +- src/nnti/transports/mpi/mpi_cmd_msg.hpp | 2 +- src/nnti/transports/mpi/mpi_cmd_op.hpp | 2 +- src/nnti/transports/mpi/mpi_peer.hpp | 2 +- src/nnti/transports/mpi/mpi_transport.cpp | 296 +- src/nnti/transports/mpi/mpi_transport.hpp | 16 +- src/nnti/transports/ugni/ugni_atomic_op.hpp | 2 +- src/nnti/transports/ugni/ugni_buffer.cpp | 2 +- src/nnti/transports/ugni/ugni_buffer.hpp | 2 +- src/nnti/transports/ugni/ugni_cmd_msg.hpp | 2 +- src/nnti/transports/ugni/ugni_cmd_op.cpp | 2 +- src/nnti/transports/ugni/ugni_cmd_op.hpp | 2 +- src/nnti/transports/ugni/ugni_cmd_tgt.hpp | 2 +- src/nnti/transports/ugni/ugni_connection.cpp | 2 +- src/nnti/transports/ugni/ugni_connection.hpp | 2 +- src/nnti/transports/ugni/ugni_peer.hpp | 2 +- src/nnti/transports/ugni/ugni_rdma_op.hpp | 51 +- src/nnti/transports/ugni/ugni_transport.cpp | 182 +- src/nnti/transports/ugni/ugni_transport.hpp | 22 +- src/opbox/CMakeLists.txt | 2 +- src/opbox/README_OpBox.md | 2 +- src/opbox/common/Message.hh | 5 +- src/opbox/common/OpRegistry.cpp | 10 +- src/opbox/common/OpRegistry.hh | 8 +- src/opbox/core/OpBoxCoreStandard.cpp | 18 +- src/opbox/core/OpBoxCoreStandard.hh | 2 +- src/opbox/core/OpBoxCoreThreaded.cpp | 37 +- src/opbox/core/OpBoxCoreThreaded.hh | 4 +- src/opbox/core/OpBoxCoreUnconfigured.cpp | 4 +- src/opbox/core/Singleton.cpp | 12 +- src/opbox/core/Singleton.hh | 2 +- .../net/libfabric_wrapper/fab_transport.cpp | 59 +- .../libfabric_wrapper/libfabric_wrapper.cpp | 10 +- src/opbox/net/nnti/NetNnti.cpp | 14 +- src/sbl/README_SBL.md | 2 +- src/sbl/sbl_boost_headers.hh | 6 +- src/sbl/sbl_logger.hh | 8 +- src/sbl/sbl_source.cpp | 59 +- src/sbl/sbl_source.hh | 11 +- src/sbl/sbl_stream.hh | 8 +- src/sbl/sbl_types.hh | 8 +- src/whookie/CMakeLists.txt | 12 +- src/whookie/README_Whookie.md | 65 +- src/whookie/Server.hh | 13 +- src/whookie/Whookie.hh | 8 +- src/whookie/client/Client.cpp | 6 +- src/whookie/client/Client.hh | 10 +- src/whookie/server/ServerBoost.cpp | 23 +- src/whookie/server/boost/connection.cpp | 6 +- src/whookie/server/boost/connection.hpp | 14 +- .../server/boost/connection_manager.cpp | 2 +- .../server/boost/connection_manager.hpp | 8 +- src/whookie/server/boost/header.hpp | 6 +- src/whookie/server/boost/mime_types.cpp | 2 +- src/whookie/server/boost/mime_types.hpp | 6 +- src/whookie/server/boost/reply.cpp | 2 +- src/whookie/server/boost/reply.hpp | 8 +- src/whookie/server/boost/request.hpp | 8 +- src/whookie/server/boost/request_handler.cpp | 61 +- src/whookie/server/boost/request_handler.hpp | 21 +- src/whookie/server/boost/request_parser.cpp | 4 +- src/whookie/server/boost/request_parser.hpp | 6 +- src/whookie/server/boost/server.cpp | 58 +- src/whookie/server/boost/server.hpp | 34 +- tests/common/CMakeLists.txt | 2 +- tests/common/unit/tb_common_resourceurl.cpp | 76 +- tests/common/unit/tb_common_structs.cpp | 22 +- .../component/mpi_dirman_centralized.cpp | 16 +- .../component/mpi_dirman_op_messages.cpp | 8 +- tests/dirman/component/mpi_dirman_restart.cpp | 12 +- .../dirman/unit/tb_dirman_corecentralized.cpp | 20 +- .../dirman/unit/tb_dirman_directorycache.cpp | 82 +- tests/kelpie/CMakeLists.txt | 32 +- tests/kelpie/component/mpi_kelpie_dht.cpp | 146 +- tests/kelpie/component/mpi_kelpie_iom_dht.cpp | 12 +- tests/kelpie/component/mpi_kelpie_rft.cpp | 24 +- .../component/mpi_kelpie_simple_peer.cpp | 6 +- .../component/mpi_kelpie_standalone.cpp | 8 +- .../nonet/tb_kelpie_nonet_iompio.cpp | 2 +- .../component/support/ExperimentLauncher.hh | 2 +- tests/kelpie/component/support/Globals.cpp | 2 +- tests/kelpie/component/support/Globals.hh | 2 +- .../unit/ioms/tb_kelpie_iom_all_basic.cpp | 2 +- .../unit/ioms/tb_kelpie_iom_pio_basic.cpp | 2 +- .../unit/ioms/tb_kelpie_iom_service_basic.cpp | 306 ++ .../messages/tb_kelpie_message_direct.cpp | 4 +- tests/kelpie/unit/tb_kelpie_localkv.cpp | 169 +- tests/lunasa/CMakeLists.txt | 1 + .../component/nnti/mpi_lunasa_nnti_put.cpp | 16 +- .../nnti/mpi_lunasa_nnti_register_memory.cpp | 6 +- .../component/nnti/mpi_lunasa_nnti_send.cpp | 10 +- .../nnti/mpi_lunasa_nnti_send_user.cpp | 10 +- .../component/tb_lunasa_backburner_ldo.cpp | 2 +- tests/lunasa/component/tb_lunasa_copy_ldo.cpp | 219 ++ tests/nnti/benchmarks/CMakeLists.txt | 20 +- tests/nnti/benchmarks/bench_utils.cpp | 8 +- tests/nnti/c-api/CMakeLists.txt | 1 - tests/nnti/c-api/IBConnectTest.c | 8 +- tests/nnti/c-api/IBQueueSendTest1.c | 16 +- tests/nnti/c-api/IBQueueSendTest2.c | 16 +- tests/nnti/c-api/IBRdmaOpTest.c | 16 +- tests/nnti/c-api/IBShortSendTest.c | 16 +- tests/nnti/c-api/IBUnexpectedSendTest.c | 12 +- tests/nnti/c-api/NntiAllocFreeTest.c | 14 +- tests/nnti/c-api/NntiLoggerTest1.c | 4 +- tests/nnti/c-api/NntiLoggerTest2.c | 4 +- tests/nnti/c-api/NntiLoggerTest3.c | 4 +- tests/nnti/c-api/test_utils.cpp | 16 +- tests/nnti/cpp-api/AtomicOpTest.cpp | 24 +- tests/nnti/cpp-api/CMakeLists.txt | 3 +- tests/nnti/cpp-api/RdmaLengthTest.cpp | 266 ++ tests/nnti/cpp-api/RdmaOpTest.cpp | 43 +- tests/nnti/cpp-api/test_utils.cpp | 96 +- tests/nnti/cpp-api/test_utils.hpp | 4 + tests/opbox/CMakeLists.txt | 31 +- tests/opbox/component/mpi_opbox_ping.cpp | 8 +- tests/opbox/component/support/Globals.cpp | 2 +- tests/opbox/component/support/Globals.hh | 2 +- tests/opbox/ops/tb_FabConnTest.cpp | 10 +- tests/opbox/ops/tb_OpboxOpPingFabTest.cpp | 12 +- .../opbox/unit/mpi_opbox_message_helpers.cpp | 2 +- tests/opbox/unit/mpi_opbox_triggerop.cpp | 2 +- tests/services/CMakeLists.txt | 4 +- .../component/mpi_service_mpisyncstart.cpp | 28 +- tests/whookie/CMakeLists.txt | 10 +- .../mpi_whookie_multiprocess_threaded.cpp | 24 +- .../whookie/component/mpi_whookie_restart.cpp | 36 +- .../component/tb_whookie_clientserver.cpp | 62 +- .../standalone/simple_whookie_example.cpp | 20 +- tools/CMakeLists.txt | 18 - tools/faodel-cli/CMakeLists.txt | 39 + tools/faodel-cli/build_info.cpp | 202 + tools/faodel-cli/build_info_ib.cpp | 3 +- tools/faodel-cli/config_info.cpp | 140 + tools/faodel-cli/dirman_server.cpp | 156 + tools/faodel-cli/faodel_cli.cpp | 220 ++ tools/faodel-cli/faodel_cli.hh | 44 + tools/faodel-cli/kelpie_client.cpp | 536 +++ tools/faodel-cli/kelpie_server.cpp | 199 + tools/faodel-cli/resource.cpp | 337 ++ tools/faodel-cli/whookie_client.cpp | 120 + tools/faodel-info/CMakeLists.txt | 33 - tools/faodel-info/cmake_info.cpp | 146 - tools/faodel-info/faodel_info.cpp | 133 - tools/kelpie-server/CMakeLists.txt | 17 +- tools/whookie/CMakeLists.txt | 15 - tools/whookie/whookie.cpp | 74 - tpl/cereal/.gitignore | 49 + tpl/cereal/.travis.yml | 39 + tpl/cereal/CMakeLists.txt | 37 + tpl/cereal/LICENSE | 24 + tpl/cereal/README.md | 85 + tpl/cereal/appveyor.yml | 35 + tpl/cereal/doc/CMakeLists.txt | 18 + tpl/cereal/doc/DoxygenLayout.xml | 190 + tpl/cereal/doc/doxygen.in | 1870 +++++++++ tpl/cereal/doc/footer.html | 31 + tpl/cereal/doc/mainpage.dox | 47 + tpl/cereal/include/cereal/access.hpp | 448 +++ .../include/cereal/archives/adapters.hpp | 163 + tpl/cereal/include/cereal/archives/binary.hpp | 169 + tpl/cereal/include/cereal/archives/json.hpp | 981 +++++ .../cereal/archives/portable_binary.hpp | 334 ++ tpl/cereal/include/cereal/archives/xml.hpp | 897 +++++ tpl/cereal/include/cereal/cereal.hpp | 987 +++++ tpl/cereal/include/cereal/details/helpers.hpp | 383 ++ .../cereal/details/polymorphic_impl.hpp | 764 ++++ .../cereal/details/polymorphic_impl_fwd.hpp | 65 + .../include/cereal/details/static_object.hpp | 127 + tpl/cereal/include/cereal/details/traits.hpp | 1389 +++++++ tpl/cereal/include/cereal/details/util.hpp | 84 + tpl/cereal/include/cereal/external/base64.hpp | 127 + .../cereal/external/rapidjson/allocators.h | 271 ++ .../cereal/external/rapidjson/document.h | 2575 +++++++++++++ .../cereal/external/rapidjson/encodedstream.h | 299 ++ .../cereal/external/rapidjson/encodings.h | 716 ++++ .../cereal/external/rapidjson/error/en.h | 74 + .../cereal/external/rapidjson/error/error.h | 155 + .../external/rapidjson/filereadstream.h | 99 + .../external/rapidjson/filewritestream.h | 104 + .../include/cereal/external/rapidjson/fwd.h | 151 + .../external/rapidjson/internal/biginteger.h | 290 ++ .../external/rapidjson/internal/diyfp.h | 258 ++ .../cereal/external/rapidjson/internal/dtoa.h | 245 ++ .../external/rapidjson/internal/ieee754.h | 78 + .../cereal/external/rapidjson/internal/itoa.h | 304 ++ .../cereal/external/rapidjson/internal/meta.h | 181 + .../external/rapidjson/internal/pow10.h | 55 + .../external/rapidjson/internal/regex.h | 701 ++++ .../external/rapidjson/internal/stack.h | 230 ++ .../external/rapidjson/internal/strfunc.h | 55 + .../external/rapidjson/internal/strtod.h | 269 ++ .../cereal/external/rapidjson/internal/swap.h | 46 + .../external/rapidjson/istreamwrapper.h | 116 + .../cereal/external/rapidjson/memorybuffer.h | 70 + .../cereal/external/rapidjson/memorystream.h | 76 + .../external/rapidjson/msinttypes/inttypes.h | 316 ++ .../external/rapidjson/msinttypes/stdint.h | 300 ++ .../external/rapidjson/ostreamwrapper.h | 81 + .../cereal/external/rapidjson/pointer.h | 1358 +++++++ .../cereal/external/rapidjson/prettywriter.h | 255 ++ .../cereal/external/rapidjson/rapidjson.h | 615 +++ .../cereal/external/rapidjson/reader.h | 1879 +++++++++ .../cereal/external/rapidjson/schema.h | 2006 ++++++++++ .../cereal/external/rapidjson/stream.h | 179 + .../cereal/external/rapidjson/stringbuffer.h | 117 + .../cereal/external/rapidjson/writer.h | 610 +++ .../cereal/external/rapidxml/license.txt | 52 + .../cereal/external/rapidxml/manual.html | 406 ++ .../cereal/external/rapidxml/rapidxml.hpp | 2624 +++++++++++++ .../external/rapidxml/rapidxml_iterators.hpp | 175 + .../external/rapidxml/rapidxml_print.hpp | 426 +++ .../external/rapidxml/rapidxml_utils.hpp | 123 + tpl/cereal/include/cereal/macros.hpp | 135 + tpl/cereal/include/cereal/types/array.hpp | 79 + .../include/cereal/types/base_class.hpp | 201 + tpl/cereal/include/cereal/types/bitset.hpp | 176 + .../include/cereal/types/boost_variant.hpp | 106 + tpl/cereal/include/cereal/types/chrono.hpp | 72 + tpl/cereal/include/cereal/types/common.hpp | 129 + tpl/cereal/include/cereal/types/complex.hpp | 56 + .../concepts/pair_associative_container.hpp | 73 + tpl/cereal/include/cereal/types/deque.hpp | 62 + .../include/cereal/types/forward_list.hpp | 68 + .../include/cereal/types/functional.hpp | 43 + tpl/cereal/include/cereal/types/list.hpp | 62 + tpl/cereal/include/cereal/types/map.hpp | 36 + tpl/cereal/include/cereal/types/memory.hpp | 425 +++ .../include/cereal/types/polymorphic.hpp | 481 +++ tpl/cereal/include/cereal/types/queue.hpp | 132 + tpl/cereal/include/cereal/types/set.hpp | 103 + tpl/cereal/include/cereal/types/stack.hpp | 76 + tpl/cereal/include/cereal/types/string.hpp | 61 + tpl/cereal/include/cereal/types/tuple.hpp | 123 + .../include/cereal/types/unordered_map.hpp | 36 + .../include/cereal/types/unordered_set.hpp | 99 + tpl/cereal/include/cereal/types/utility.hpp | 47 + tpl/cereal/include/cereal/types/valarray.hpp | 89 + tpl/cereal/include/cereal/types/vector.hpp | 112 + tpl/cereal/sandbox/CMakeLists.txt | 17 + tpl/cereal/sandbox/performance.cpp | 469 +++ tpl/cereal/sandbox/sandbox.cpp | 820 ++++ tpl/cereal/sandbox/sandbox_json.cpp | 446 +++ tpl/cereal/sandbox/sandbox_rtti.cpp | 231 ++ .../sandbox/sandbox_shared_lib/CMakeLists.txt | 1 + .../sandbox/sandbox_shared_lib/base.cpp | 9 + .../sandbox/sandbox_shared_lib/base.hpp | 43 + .../sandbox/sandbox_shared_lib/derived.cpp | 10 + .../sandbox/sandbox_shared_lib/derived.hpp | 19 + tpl/cereal/sandbox/sandbox_vs.cpp | 278 ++ tpl/cereal/scripts/add_rapidjson_prefix.sh | 4 + tpl/cereal/scripts/appveyor.bat | 75 + tpl/cereal/scripts/renameincludes.sh | 12 + tpl/cereal/scripts/updatecoverage.sh | 33 + tpl/cereal/scripts/updatedoc.in | 25 + tpl/cereal/unittests/CMakeLists.txt | 92 + tpl/cereal/unittests/array.cpp | 52 + tpl/cereal/unittests/array.hpp | 95 + tpl/cereal/unittests/basic_string.cpp | 171 + tpl/cereal/unittests/basic_string.hpp | 114 + tpl/cereal/unittests/bitset.cpp | 52 + tpl/cereal/unittests/bitset.hpp | 87 + tpl/cereal/unittests/boost_variant.cpp | 52 + tpl/cereal/unittests/boost_variant.hpp | 70 + tpl/cereal/unittests/chrono.cpp | 52 + tpl/cereal/unittests/chrono.hpp | 105 + .../unittests/cmake-config-module.cmake | 123 + tpl/cereal/unittests/common.hpp | 223 ++ tpl/cereal/unittests/complex.cpp | 52 + tpl/cereal/unittests/complex.hpp | 77 + tpl/cereal/unittests/deque.cpp | 52 + tpl/cereal/unittests/deque.hpp | 101 + tpl/cereal/unittests/doctest.h | 3374 +++++++++++++++++ tpl/cereal/unittests/forward_list.cpp | 52 + tpl/cereal/unittests/forward_list.hpp | 95 + tpl/cereal/unittests/list.cpp | 52 + tpl/cereal/unittests/list.hpp | 95 + tpl/cereal/unittests/load_construct.cpp | 52 + tpl/cereal/unittests/load_construct.hpp | 260 ++ tpl/cereal/unittests/map.cpp | 72 + tpl/cereal/unittests/map.hpp | 169 + tpl/cereal/unittests/memory.cpp | 72 + tpl/cereal/unittests/memory.hpp | 116 + tpl/cereal/unittests/memory_cycles.cpp | 52 + tpl/cereal/unittests/memory_cycles.hpp | 142 + tpl/cereal/unittests/multimap.cpp | 52 + tpl/cereal/unittests/multimap.hpp | 129 + tpl/cereal/unittests/multiset.cpp | 52 + tpl/cereal/unittests/multiset.hpp | 134 + tpl/cereal/unittests/pair.cpp | 51 + tpl/cereal/unittests/pair.hpp | 92 + tpl/cereal/unittests/pod.cpp | 52 + tpl/cereal/unittests/pod.hpp | 147 + tpl/cereal/unittests/polymorphic.cpp | 74 + tpl/cereal/unittests/polymorphic.hpp | 359 ++ tpl/cereal/unittests/portability_test.cpp | 202 + .../unittests/portable_binary_archive.cpp | 134 + .../unittests/portable_binary_archive.hpp | 156 + tpl/cereal/unittests/priority_queue.cpp | 52 + tpl/cereal/unittests/priority_queue.hpp | 107 + tpl/cereal/unittests/queue.cpp | 52 + tpl/cereal/unittests/queue.hpp | 107 + .../unittests/run_portability_test.cmake | 16 + tpl/cereal/unittests/run_valgrind.sh | 10 + tpl/cereal/unittests/set.cpp | 52 + tpl/cereal/unittests/set.hpp | 95 + tpl/cereal/unittests/stack.cpp | 52 + tpl/cereal/unittests/stack.hpp | 107 + tpl/cereal/unittests/structs.cpp | 52 + tpl/cereal/unittests/structs.hpp | 68 + tpl/cereal/unittests/structs_minimal.cpp | 52 + tpl/cereal/unittests/structs_minimal.hpp | 254 ++ tpl/cereal/unittests/structs_specialized.cpp | 52 + tpl/cereal/unittests/structs_specialized.hpp | 461 +++ tpl/cereal/unittests/tuple.cpp | 52 + tpl/cereal/unittests/tuple.hpp | 101 + tpl/cereal/unittests/unordered_loads.cpp | 42 + tpl/cereal/unittests/unordered_loads.hpp | 149 + tpl/cereal/unittests/unordered_map.cpp | 52 + tpl/cereal/unittests/unordered_map.hpp | 124 + tpl/cereal/unittests/unordered_multimap.cpp | 52 + tpl/cereal/unittests/unordered_multimap.hpp | 155 + tpl/cereal/unittests/unordered_multiset.cpp | 52 + tpl/cereal/unittests/unordered_multiset.hpp | 134 + tpl/cereal/unittests/unordered_set.cpp | 52 + tpl/cereal/unittests/unordered_set.hpp | 114 + tpl/cereal/unittests/user_data_adapters.cpp | 52 + tpl/cereal/unittests/user_data_adapters.hpp | 120 + tpl/cereal/unittests/valarray.cpp | 52 + tpl/cereal/unittests/valarray.hpp | 101 + tpl/cereal/unittests/vector.cpp | 52 + tpl/cereal/unittests/vector.hpp | 110 + tpl/cereal/unittests/versioning.cpp | 74 + tpl/cereal/unittests/versioning.hpp | 217 ++ tpl/cereal/vs2013/.gitignore | 3 + tpl/cereal/vs2013/sandbox/sandbox.vcxproj | 256 ++ .../vs2013/sandbox/sandbox.vcxproj.filters | 22 + .../vs2013/sandbox_json/sandbox_json.vcxproj | 248 ++ .../sandbox_json/sandbox_json.vcxproj.filters | 22 + .../vs2013/sandbox_rtti/sandbox_rtti.vcxproj | 252 ++ .../sandbox_rtti/sandbox_rtti.vcxproj.filters | 22 + .../vs2013/sandbox_vs/sandbox_vs.vcxproj | 266 ++ .../sandbox_vs/sandbox_vs.vcxproj.filters | 22 + .../sandbox_vs_dll/sandbox_vs_dll.vcxproj | 261 ++ .../sandbox_vs_dll.vcxproj.filters | 33 + tpl/cereal/vs2013/unittests/main.cpp | 30 + tpl/cereal/vs2013/unittests/unittests.vcxproj | 327 ++ .../unittests/unittests.vcxproj.filters | 127 + tpl/cereal/vs2013/vs2013.sln | 147 + tpl/gperftools/CMakeLists.txt | 75 +- tpl/gperftools/ChangeLog | 648 +--- tpl/gperftools/NEWS | 415 ++ tpl/gperftools/README | 41 +- tpl/gperftools/benchmark/malloc_bench.cc | 374 +- tpl/gperftools/doc/cpuprofile-fileformat.html | 264 -- tpl/gperftools/doc/cpuprofile.html | 536 --- tpl/gperftools/doc/designstyle.css | 109 - tpl/gperftools/doc/heap-example1.png | Bin 37619 -> 0 bytes tpl/gperftools/doc/heap_checker.html | 534 --- tpl/gperftools/doc/heapprofile.html | 382 -- tpl/gperftools/doc/index.html | 20 - tpl/gperftools/doc/overview.dot | 15 - tpl/gperftools/doc/overview.gif | Bin 6472 -> 0 bytes tpl/gperftools/doc/pageheap.dot | 29 - tpl/gperftools/doc/pageheap.gif | Bin 15486 -> 0 bytes tpl/gperftools/doc/pprof-test-big.gif | Bin 111566 -> 0 bytes tpl/gperftools/doc/pprof-test.gif | Bin 56995 -> 0 bytes tpl/gperftools/doc/pprof-vsnprintf-big.gif | Bin 100721 -> 0 bytes tpl/gperftools/doc/pprof-vsnprintf.gif | Bin 31054 -> 0 bytes tpl/gperftools/doc/pprof.1 | 131 - tpl/gperftools/doc/pprof.see_also | 11 - tpl/gperftools/doc/pprof_remote_servers.html | 260 -- tpl/gperftools/doc/spanmap.dot | 22 - tpl/gperftools/doc/spanmap.gif | Bin 8482 -> 0 bytes tpl/gperftools/doc/t-test1.times.txt | 480 --- ...loc-opspercpusec.vs.threads.1024.bytes.png | Bin 1882 -> 0 bytes ...lloc-opspercpusec.vs.threads.128.bytes.png | Bin 1731 -> 0 bytes ...c-opspercpusec.vs.threads.131072.bytes.png | Bin 1314 -> 0 bytes ...oc-opspercpusec.vs.threads.16384.bytes.png | Bin 1815 -> 0 bytes ...loc-opspercpusec.vs.threads.2048.bytes.png | Bin 1877 -> 0 bytes ...lloc-opspercpusec.vs.threads.256.bytes.png | Bin 1838 -> 0 bytes ...oc-opspercpusec.vs.threads.32768.bytes.png | Bin 1516 -> 0 bytes ...loc-opspercpusec.vs.threads.4096.bytes.png | Bin 2005 -> 0 bytes ...lloc-opspercpusec.vs.threads.512.bytes.png | Bin 1683 -> 0 bytes ...alloc-opspercpusec.vs.threads.64.bytes.png | Bin 1656 -> 0 bytes ...oc-opspercpusec.vs.threads.65536.bytes.png | Bin 1498 -> 0 bytes ...loc-opspercpusec.vs.threads.8192.bytes.png | Bin 1912 -> 0 bytes .../tcmalloc-opspersec.vs.size.1.threads.png | Bin 1689 -> 0 bytes .../tcmalloc-opspersec.vs.size.12.threads.png | Bin 2216 -> 0 bytes .../tcmalloc-opspersec.vs.size.16.threads.png | Bin 2010 -> 0 bytes .../tcmalloc-opspersec.vs.size.2.threads.png | Bin 2163 -> 0 bytes .../tcmalloc-opspersec.vs.size.20.threads.png | Bin 2147 -> 0 bytes .../tcmalloc-opspersec.vs.size.3.threads.png | Bin 2270 -> 0 bytes .../tcmalloc-opspersec.vs.size.4.threads.png | Bin 2174 -> 0 bytes .../tcmalloc-opspersec.vs.size.5.threads.png | Bin 1995 -> 0 bytes .../tcmalloc-opspersec.vs.size.8.threads.png | Bin 2156 -> 0 bytes tpl/gperftools/doc/tcmalloc.html | 765 ---- tpl/gperftools/doc/threadheap.dot | 21 - tpl/gperftools/doc/threadheap.gif | Bin 7571 -> 0 bytes tpl/gperftools/src/base/atomicops.h | 8 + tpl/gperftools/src/base/basictypes.h | 52 +- tpl/gperftools/src/base/commandlineflags.h | 11 +- tpl/gperftools/src/base/elfcore.h | 6 +- .../src/base/linux_syscall_support.h | 531 ++- tpl/gperftools/src/base/linuxthreads.h | 7 +- tpl/gperftools/src/base/low_level_alloc.cc | 79 +- tpl/gperftools/src/base/low_level_alloc.h | 13 + tpl/gperftools/src/base/spinlock.cc | 7 + tpl/gperftools/src/base/spinlock_linux-inl.h | 23 +- tpl/gperftools/src/base/sysinfo.cc | 49 +- tpl/gperftools/src/base/thread_lister.c | 8 +- tpl/gperftools/src/base/vdso_support.cc | 1 - tpl/gperftools/src/base/vdso_support.h | 4 + tpl/gperftools/src/central_freelist.cc | 10 +- tpl/gperftools/src/common.cc | 31 +- tpl/gperftools/src/common.h | 119 +- tpl/gperftools/src/config.h | 356 -- tpl/gperftools/src/debugallocation.cc | 257 +- tpl/gperftools/src/fake_stacktrace_scope.cc | 39 + tpl/gperftools/src/getpc.h | 8 + tpl/gperftools/src/google/heap-checker.h | 2 +- tpl/gperftools/src/google/heap-profiler.h | 2 +- tpl/gperftools/src/google/malloc_extension.h | 2 +- .../src/google/malloc_extension_c.h | 2 +- tpl/gperftools/src/google/malloc_hook.h | 2 +- tpl/gperftools/src/google/malloc_hook_c.h | 2 +- tpl/gperftools/src/google/profiler.h | 2 +- tpl/gperftools/src/google/stacktrace.h | 2 +- tpl/gperftools/src/google/tcmalloc.h | 2 +- tpl/gperftools/src/gperftools/heap-checker.h | 2 +- tpl/gperftools/src/gperftools/heap-profiler.h | 2 +- .../src/gperftools/malloc_extension.h | 29 +- .../src/gperftools/malloc_extension_c.h | 2 + tpl/gperftools/src/gperftools/malloc_hook.h | 2 +- tpl/gperftools/src/gperftools/nallocx.h | 37 + tpl/gperftools/src/gperftools/profiler.h | 2 +- tpl/gperftools/src/gperftools/tcmalloc.h | 137 - tpl/gperftools/src/gperftools/tcmalloc.h.in | 151 +- tpl/gperftools/src/heap-checker.cc | 6 +- tpl/gperftools/src/heap-profile-table.cc | 24 +- tpl/gperftools/src/heap-profiler.cc | 18 +- tpl/gperftools/src/internal_logging.cc | 2 - tpl/gperftools/src/libc_override.h | 8 + .../src/libc_override_gcc_and_weak.h | 173 +- tpl/gperftools/src/libc_override_glibc.h | 57 - tpl/gperftools/src/libc_override_osx.h | 29 +- tpl/gperftools/src/libc_override_redefine.h | 93 +- tpl/gperftools/src/linked_list.h | 14 +- tpl/gperftools/src/malloc_extension.cc | 22 +- tpl/gperftools/src/malloc_hook-inl.h | 6 +- tpl/gperftools/src/malloc_hook.cc | 19 + tpl/gperftools/src/malloc_hook_mmap_linux.h | 32 +- tpl/gperftools/src/maybe_emergency_malloc.h | 55 + tpl/gperftools/src/maybe_threads.cc | 6 + tpl/gperftools/src/memfs_malloc.cc | 10 +- tpl/gperftools/src/memory_region_map.cc | 19 +- tpl/gperftools/src/memory_region_map.h | 3 + tpl/gperftools/src/packed-cache-inl.h | 109 +- tpl/gperftools/src/page_heap.cc | 273 +- tpl/gperftools/src/page_heap.h | 92 +- tpl/gperftools/src/page_heap_allocator.h | 65 + tpl/gperftools/src/pagemap.h | 42 +- tpl/gperftools/src/pprof | 64 +- tpl/gperftools/src/profile-handler.cc | 317 +- tpl/gperftools/src/profile-handler.h | 21 +- tpl/gperftools/src/profiledata.h | 2 +- tpl/gperftools/src/profiler.cc | 49 +- tpl/gperftools/src/sampler.cc | 70 +- tpl/gperftools/src/sampler.h | 140 +- tpl/gperftools/src/span.h | 75 +- tpl/gperftools/src/stack_trace_table.cc | 119 +- tpl/gperftools/src/stack_trace_table.h | 21 +- tpl/gperftools/src/stacktrace.cc | 76 +- tpl/gperftools/src/stacktrace_libgcc-inl.h | 111 + tpl/gperftools/src/stacktrace_libunwind-inl.h | 4 +- .../src/stacktrace_powerpc-linux-inl.h | 4 +- tpl/gperftools/src/static_vars.cc | 101 +- tpl/gperftools/src/static_vars.h | 42 +- tpl/gperftools/src/symbolize.cc | 31 +- tpl/gperftools/src/system-alloc.cc | 95 +- tpl/gperftools/src/system-alloc.h | 3 +- tpl/gperftools/src/tcmalloc.cc | 1265 ++++-- tpl/gperftools/src/tcmalloc.h | 8 +- .../src/tests/current_allocated_bytes_test.cc | 4 +- .../src/tests/debugallocation_test.sh | 3 + .../src/tests/heap-checker-death_unittest.sh | 2 +- .../src/tests/heap-checker_unittest.cc | 4 +- .../src/tests/heap-profiler_unittest.sh | 7 +- tpl/gperftools/src/tests/markidle_unittest.cc | 18 + tpl/gperftools/src/tests/packed-cache_test.cc | 41 +- tpl/gperftools/src/tests/page_heap_test.cc | 79 +- .../src/tests/profile-handler_unittest.cc | 181 +- .../src/tests/profiledata_unittest.cc | 1 + tpl/gperftools/src/tests/profiler_unittest.cc | 2 +- tpl/gperftools/src/tests/sampler_test.cc | 39 +- .../src/tests/simple_compat_test.cc | 3 + .../src/tests/stack_trace_table_test.cc | 19 +- tpl/gperftools/src/tests/tcmalloc_unittest.cc | 245 +- tpl/gperftools/src/tests/tcmalloc_unittest.sh | 8 +- tpl/gperftools/src/thread_cache.cc | 116 +- tpl/gperftools/src/thread_cache.h | 210 +- tpl/gperftools/src/windows/addr2line-pdb.c | 47 +- tpl/gperftools/src/windows/config.h | 221 +- .../src/windows/gperftools/tcmalloc.h.in | 148 +- tpl/gperftools/src/windows/ia32_modrm_map.cc | 4 +- tpl/gperftools/src/windows/ia32_opcode_map.cc | 4 +- tpl/gperftools/src/windows/mingw.h | 2 + .../src/windows/mini_disassembler.h | 2 +- .../src/windows/mini_disassembler_types.h | 6 +- .../src/windows/override_functions.cc | 82 +- tpl/gperftools/src/windows/patch_functions.cc | 73 +- tpl/gperftools/src/windows/port.cc | 18 +- tpl/gperftools/src/windows/port.h | 2 + .../src/windows/preamble_patcher.cc | 2 +- tpl/gperftools/src/windows/system-alloc.cc | 6 +- 731 files changed, 62288 insertions(+), 10067 deletions(-) create mode 100755 examples/faodel-cli/basic-startstop.sh create mode 100644 examples/opbox/benchmarks/connect/CMakeLists.txt create mode 100644 examples/opbox/benchmarks/connect/connect.cpp create mode 100644 src/kelpie/ioms/IomCassandra.cpp create mode 100644 src/kelpie/ioms/IomCassandra.hh create mode 100644 src/kelpie/ops/direct/OpKelpieList.cpp create mode 100644 src/kelpie/ops/direct/OpKelpieList.hh create mode 100644 src/nnti/nntiProbeCereal.cmake create mode 100644 src/nnti/nnti_callback.cpp create mode 100644 src/nnti/nnti_serialize.cpp create mode 100644 src/nnti/nnti_serialize.hpp create mode 100644 src/nnti/nnti_transport.cpp create mode 100644 src/nnti/nnti_wr.cpp create mode 100644 src/nnti/serializers/cereal/nnti_cereal.hpp create mode 100644 src/nnti/serializers/cereal/nnti_packable.hpp create mode 100644 src/nnti/serializers/xdr/nnti_xdr.hpp create mode 100644 tests/kelpie/unit/ioms/tb_kelpie_iom_service_basic.cpp create mode 100644 tests/lunasa/component/tb_lunasa_copy_ldo.cpp create mode 100644 tests/nnti/cpp-api/RdmaLengthTest.cpp delete mode 100644 tools/CMakeLists.txt create mode 100644 tools/faodel-cli/CMakeLists.txt create mode 100644 tools/faodel-cli/build_info.cpp create mode 100644 tools/faodel-cli/config_info.cpp create mode 100644 tools/faodel-cli/dirman_server.cpp create mode 100644 tools/faodel-cli/faodel_cli.cpp create mode 100644 tools/faodel-cli/faodel_cli.hh create mode 100644 tools/faodel-cli/kelpie_client.cpp create mode 100644 tools/faodel-cli/kelpie_server.cpp create mode 100644 tools/faodel-cli/resource.cpp create mode 100644 tools/faodel-cli/whookie_client.cpp delete mode 100644 tools/faodel-info/CMakeLists.txt delete mode 100644 tools/faodel-info/cmake_info.cpp delete mode 100644 tools/faodel-info/faodel_info.cpp delete mode 100644 tools/whookie/CMakeLists.txt delete mode 100644 tools/whookie/whookie.cpp create mode 100644 tpl/cereal/.gitignore create mode 100644 tpl/cereal/.travis.yml create mode 100644 tpl/cereal/CMakeLists.txt create mode 100644 tpl/cereal/LICENSE create mode 100644 tpl/cereal/README.md create mode 100644 tpl/cereal/appveyor.yml create mode 100644 tpl/cereal/doc/CMakeLists.txt create mode 100644 tpl/cereal/doc/DoxygenLayout.xml create mode 100644 tpl/cereal/doc/doxygen.in create mode 100644 tpl/cereal/doc/footer.html create mode 100644 tpl/cereal/doc/mainpage.dox create mode 100644 tpl/cereal/include/cereal/access.hpp create mode 100644 tpl/cereal/include/cereal/archives/adapters.hpp create mode 100644 tpl/cereal/include/cereal/archives/binary.hpp create mode 100644 tpl/cereal/include/cereal/archives/json.hpp create mode 100644 tpl/cereal/include/cereal/archives/portable_binary.hpp create mode 100644 tpl/cereal/include/cereal/archives/xml.hpp create mode 100644 tpl/cereal/include/cereal/cereal.hpp create mode 100644 tpl/cereal/include/cereal/details/helpers.hpp create mode 100644 tpl/cereal/include/cereal/details/polymorphic_impl.hpp create mode 100644 tpl/cereal/include/cereal/details/polymorphic_impl_fwd.hpp create mode 100644 tpl/cereal/include/cereal/details/static_object.hpp create mode 100644 tpl/cereal/include/cereal/details/traits.hpp create mode 100644 tpl/cereal/include/cereal/details/util.hpp create mode 100644 tpl/cereal/include/cereal/external/base64.hpp create mode 100644 tpl/cereal/include/cereal/external/rapidjson/allocators.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/document.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/encodedstream.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/encodings.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/error/en.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/error/error.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/filereadstream.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/filewritestream.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/fwd.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/biginteger.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/diyfp.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/dtoa.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/ieee754.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/itoa.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/meta.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/pow10.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/regex.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/stack.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/strfunc.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/strtod.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/internal/swap.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/istreamwrapper.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/memorybuffer.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/memorystream.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/msinttypes/inttypes.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/msinttypes/stdint.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/ostreamwrapper.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/pointer.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/prettywriter.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/rapidjson.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/reader.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/schema.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/stream.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/stringbuffer.h create mode 100644 tpl/cereal/include/cereal/external/rapidjson/writer.h create mode 100644 tpl/cereal/include/cereal/external/rapidxml/license.txt create mode 100644 tpl/cereal/include/cereal/external/rapidxml/manual.html create mode 100644 tpl/cereal/include/cereal/external/rapidxml/rapidxml.hpp create mode 100644 tpl/cereal/include/cereal/external/rapidxml/rapidxml_iterators.hpp create mode 100644 tpl/cereal/include/cereal/external/rapidxml/rapidxml_print.hpp create mode 100644 tpl/cereal/include/cereal/external/rapidxml/rapidxml_utils.hpp create mode 100644 tpl/cereal/include/cereal/macros.hpp create mode 100644 tpl/cereal/include/cereal/types/array.hpp create mode 100644 tpl/cereal/include/cereal/types/base_class.hpp create mode 100644 tpl/cereal/include/cereal/types/bitset.hpp create mode 100644 tpl/cereal/include/cereal/types/boost_variant.hpp create mode 100644 tpl/cereal/include/cereal/types/chrono.hpp create mode 100644 tpl/cereal/include/cereal/types/common.hpp create mode 100644 tpl/cereal/include/cereal/types/complex.hpp create mode 100644 tpl/cereal/include/cereal/types/concepts/pair_associative_container.hpp create mode 100644 tpl/cereal/include/cereal/types/deque.hpp create mode 100644 tpl/cereal/include/cereal/types/forward_list.hpp create mode 100644 tpl/cereal/include/cereal/types/functional.hpp create mode 100644 tpl/cereal/include/cereal/types/list.hpp create mode 100644 tpl/cereal/include/cereal/types/map.hpp create mode 100644 tpl/cereal/include/cereal/types/memory.hpp create mode 100644 tpl/cereal/include/cereal/types/polymorphic.hpp create mode 100644 tpl/cereal/include/cereal/types/queue.hpp create mode 100644 tpl/cereal/include/cereal/types/set.hpp create mode 100644 tpl/cereal/include/cereal/types/stack.hpp create mode 100644 tpl/cereal/include/cereal/types/string.hpp create mode 100644 tpl/cereal/include/cereal/types/tuple.hpp create mode 100644 tpl/cereal/include/cereal/types/unordered_map.hpp create mode 100644 tpl/cereal/include/cereal/types/unordered_set.hpp create mode 100644 tpl/cereal/include/cereal/types/utility.hpp create mode 100644 tpl/cereal/include/cereal/types/valarray.hpp create mode 100644 tpl/cereal/include/cereal/types/vector.hpp create mode 100644 tpl/cereal/sandbox/CMakeLists.txt create mode 100644 tpl/cereal/sandbox/performance.cpp create mode 100644 tpl/cereal/sandbox/sandbox.cpp create mode 100644 tpl/cereal/sandbox/sandbox_json.cpp create mode 100644 tpl/cereal/sandbox/sandbox_rtti.cpp create mode 100644 tpl/cereal/sandbox/sandbox_shared_lib/CMakeLists.txt create mode 100755 tpl/cereal/sandbox/sandbox_shared_lib/base.cpp create mode 100755 tpl/cereal/sandbox/sandbox_shared_lib/base.hpp create mode 100755 tpl/cereal/sandbox/sandbox_shared_lib/derived.cpp create mode 100755 tpl/cereal/sandbox/sandbox_shared_lib/derived.hpp create mode 100644 tpl/cereal/sandbox/sandbox_vs.cpp create mode 100755 tpl/cereal/scripts/add_rapidjson_prefix.sh create mode 100644 tpl/cereal/scripts/appveyor.bat create mode 100755 tpl/cereal/scripts/renameincludes.sh create mode 100755 tpl/cereal/scripts/updatecoverage.sh create mode 100755 tpl/cereal/scripts/updatedoc.in create mode 100644 tpl/cereal/unittests/CMakeLists.txt create mode 100644 tpl/cereal/unittests/array.cpp create mode 100644 tpl/cereal/unittests/array.hpp create mode 100644 tpl/cereal/unittests/basic_string.cpp create mode 100644 tpl/cereal/unittests/basic_string.hpp create mode 100644 tpl/cereal/unittests/bitset.cpp create mode 100644 tpl/cereal/unittests/bitset.hpp create mode 100644 tpl/cereal/unittests/boost_variant.cpp create mode 100644 tpl/cereal/unittests/boost_variant.hpp create mode 100644 tpl/cereal/unittests/chrono.cpp create mode 100644 tpl/cereal/unittests/chrono.hpp create mode 100644 tpl/cereal/unittests/cmake-config-module.cmake create mode 100644 tpl/cereal/unittests/common.hpp create mode 100644 tpl/cereal/unittests/complex.cpp create mode 100644 tpl/cereal/unittests/complex.hpp create mode 100644 tpl/cereal/unittests/deque.cpp create mode 100644 tpl/cereal/unittests/deque.hpp create mode 100644 tpl/cereal/unittests/doctest.h create mode 100644 tpl/cereal/unittests/forward_list.cpp create mode 100644 tpl/cereal/unittests/forward_list.hpp create mode 100644 tpl/cereal/unittests/list.cpp create mode 100644 tpl/cereal/unittests/list.hpp create mode 100644 tpl/cereal/unittests/load_construct.cpp create mode 100644 tpl/cereal/unittests/load_construct.hpp create mode 100644 tpl/cereal/unittests/map.cpp create mode 100644 tpl/cereal/unittests/map.hpp create mode 100644 tpl/cereal/unittests/memory.cpp create mode 100644 tpl/cereal/unittests/memory.hpp create mode 100644 tpl/cereal/unittests/memory_cycles.cpp create mode 100644 tpl/cereal/unittests/memory_cycles.hpp create mode 100644 tpl/cereal/unittests/multimap.cpp create mode 100644 tpl/cereal/unittests/multimap.hpp create mode 100644 tpl/cereal/unittests/multiset.cpp create mode 100644 tpl/cereal/unittests/multiset.hpp create mode 100644 tpl/cereal/unittests/pair.cpp create mode 100644 tpl/cereal/unittests/pair.hpp create mode 100644 tpl/cereal/unittests/pod.cpp create mode 100644 tpl/cereal/unittests/pod.hpp create mode 100644 tpl/cereal/unittests/polymorphic.cpp create mode 100644 tpl/cereal/unittests/polymorphic.hpp create mode 100644 tpl/cereal/unittests/portability_test.cpp create mode 100644 tpl/cereal/unittests/portable_binary_archive.cpp create mode 100644 tpl/cereal/unittests/portable_binary_archive.hpp create mode 100644 tpl/cereal/unittests/priority_queue.cpp create mode 100644 tpl/cereal/unittests/priority_queue.hpp create mode 100644 tpl/cereal/unittests/queue.cpp create mode 100644 tpl/cereal/unittests/queue.hpp create mode 100644 tpl/cereal/unittests/run_portability_test.cmake create mode 100755 tpl/cereal/unittests/run_valgrind.sh create mode 100644 tpl/cereal/unittests/set.cpp create mode 100644 tpl/cereal/unittests/set.hpp create mode 100644 tpl/cereal/unittests/stack.cpp create mode 100644 tpl/cereal/unittests/stack.hpp create mode 100644 tpl/cereal/unittests/structs.cpp create mode 100644 tpl/cereal/unittests/structs.hpp create mode 100644 tpl/cereal/unittests/structs_minimal.cpp create mode 100644 tpl/cereal/unittests/structs_minimal.hpp create mode 100644 tpl/cereal/unittests/structs_specialized.cpp create mode 100644 tpl/cereal/unittests/structs_specialized.hpp create mode 100644 tpl/cereal/unittests/tuple.cpp create mode 100644 tpl/cereal/unittests/tuple.hpp create mode 100644 tpl/cereal/unittests/unordered_loads.cpp create mode 100644 tpl/cereal/unittests/unordered_loads.hpp create mode 100644 tpl/cereal/unittests/unordered_map.cpp create mode 100644 tpl/cereal/unittests/unordered_map.hpp create mode 100644 tpl/cereal/unittests/unordered_multimap.cpp create mode 100644 tpl/cereal/unittests/unordered_multimap.hpp create mode 100644 tpl/cereal/unittests/unordered_multiset.cpp create mode 100644 tpl/cereal/unittests/unordered_multiset.hpp create mode 100644 tpl/cereal/unittests/unordered_set.cpp create mode 100644 tpl/cereal/unittests/unordered_set.hpp create mode 100644 tpl/cereal/unittests/user_data_adapters.cpp create mode 100644 tpl/cereal/unittests/user_data_adapters.hpp create mode 100644 tpl/cereal/unittests/valarray.cpp create mode 100644 tpl/cereal/unittests/valarray.hpp create mode 100644 tpl/cereal/unittests/vector.cpp create mode 100644 tpl/cereal/unittests/vector.hpp create mode 100644 tpl/cereal/unittests/versioning.cpp create mode 100644 tpl/cereal/unittests/versioning.hpp create mode 100644 tpl/cereal/vs2013/.gitignore create mode 100644 tpl/cereal/vs2013/sandbox/sandbox.vcxproj create mode 100644 tpl/cereal/vs2013/sandbox/sandbox.vcxproj.filters create mode 100644 tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj create mode 100644 tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj.filters create mode 100644 tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj create mode 100644 tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj.filters create mode 100644 tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj create mode 100644 tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj.filters create mode 100755 tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj create mode 100755 tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj.filters create mode 100755 tpl/cereal/vs2013/unittests/main.cpp create mode 100644 tpl/cereal/vs2013/unittests/unittests.vcxproj create mode 100644 tpl/cereal/vs2013/unittests/unittests.vcxproj.filters create mode 100644 tpl/cereal/vs2013/vs2013.sln delete mode 100644 tpl/gperftools/doc/cpuprofile-fileformat.html delete mode 100644 tpl/gperftools/doc/cpuprofile.html delete mode 100644 tpl/gperftools/doc/designstyle.css delete mode 100644 tpl/gperftools/doc/heap-example1.png delete mode 100644 tpl/gperftools/doc/heap_checker.html delete mode 100644 tpl/gperftools/doc/heapprofile.html delete mode 100644 tpl/gperftools/doc/index.html delete mode 100644 tpl/gperftools/doc/overview.dot delete mode 100644 tpl/gperftools/doc/overview.gif delete mode 100644 tpl/gperftools/doc/pageheap.dot delete mode 100644 tpl/gperftools/doc/pageheap.gif delete mode 100644 tpl/gperftools/doc/pprof-test-big.gif delete mode 100644 tpl/gperftools/doc/pprof-test.gif delete mode 100644 tpl/gperftools/doc/pprof-vsnprintf-big.gif delete mode 100644 tpl/gperftools/doc/pprof-vsnprintf.gif delete mode 100644 tpl/gperftools/doc/pprof.1 delete mode 100644 tpl/gperftools/doc/pprof.see_also delete mode 100644 tpl/gperftools/doc/pprof_remote_servers.html delete mode 100644 tpl/gperftools/doc/spanmap.dot delete mode 100644 tpl/gperftools/doc/spanmap.gif delete mode 100644 tpl/gperftools/doc/t-test1.times.txt delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.1024.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.128.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.131072.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.16384.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.2048.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.256.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.32768.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.4096.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.512.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.64.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.65536.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.8192.bytes.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.1.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.12.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.16.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.2.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.20.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.3.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.4.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.5.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc-opspersec.vs.size.8.threads.png delete mode 100644 tpl/gperftools/doc/tcmalloc.html delete mode 100644 tpl/gperftools/doc/threadheap.dot delete mode 100644 tpl/gperftools/doc/threadheap.gif delete mode 100644 tpl/gperftools/src/config.h create mode 100644 tpl/gperftools/src/fake_stacktrace_scope.cc create mode 100644 tpl/gperftools/src/gperftools/nallocx.h delete mode 100644 tpl/gperftools/src/gperftools/tcmalloc.h mode change 100755 => 100644 tpl/gperftools/src/malloc_hook_mmap_linux.h create mode 100644 tpl/gperftools/src/maybe_emergency_malloc.h mode change 100755 => 100644 tpl/gperftools/src/memory_region_map.cc mode change 100755 => 100644 tpl/gperftools/src/sampler.cc mode change 100755 => 100644 tpl/gperftools/src/sampler.h create mode 100644 tpl/gperftools/src/stacktrace_libgcc-inl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e87e1c..2dcac7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.8.0) project( Faodel LANGUAGES CXX C - VERSION 1.1811.1 + VERSION 1.1906.1 ) @@ -32,6 +32,9 @@ project( Faodel if( POLICY CMP0074 ) cmake_policy( SET CMP0074 NEW ) # find_package() uses PackageName_ROOT vars endif() +if( POLICY CMP0075 ) + cmake_policy( SET CMP0075 OLD ) # check_include_file() uses CMAKE_REQUIRED_LIBRARIES var +endif() # CMake built-ins that we'll use include( FindPkgConfig ) @@ -112,12 +115,14 @@ endfunction() # Options option( BUILD_SHARED_LIBS "Build Faodel as shared libs" OFF ) option( BUILD_TESTS "Faodel testing gtest and ctest" ON ) -option( BUILD_DOCS "Generate documentation using Doxygen" ON ) +option( BUILD_DOCS "Generate documentation using Doxygen" ON ) -option( Faodel_ENABLE_MPI_SUPPORT "Enable use of MPI communication" ON ) -option( Faodel_ENABLE_TCMALLOC "Use tcmalloc from gperftools in Lunasa, potentially other places" ON ) +option( Faodel_ENABLE_CEREAL "Enable use of Cereal for serialization in NNTI (disables XDR)" OFF ) +option( Faodel_ENABLE_MPI_SUPPORT "Enable use of MPI communication" ON ) +option( Faodel_ENABLE_TCMALLOC "Use tcmalloc from gperftools in Lunasa, potentially other places" ON ) option( Faodel_ENABLE_IOM_HDF5 "Build the HDF5-based IOM in Kelpie" OFF ) option( Faodel_ENABLE_IOM_LEVELDB "Build the LevelDB-based IOM in Kelpie" OFF ) +option( Faodel_ENABLE_IOM_CASSANDRA "Build the Cassandra-based IOM in Kelpie" OFF ) set( Faodel_NETWORK_LIBRARY "nnti" CACHE STRING "RDMA Network library to use for low-level communication" ) set_property(CACHE Faodel_NETWORK_LIBRARY PROPERTY STRINGS nnti libfabric) @@ -337,8 +342,7 @@ set( CMAKE_NO_SYSTEM_FROM_IMPORTED ${savevar} ) if( Faodel_ENABLE_TCMALLOC ) # Bail out if we're on an architecture that doesn't support tcmalloc - if( (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" ) OR - (CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le" ) ) + if( CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le" ) message( FATAL_ERROR "The TCMALLOC library included with FAODEL does not currently have " " support for the ${CMAKE_SYSTEM_PROCESSOR} CPU (it has optimizations" " for x86_64). You must set Faodel_ENABLE_TCMALLOC to OFF in order to" @@ -349,11 +353,13 @@ if( Faodel_ENABLE_TCMALLOC ) message( STATUS "Faodel_ENABLE_TCMALLOC is set, gperftools TPL will be built and tcmalloc will be used" ) set( PKGCONFIG_TCMALLOC "-ltcmalloc -lspinlock" ) endif() - + +add_subdirectory( tpl/cereal ) + add_subdirectory( src/sbl ) -add_subdirectory(src/faodel-common) -add_subdirectory( src/webhook ) -add_subdirectory(src/faodel-services) +add_subdirectory( src/faodel-common) +add_subdirectory(src/whookie) +add_subdirectory( src/faodel-services) if( BUILD_NNTI ) add_subdirectory( src/nnti ) endif() @@ -363,7 +369,7 @@ add_subdirectory( src/dirman ) add_subdirectory( src/kelpie ) # Set some useful properties on our targets, now that they're defined -foreach( COMP sbl common webhook services lunasa opbox kelpie ) +foreach( COMP sbl common whookie services lunasa opbox kelpie ) target_include_directories( ${COMP} INTERFACE @@ -395,9 +401,8 @@ if (Faodel_NETWORK_LIBRARY STREQUAL "libfabric") endif() # Build tools -add_subdirectory( tools/faodel-info ) +add_subdirectory( tools/faodel-cli ) add_subdirectory( tools/kelpie-server ) -add_subdirectory( tools/whookie ) # Do the tests @@ -407,7 +412,7 @@ if( BUILD_TESTS ) add_subdirectory( tests/sbl ) add_subdirectory( tests/common ) - add_subdirectory( tests/webhook ) + add_subdirectory( tests/whookie ) add_subdirectory( tests/services ) if( BUILD_NNTI ) add_subdirectory( tests/nnti ) @@ -605,6 +610,15 @@ else() message( STATUS " Not building the MPI Transport" ) endif() endif() +if( ${NNTI_USE_XDR} ) + message( STATUS " Using XDR for serialization" ) +else() + if( ${NNTI_USE_CEREAL} ) + message( STATUS " Using Cereal for serialization" ) + else() + message( STATUS " ERROR - Couldn't find a serialization library" ) + endif() +endif() message( STATUS "" ) message( STATUS "Opbox Config:" ) message( STATUS " Network Library: ${Faodel_NETWORK_LIBRARY}" ) diff --git a/INSTALL.md b/INSTALL.md index 7e87214..7ea0f79 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -139,8 +139,8 @@ what happened in the test. Common problems include: export OMPI_MCA_rmaps_base_oversubscribe=1 -- Check your webhook.interfaces list: Compute nodes often have several - network interfaces and webhook often guesses wrong. Log into a compute +- Check your whookie.interfaces list: Compute nodes often have several + network interfaces and whookie often guesses wrong. Log into a compute node, do "ifconfig" or "ip addr" to find a live network interface, and set it in your config. @@ -155,15 +155,15 @@ what happened in the test. Common problems include: Given that Atomics are not currently used in FAODEL's current libs, it may not be essential for these tests to work in order to use FAODEL. -FAODEL provides the faodel_info tool as a sanity check for your build. This +FAODEL provides the faodel tool as a sanity check for your build. This tools prints out build information and performs basic checks to determine if the libraries will work. You should run this test on your platform's login node, as well as a compute node (some platforms have different hardware). - build/tools/faodel-info/faodel_info + build/tools/faodel-cli/faodel build-info salloc -N 1 - srun build/tools/faodel-info/faodel_info + srun build/tools/faodel-cli/faodel build-info exit @@ -211,7 +211,7 @@ can be found on the FAODEL wiki. Common settings that a user may wish to change include: ``` -webhook.interfaces ib0,eth0 # Change the nic used for webhook +whookie.interfaces ib0,eth0 # Change the nic used for whookie net.transport.name ibverbs # Select net driver when using nnti or libfabric @@ -311,7 +311,20 @@ configure and build on any node and run on any node. +Selecting An Infiniband Network Device +-------------------------------------- +When FAODEL bootstraps the network, it searches for an Infiniband device +with an active port. By default, FAODEL queries the verbs library for a +list of devices and chooses the first one with an active port. If there +are multiple devices or multiple active ports, FAODEL may not choose the +correct device. +In this case, add the following to the configuration file: +``` +net.transport.interfaces ib1,ib0 # Prefer ib1 over ib0 +``` +When `net.transport.interfaces` is defined, FAODEL will search for these +devices (and only these devices) in the order given. Building Third-Party Libraries (TPLs) @@ -383,6 +396,32 @@ the libfabric library in order to run applications. +Data Structure Serialization +============================ + +NNTI data structures can to sent to peers both implicitly (command +messages) and explicitly (buffer references). To support +heterogeneous platforms, these data structures are serialized to a +portable format, sent to the peer and deserialized at the recipient. + +NNTI has historically used XDR for serialization because it is +fast, tight and ubiquitous. In recent releases (eg. Mojave) of +MacOS, XDR is not fully implemented. NNTI detects this condition +during configuration and uses the bundled Cereal library as an +alternative. + +If you prefer Cereal over XDR, you can force the use of Cereal +using the `Faodel_ENABLE_CEREAL` option. + +Note 1: Cereal is a header-only implementation and is only used +internally by NNTI. It does not get installed as part of Faodel. + +Note 2: There is no way to reference an external installation of +Cereal. + + + + Platform-Specific Notes: Installing on Mutrino (Cray XC40) ========================================================== @@ -520,7 +559,7 @@ defined as ipogif0). Thus, you should add the following info to the configuration file specified by `FAODEL_CONFIG`: ``` -webhook.interfaces ipogif0 +whookie.interfaces ipogif0 net.transport.name ugni ``` @@ -554,6 +593,27 @@ find . -name CTestTestfile.cmake | xargs sed -i 's@/opt/cray/elogin/eproxy/2.0.2 FAODEL. +Installing on Astra (ARM-based Mellanox InfiniBand Cluster) +=========================================================== + +Astra is an ARM-based cluster with a Mellanox InfiniBand interconnect. +In general, Faodel operates the same on Astra as on any other +InfiniBand platform. One extra feature of Astra is that it +has full support for Mellanox's On-Demand Paging feature that +allows a process' entire virtual address space to be registered +without pinning (locking) pages in memory. The availability of +ODP is detected during configuration, but it is disable by +default because it is still experimental. To enable it, add the +following to your configuration file. + +``` +net.transport.use_odp true +``` + +This feature is still experimental with no guarantees of +performance or correctness. + + Installing on Kahuna (Generic InfiniBand Cluster) ================================================= @@ -591,7 +651,7 @@ ports for sockets. Thus, you should add the following info to the configuration file specified by `FAODEL_CONFIG`: ``` -webhook.interfaces eth0,ib0 +whookie.interfaces eth0,ib0 net.transport.name ibverbs ``` @@ -625,6 +685,7 @@ Advanced Options | ------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------- | | HDF5_DIR | Path | Location of HDF5 libs. Only used when FAODEL_ENABLE_IOM_HDF5 used | | leveldb_DIR | Path | Location of leveldb libs. Only used when FAODEL_ENABLE_IOM_LEVELDB used | +| Faodel_ENABLE_CEREAL | Boolean | If XDR is not fully implemented (eg. MacOS Mojave), Cereal is used. CMake should autodetect, but this forces Cereal. | | Faodel_NO_ISYSTEM_FLAG | Boolean | Some compilers use "-isystem" to identify system libs instead of "-I". CMake should autodetect, but this can override | | Faodel_OPBOX_NET_NNTI | Boolean | Set to true if Faodel_NETWORK_LIBRARY is nnti | | Faodel_PERFTOOLS_* | - | These variables are used by the tpl/gperftools library. See their documentation for more info | diff --git a/NEWS.md b/NEWS.md index 31f9df7..ff0f029 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,12 +6,36 @@ faodel tools. Releases are named alphabetically and have a 4-digit ID associated with them that designates the year and month of the release. -DIO (1.1811.2) --------------- -- Summary: Fix build problems discovered during Spack integration -- Release Improvements: - - Common Logging failed to build when SBL was selected for output (thanks to @fbudin69500) - - Kelpie failed to build when MPI was disabled (thanks to @fbudin69500) +Excelsior! (1.1906.1) +--------------------- +- Summary: Job-to-Job improvements via new cli tool +- Release Improvements + - New faodel-cli tool for manipulating many things + - Gets build/configure info (replaces faodel-info) + - Start/stop services (dirman, kelpie) + - Define/query/remove dirman resources + - Put/get/list kelpie objects + - New example/kelpie-cli script shows how to use + - Support for ARM platform + - NNTI adds On-Demand Paging capability + - NNTI adds Cereal as alternative for serialization + - NNTI has better detection and selection of IB devices + - Fixes + - SBL could segfault due to Boost if exit without calling finish + - FAODEL couldn't be included in a larger project's cmake + - LDO had a race condition in destructor +- Significant User-Visible Changes: + - faodel-info and whookie tools replaced by faodel cli tool + - Dirman's DirInfo "children" renamed to "members" + - Faodel now has a package in the Spack develop branch +- Known Issues + - FAODEL's libfabric transport is still experimental. It does not fully + implement Atomics or Long Sends. While Kelpie does not require + these operations, other OpBox-based applications may break + without this support. + - On Cray machines with the Aries interconnect, FAODEL can be overwhelmed + by a sustained stream of sends larger than the MTU. To avoid this problem, + the sender should limit itself to bursts of 32 long sends at a time. DIO (1.1811.1) -------------- @@ -24,10 +48,10 @@ DIO (1.1811.1) - Lunasa has templated containers for storing a bundle of items in an LDO - GenericSequentialDataBundle : When data is accessed in order - GenericRandomDataBundle : When data is accessed out of order - - Users can now define webhooks for rendering specific DataObject types + - Users can now define whookies for rendering specific DataObject types - Bootstrap Start/Stops are much more robust - General build fixes for use with EMPIRE -- Significant User Visible Changes: +- Significant User-Visible Changes: - Common and services directories renamed to faodel-common and faodel-services - Kelpie pools now have a "behavior" that controls how data is copied - Some components/tools changed directories. Check your includes @@ -42,7 +66,7 @@ DIO (1.1811.1) these operations, other OpBox-based applications may break without this support. - On Cray machines with the Aries interconnect, FAODEL can be overwhelmed - by a sustrained stream of sends larger than the MTU. To avoid this problem, + by a sustained stream of sends larger than the MTU. To avoid this problem, the sender should limit itself to bursts of 32 long sends at a time. - This version does not have support for ARM8 or POWER cpus. @@ -98,7 +122,7 @@ Amigo (0.1707.1) ---------------- - Summary: First packaged release, for friendly users - Release Improvements: - - Stable versions of SBL, Gutties, WebHook, NNTI, Lunasa, and Opbox + - Stable versions of SBL, Gutties, Whookie, NNTI, Lunasa, and Opbox - Experimental version of Kelpie (nonet) - Switched to Graith CMake modules - Initial doxygen and readme documentation diff --git a/README.md b/README.md index b775f35..9757556 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ FAODEL is composed of multiple libraries: layer for high-performance networks. It provides application with the ability to send messages and coordinate RDMA transfers via registered memory. -- [WebHook](src/webhook/README_WebHook.md): WebHook is a network service for +- [Whookie](src/whookie/README_Whookie.md): Whookie is a network service for FAODEL nodes that enables users and applications to query and change the state of a node via an HTTP connection. - [Services](src/faodel-services/README_Services.md): Basic services that make it @@ -81,8 +81,9 @@ The following helped contribute ideas and provided feedback for the project: This release includes third-party software that contains its own licensing and copyright info: +- cereal (in tpl/cereal) - gperftools (in tpl/gperftools) -- Boost ASIO examples (in src/webhook/server) +- Boost ASIO examples (in src/whookie/server) Copyright ========= diff --git a/cmake/FaodelTPLs.cmake b/cmake/FaodelTPLs.cmake index 4852990..5f621ec 100644 --- a/cmake/FaodelTPLs.cmake +++ b/cmake/FaodelTPLs.cmake @@ -96,6 +96,33 @@ endif() +####################### +## Cassandra IOM +####################### + +if( Faodel_ENABLE_IOM_CASSANDRA ) + + # The DataStax Cassandra C/C++ driver generates a pkgconfig module, so let's try to find that. + set( PKG_CONFIG_USE_CMAKE_PREFIX_PATH 1 ) + + pkg_search_module( cassandra_pc cassandra REQUIRED ) + + if( cassandra_pc_FOUND ) + set( CASSANDRA_FOUND TRUE ) + set( FAODEL_HAVE_CASSANDRA TRUE ) + + add_library( Faodel::Cassandra INTERFACE IMPORTED ) + target_include_directories( Faodel::Cassandra INTERFACE ${cassandra_pc_INCLUDE_DIRS} ) + target_link_libraries( Faodel::Cassandra INTERFACE ${cassandra_pc_LDFLAGS} ) + target_compile_definitions( Faodel::Cassandra INTERFACE ${cassandra_pc_CFLAGS_OTHER} ) + + message( STATUS "Will build Cassandra IOM, Faodel_ENABLE_IOM_CASSANDRA set and Cassandra driver found" ) + else() + message( STATUS "Cannot build Cassandra IOM as requested, Cassandra driver not found. Set CMAKE_PREFIX_PATH" ) + endif() + +endif() + ######################## ## MPI ######################## @@ -198,7 +225,7 @@ if (Faodel_NETWORK_LIBRARY STREQUAL "libfabric") endif() LIST( APPEND FaodelNetlib_TARGETS Libfabric ) - set( PKGCONFIG_REQUIRES "libfabric" ) + set( PKGCONFIG_REQUIRES "${PKGCONFIG_REQUIRES} libfabric" ) message( STATUS "Found Libfabric, target appended to FaodelNetlib_TARGETS" ) endif() diff --git a/cmake/faodel.pc.in b/cmake/faodel.pc.in index fbc7004..ba8f8c8 100644 --- a/cmake/faodel.pc.in +++ b/cmake/faodel.pc.in @@ -27,7 +27,7 @@ Name: Faodel Description: Collection of libraries for managing data in distributed memory/storage Version: @PROJECT_VERSION@ Requires: @PKGCONFIG_REQUIRES@ -Libs: -L${libdir} -lkelpie -ldirman -lopbox -llunasa @PKGCONFIG_NNTI@ -lfaodel-services -lwebhook -lfaodel-common -lsbl @PKGCONFIG_TCMALLOC@ +Libs: -L${libdir} -lkelpie -ldirman -lopbox -llunasa @PKGCONFIG_NNTI@ -lfaodel-services -lwhookie -lfaodel-common -lsbl @PKGCONFIG_TCMALLOC@ Libs.private: @PKGCONFIG_LIBS_PRIVATE@ Cflags: @PKGCONFIG_CFLAGS@ -I${includedir} -I${includedir}/faodel @PKGCONFIG_CFLAGS@ diff --git a/docs/ConfigurationCookbook.md b/docs/ConfigurationCookbook.md index 91d41f4..e8667b6 100644 --- a/docs/ConfigurationCookbook.md +++ b/docs/ConfigurationCookbook.md @@ -5,7 +5,7 @@ Many FAODEL components support the debug flag. In particular: ``` bootstrap.debug true -webhook.debug true +whookie.debug true lunasa.debug true opbox.debug true mpisyncstart.debug true @@ -14,7 +14,7 @@ kelpie.debug true ``` ## Delaying Exit -It can be beneficial to use webhook as a means of querying the state of +It can be beneficial to use whookie as a means of querying the state of a running (or halted) simulation. You can either halt or delay shutdown via bootstrap options. Also, you can display a large "ok" message on a successful exit to make it more obvious how the sim ended. diff --git a/docs/Doxyfile b/docs/Doxyfile index 18a8d03..6b004f5 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -783,7 +783,7 @@ WARN_LOGFILE = INPUT = src/sbl INPUT += src/faodel-common INPUT += src/dirman -INPUT += src/webhook +INPUT += src/whookie INPUT += src/nnti INPUT += src/lunasa INPUT += src/opbox @@ -797,7 +797,7 @@ INPUT += src/kelpie/README_Kelpie.md INPUT += src/opbox/README_OpBox.md INPUT += src/lunasa/README_Lunasa.md INPUT += src/nnti/README_NNTI.md -INPUT += src/webhook/README_WebHook.md +INPUT += src/whookie/README_Whookie.md INPUT += src/dirman/README_DirMan.md INPUT += src/faodel-services/README_Services.md INPUT += src/faodel-common/README_Common.md @@ -842,7 +842,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = src/webhook/server/boost +EXCLUDE = src/whookie/server/boost # 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 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3306d53..ab7dab7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.8.0) project(faodel_examples LANGUAGES CXX C) +# Policy assertions +if( POLICY CMP0074 ) + cmake_policy( SET CMP0074 NEW ) # find_package() uses PackageName_ROOT vars +endif() # By default, cmake looks in your ~/.cmake directory for information # about packages. While that should be fine for most users, we disable @@ -15,7 +19,7 @@ find_package( Faodel CONFIG REQUIRED ) add_subdirectory( common ) -add_subdirectory( webhook ) +add_subdirectory( whookie ) if( Faodel_ENABLE_MPI_SUPPORT ) # These examples have some MPI requirements, so don't build if no mpi diff --git a/examples/common/data_types/nodeid_example.cpp b/examples/common/data_types/nodeid_example.cpp index 148e938..f58824b 100644 --- a/examples/common/data_types/nodeid_example.cpp +++ b/examples/common/data_types/nodeid_example.cpp @@ -17,12 +17,12 @@ void example_nodeid() { //that are running in the system. The nodeid is a 64b value that //any rank in the system can communicate with. - //The Webhook service is responsible for managing a communication + //The Whookie service is responsible for managing a communication //socket for each rank. This socket is a regular tcp/ip socket //(though most transports allow you to run on top of the native //hpc interconnect). A rank's nodeid_t can be discovered by - //querying webhook: - // webhook::Server::GetMyID(); + //querying whookie: + // whookie::Server::GetMyID(); //You can manually specify the ip/port for a node. This is mostly diff --git a/examples/common/data_types/resourceurl_example.cpp b/examples/common/data_types/resourceurl_example.cpp index f3e39d8..a9a6e55 100644 --- a/examples/common/data_types/resourceurl_example.cpp +++ b/examples/common/data_types/resourceurl_example.cpp @@ -51,7 +51,7 @@ void example_resourceurl() { ResourceURL u1(url_string); cout << "URL1 "< /dev/null 2>&1 +if [ $? -eq 0 ] ; then + LAUNCHER=mpirun +else + type srun > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + LAUNCHER=srun + else + echo "$0: Couldn't find mpirun or srun. Exiting." + exit + fi +fi + +NO_PAUSE=$# +YELLOW='\033[0;33m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +GreenText() { + echo -e ${GREEN} +} +YellowText() { + echo -e ${YELLOW} +} +PlainText() { + echo -e ${NC} +} +PressEnter() { + if [ $NO_PAUSE -eq 0 ]; then + GreenText + read -p "Press enter to continue$" + fi + YellowText +} + + + +YellowText +cat < #include "kelpie/Kelpie.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" //The configuration used in this example std::string default_config_string = R"EOF( @@ -26,7 +26,7 @@ kelpie.type nonet # Uncomment these options to get debug info for each component #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true @@ -38,23 +38,23 @@ using namespace std; int main(){ char tmp; - cout <<"Kelpie using-webhook example\n"; + cout <<"Kelpie using-whookie example\n"; //Startup all the registered services faodel::Configuration config(default_config_string); faodel::bootstrap::Start(config, kelpie::bootstrap); //Retrieve info about our built-in webserver - string webhook_url = webhook::Server::GetNodeID().GetHttpLink(); + string whookie_url = whookie::Server::GetNodeID().GetHttpLink(); cout <<"Kelpie is now up and running.\n" - <<" Go to a browser and look at: "< #include @@ -238,8 +238,8 @@ int main(int argc, char **argv){ * The value of allows us to tell NNTI which process we want to connect to. * We get our ID here so that we can send it to our partner process. */ - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + nodeid = whookie::Server::GetNodeID(); /* Initialize and configure our NNTI transport */ transport = nnti::transports::factory::get_instance(config); diff --git a/examples/lunasa/simple_user_ldo_put/simple_user_ldo_put_example.cpp b/examples/lunasa/simple_user_ldo_put/simple_user_ldo_put_example.cpp index 7b4f869..f3efe4f 100644 --- a/examples/lunasa/simple_user_ldo_put/simple_user_ldo_put_example.cpp +++ b/examples/lunasa/simple_user_ldo_put/simple_user_ldo_put_example.cpp @@ -19,7 +19,7 @@ #include "nnti/nnti_callback.hpp" #include "nnti/nnti_wr.hpp" #include "nnti/transport_factory.hpp" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include #include @@ -541,8 +541,8 @@ int main(int argc, char **argv){ * The value of allows us to tell NNTI which process we want to connect to. * We get our ID here so that we can send it to our partner process. */ - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + nodeid = whookie::Server::GetNodeID(); config.Set("nnti.bind.address", nodeid.GetIP()); config.Set("nnti.listen.port", nodeid.GetPort()); diff --git a/examples/nnti/cpp/PingPong.cpp b/examples/nnti/cpp/PingPong.cpp index 99f6d88..3e56fc8 100644 --- a/examples/nnti/cpp/PingPong.cpp +++ b/examples/nnti/cpp/PingPong.cpp @@ -20,7 +20,7 @@ #include "faodel-common/Configuration.hh" #include "faodel-common/Bootstrap.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "nnti/nnti_logger.hpp" @@ -143,7 +143,7 @@ int main(int argc, char *argv[]) faodel::Configuration config(default_config_string); config.AppendFromReferences(); - faodel::bootstrap::Start(config, webhook::bootstrap); + faodel::bootstrap::Start(config, whookie::bootstrap); t = nnti::transports::factory::get_instance(config); @@ -173,7 +173,7 @@ int main(int argc, char *argv[]) my_pingpong_buf, peer_pingpong_buf); - webhook::Server::registerHook("/pingpong/shutdown", [ppc] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/pingpong/shutdown", [ppc] (const std::map &args, std::stringstream &results){ shutdown_cb(ppc); }); diff --git a/examples/opbox/CMakeLists.txt b/examples/opbox/CMakeLists.txt index fbac157..37b83d7 100644 --- a/examples/opbox/CMakeLists.txt +++ b/examples/opbox/CMakeLists.txt @@ -10,8 +10,10 @@ add_library(mpi_support support/SimpleDataStore.hh support/SimpleDataStore.cpp ) -set_target_properties(mpi_support PROPERTIES LINKER_LANGUAGE CXX ) -target_link_libraries( mpi_support Faodel::common ) +set_target_properties( mpi_support PROPERTIES LINKER_LANGUAGE CXX ) +target_link_libraries( mpi_support + MPI::MPI_CXX + Faodel::common ) set(EXAMPLES_LIBS mpi_support diff --git a/examples/opbox/advanced/dirty_use/lingering_data.cpp b/examples/opbox/advanced/dirty_use/lingering_data.cpp index 834c55a..595c9cf 100644 --- a/examples/opbox/advanced/dirty_use/lingering_data.cpp +++ b/examples/opbox/advanced/dirty_use/lingering_data.cpp @@ -42,14 +42,14 @@ Globals G; std::string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server net.transport.name mpi -master.webhook.port 7777 -server.webhook.port 1991 +master.whookie.port 7777 +server.whookie.port 1991 dirman.type centralized dirman.root_role master #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true lunasa.debug true diff --git a/examples/opbox/advanced/job2job/README.md b/examples/opbox/advanced/job2job/README.md index 5aefc34..56b7ca9 100644 --- a/examples/opbox/advanced/job2job/README.md +++ b/examples/opbox/advanced/job2job/README.md @@ -25,7 +25,7 @@ Building the entire FAODEL stack with these options can't hurt. ## Prerequisites -This test requires the entire FAODEL stack: sbl, common, webhook, nnti, +This test requires the entire FAODEL stack: sbl, common, whookie, nnti, lunasa, and opbox. diff --git a/examples/opbox/advanced/job2job/job2job-client.cpp b/examples/opbox/advanced/job2job/job2job-client.cpp index 88835b9..a5be14a 100644 --- a/examples/opbox/advanced/job2job/job2job-client.cpp +++ b/examples/opbox/advanced/job2job/job2job-client.cpp @@ -21,7 +21,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking diff --git a/examples/opbox/advanced/job2job/job2job-server.cpp b/examples/opbox/advanced/job2job/job2job-server.cpp index a635331..a2343cf 100644 --- a/examples/opbox/advanced/job2job/job2job-server.cpp +++ b/examples/opbox/advanced/job2job/job2job-server.cpp @@ -21,7 +21,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking diff --git a/examples/opbox/advanced/job2job/kahuna.ibverbs.conf b/examples/opbox/advanced/job2job/kahuna.ibverbs.conf index 0499bfd..5c92d99 100644 --- a/examples/opbox/advanced/job2job/kahuna.ibverbs.conf +++ b/examples/opbox/advanced/job2job/kahuna.ibverbs.conf @@ -2,6 +2,6 @@ nnti.logger.severity debug nnti.transport.name ibverbs nnti.transport.protocol ib nnti.test.server_protocol ib -webhook.interfaces eth2,lo +whookie.interfaces eth2,lo diff --git a/examples/opbox/advanced/job2job/kahuna.mpi.conf b/examples/opbox/advanced/job2job/kahuna.mpi.conf index 914b4ef..57c71f8 100644 --- a/examples/opbox/advanced/job2job/kahuna.mpi.conf +++ b/examples/opbox/advanced/job2job/kahuna.mpi.conf @@ -2,5 +2,5 @@ nnti.logger.severity debug nnti.transport.name mpi nnti.transport.protocol mpi nnti.test.server_protocol mpi -webhook.interfaces eth,lo +whookie.interfaces eth,lo diff --git a/examples/opbox/advanced/job2job/mutrino.mpi.conf b/examples/opbox/advanced/job2job/mutrino.mpi.conf index 7bf8a27..6497170 100644 --- a/examples/opbox/advanced/job2job/mutrino.mpi.conf +++ b/examples/opbox/advanced/job2job/mutrino.mpi.conf @@ -2,5 +2,5 @@ nnti.logger.severity debug nnti.transport.name mpi nnti.transport.protocol mpi nnti.test.server_protocol mpi -webhook.interfaces ipogif0,eth,lo +whookie.interfaces ipogif0,eth,lo diff --git a/examples/opbox/advanced/job2job/mutrino.ugni.conf b/examples/opbox/advanced/job2job/mutrino.ugni.conf index b52b4a0..8279354 100644 --- a/examples/opbox/advanced/job2job/mutrino.ugni.conf +++ b/examples/opbox/advanced/job2job/mutrino.ugni.conf @@ -2,6 +2,6 @@ nnti.logger.severity info nnti.transport.name ugni nnti.transport.protocol ugni nnti.test.server_protocol ugni -webhook.interfaces ipogif0,eth,lo +whookie.interfaces ipogif0,eth,lo diff --git a/examples/opbox/basic/message_packing/message_packing_example.cpp b/examples/opbox/basic/message_packing/message_packing_example.cpp index c36f9e8..9583725 100644 --- a/examples/opbox/basic/message_packing/message_packing_example.cpp +++ b/examples/opbox/basic/message_packing/message_packing_example.cpp @@ -31,8 +31,8 @@ net.transport.name mpi # # note: node_role is set by Globals based on rank. # -master.webhook.port 7777 -server.webhook.port 1992 +master.whookie.port 7777 +server.whookie.port 1992 # Select the type of dirman to use. Currently we only have centralized, which # just sticks all the directory info on one node (called root). We use roles @@ -42,7 +42,7 @@ dirman.root_role master # Turn these on if you want to see more debug messages #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #dirman.cache.others.debug true diff --git a/examples/opbox/basic/my_simple_ping/my_simple_ping.cpp b/examples/opbox/basic/my_simple_ping/my_simple_ping.cpp index b40c4f1..750bc3d 100644 --- a/examples/opbox/basic/my_simple_ping/my_simple_ping.cpp +++ b/examples/opbox/basic/my_simple_ping/my_simple_ping.cpp @@ -18,7 +18,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking @@ -27,8 +27,8 @@ std::string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server net.transport.name mpi -master.webhook.port 7777 -server.webhook.port 1992 +master.whookie.port 7777 +server.whookie.port 1992 dirman.type centralized dirman.root_role master @@ -37,7 +37,7 @@ dirman.root_role master #target.dirman.write_to_file ./dirman.txt #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true diff --git a/examples/opbox/basic/rdma_ping/rdma_ping.cpp b/examples/opbox/basic/rdma_ping/rdma_ping.cpp index ba4af35..717d872 100644 --- a/examples/opbox/basic/rdma_ping/rdma_ping.cpp +++ b/examples/opbox/basic/rdma_ping/rdma_ping.cpp @@ -19,7 +19,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking @@ -28,8 +28,8 @@ std::string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server net.transport.name mpi -master.webhook.port 7777 -server.webhook.port 1992 +master.whookie.port 7777 +server.whookie.port 1992 dirman.type centralized dirman.root_role master @@ -38,7 +38,7 @@ dirman.root_role master #target.dirman.write_to_file ./dirman.txt #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true diff --git a/examples/opbox/benchmarks/CMakeLists.txt b/examples/opbox/benchmarks/CMakeLists.txt index 378e6b1..427b85a 100644 --- a/examples/opbox/benchmarks/CMakeLists.txt +++ b/examples/opbox/benchmarks/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory( atomics ) +add_subdirectory( connect ) add_subdirectory( msgplusrdma ) add_subdirectory( rdma ) add_subdirectory( short_message ) diff --git a/examples/opbox/benchmarks/atomics/atomics.cpp b/examples/opbox/benchmarks/atomics/atomics.cpp index 3513b22..c169312 100644 --- a/examples/opbox/benchmarks/atomics/atomics.cpp +++ b/examples/opbox/benchmarks/atomics/atomics.cpp @@ -22,7 +22,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking @@ -30,8 +30,8 @@ Globals G; std::string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server -master.webhook.port 7777 -server.webhook.port 1992 +master.whookie.port 7777 +server.whookie.port 1992 dirman.type centralized dirman.root_role master @@ -40,7 +40,7 @@ dirman.root_role master #target.dirman.write_to_file ./dirman.txt #bootstrap.debug true -#webhook.debug true +#whookie.debug true opbox.debug true dirman.debug true diff --git a/examples/opbox/benchmarks/connect/CMakeLists.txt b/examples/opbox/benchmarks/connect/CMakeLists.txt new file mode 100644 index 0000000..b210fb3 --- /dev/null +++ b/examples/opbox/benchmarks/connect/CMakeLists.txt @@ -0,0 +1,16 @@ +set(PROJECT_NAME connect) + +set(SOURCES + connect.cpp +) + + +add_executable(${PROJECT_NAME} ${SOURCES}) +set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX ) + +target_link_libraries(${PROJECT_NAME} ${EXAMPLES_LIBS}) + +install(TARGETS ${PROJECT_NAME} + EXPORT faodelExampleTargets + RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin +) diff --git a/examples/opbox/benchmarks/connect/connect.cpp b/examples/opbox/benchmarks/connect/connect.cpp new file mode 100644 index 0000000..8faabd9 --- /dev/null +++ b/examples/opbox/benchmarks/connect/connect.cpp @@ -0,0 +1,226 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include + +#include + +#include + +#include "faodel-common/Common.hh" +#include "opbox/OpBox.hh" + +using namespace std; +using namespace faodel; + + +Configuration config; +int mpi_rank, mpi_size; +int root_rank; + +uint32_t connect_count; + +struct connect_timer { + std::chrono::high_resolution_clock::time_point start; + std::chrono::high_resolution_clock::time_point end; +}; +std::vector *timers; + +void +connect_1to1(uint32_t count) +{ + int rc; + + //std::cout << "Our MPI rank is " << mpi_rank << std::endl; + faodel::nodeid_t myid = opbox::GetMyID(); + //std::cout << "Our nodeid is " << myid.GetHex() << std::endl; + + std::vector gather_result(mpi_size); + MPI_Allgather( + &myid, + sizeof(faodel::nodeid_t), + MPI_CHAR, + gather_result.data(), + sizeof(faodel::nodeid_t), + MPI_CHAR, + MPI_COMM_WORLD); + + if (mpi_rank == root_rank) { + } else if (mpi_rank == root_rank+1) { + opbox::net::peer_t *peer; + faodel::nodeid_t root_nodeid = gather_result[root_rank]; + for(int i = 0; iat(i).start = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Connect(&peer, root_nodeid); + timers->at(i).end = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Disconnect(root_nodeid); + } + for(int i = 0; i( + timers->at(i).end.time_since_epoch()).count(); + uint64_t start = std::chrono::duration_cast( + timers->at(i).start.time_since_epoch()).count(); + uint64_t per_connect_us = end - start; + fprintf(stdout, "1-to-1 Connect() chrono - %6lu %6lu %6lu %6luus\n", + i, start, end, per_connect_us); + } + uint64_t total_us = std::chrono::duration_cast( + timers->at(count - 1).end - timers->at(0).start).count(); + float total_sec = (float) total_us/1000000.0; + fprintf(stdout, "1-to-1 Connect() chrono - total - %6lu %6luus %6.6fs\n", + count, total_us, total_sec); + } else { + } + MPI_Barrier(MPI_COMM_WORLD); +} + +void +connect_nto1(uint32_t count) +{ + int rc; + + //std::cout << "Our MPI rank is " << mpi_rank << std::endl; + faodel::nodeid_t myid = opbox::GetMyID(); + //std::cout << "Our nodeid is " << myid.GetHex() << std::endl; + + std::vector gather_result(mpi_size); + MPI_Allgather( + &myid, + sizeof(faodel::nodeid_t), + MPI_CHAR, + gather_result.data(), + sizeof(faodel::nodeid_t), + MPI_CHAR, + MPI_COMM_WORLD); + + if(mpi_rank == root_rank) { + } else { + opbox::net::peer_t *peer; + faodel::nodeid_t root_nodeid = gather_result[root_rank]; + for(int i = 0; iat(i).start = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Connect(&peer, root_nodeid); + timers->at(i).end = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Disconnect(root_nodeid); + } + for(int i = 0; i( + timers->at(i).end.time_since_epoch()).count(); + uint64_t start = std::chrono::duration_cast( + timers->at(i).start.time_since_epoch()).count(); + uint64_t per_connect_us = end - start; + fprintf(stdout, "N-to-1 Connect() chrono - %6lu %6lu %6lu %6luus\n", + i, start, end, per_connect_us); + } + uint64_t total_us = std::chrono::duration_cast( + timers->at(count - 1).end - timers->at(0).start).count(); + float total_sec = (float) total_us/1000000.0; + fprintf(stdout, "N-to-1 Connect() chrono - total - %6lu %6luus %6.6fs\n", + count, total_us, total_sec); + } + MPI_Barrier(MPI_COMM_WORLD); +} + +void +connect_1ton(uint32_t count) +{ + int rc; + + //std::cout << "Our MPI rank is " << mpi_rank << std::endl; + faodel::nodeid_t myid = opbox::GetMyID(); + //std::cout << "Our nodeid is " << myid.GetHex() << std::endl; + + std::vector gather_result(mpi_size); + MPI_Allgather( + &myid, + sizeof(faodel::nodeid_t), + MPI_CHAR, + gather_result.data(), + sizeof(faodel::nodeid_t), + MPI_CHAR, + MPI_COMM_WORLD); + + if(mpi_rank == root_rank) { + for(int i=0; iat(i).start = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Connect(&peer, nodeid); + timers->at(i).end = std::chrono::high_resolution_clock::now(); + rc = opbox::net::Disconnect(nodeid); + } + } + for(int i = 0; i( + timers->at(i).end.time_since_epoch()).count(); + uint64_t start = std::chrono::duration_cast( + timers->at(i).start.time_since_epoch()).count(); + uint64_t per_connect_us = end - start; + fprintf(stdout, "1-to-N Connect() chrono - %6lu %6lu %6lu %6luus\n", + i, start, end, per_connect_us); + } + uint64_t total_us = std::chrono::duration_cast( + timers->at(count - 1).end - timers->at(0).start).count(); + float total_sec = (float) total_us/1000000.0; + fprintf(stdout, "1-to-N Connect() chrono - total - %6lu %6luus %6.6fs\n", + count, total_us, total_sec); + } else { + } + MPI_Barrier(MPI_COMM_WORLD); +} + +int main(int argc, char **argv) { + int provided; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("help", "Show this help message") + ("count", boost::program_options::value(), "number of connects to issue") + ; + + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + + if (vm.count("help")) { + cout << desc << "\n"; + return 1; + } + + connect_count = 25; + if (vm.count("count")) { + connect_count = vm["count"].as(); + } + + //This is a simple example of how to launch a small ping-pong communication + cout <<"Connect Benchmark (" << connect_count << ")" << endl; + + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + + bootstrap::Start(Configuration(""), opbox::bootstrap); + + timers = new std::vector(connect_count); + + root_rank=0; + MPI_Barrier(MPI_COMM_WORLD); + + connect_1to1(connect_count); + MPI_Barrier(MPI_COMM_WORLD); + + connect_1ton(connect_count); + MPI_Barrier(MPI_COMM_WORLD); + + connect_nto1(connect_count); + MPI_Barrier(MPI_COMM_WORLD); + + MPI_Barrier(MPI_COMM_WORLD); + bootstrap::Finish(); + + MPI_Finalize(); + return 0; +} diff --git a/examples/opbox/benchmarks/msgplusrdma/msgplusrdma.cpp b/examples/opbox/benchmarks/msgplusrdma/msgplusrdma.cpp index 7fa6418..49ec0ba 100644 --- a/examples/opbox/benchmarks/msgplusrdma/msgplusrdma.cpp +++ b/examples/opbox/benchmarks/msgplusrdma/msgplusrdma.cpp @@ -21,7 +21,7 @@ Globals G; //The FAODEL uses a plain-text configuration string to set //different parameters in the stack. The below string defines what -//port the webhook sever will listen on, the type of directory management +//port the whookie sever will listen on, the type of directory management //service to employ, and whether internal components should spew their //debug information or not. If things don't work, try turning on the debug //info to get a better idea of where things are breaking @@ -29,8 +29,8 @@ Globals G; std::string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server -master.webhook.port 7777 -server.webhook.port 1992 +master.whookie.port 7777 +server.whookie.port 1992 dirman.type centralized dirman.root_role master @@ -39,7 +39,7 @@ dirman.root_role master #target.dirman.write_to_file ./dirman.txt #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true diff --git a/examples/opbox/benchmarks/rdma/OpBenchmarkGet.cpp b/examples/opbox/benchmarks/rdma/OpBenchmarkGet.cpp index 48925ba..d20e63a 100644 --- a/examples/opbox/benchmarks/rdma/OpBenchmarkGet.cpp +++ b/examples/opbox/benchmarks/rdma/OpBenchmarkGet.cpp @@ -18,6 +18,7 @@ const string OpBenchmarkGet::op_name = "OpBenchmarkGet"; OpBenchmarkGet::OpBenchmarkGet(opbox::net::peer_ptr_t dst, uint32_t size, uint32_t count, uint32_t max_inflight) : faodel::LoggingInterface("Opbox", "OpBenchmarkGet"), state(State::start), + warmup_count(10), size(size), count(count), max_inflight(max_inflight), started(0), inflight(0), completed(0), first_burst_sent(false), @@ -30,6 +31,7 @@ OpBenchmarkGet::OpBenchmarkGet(opbox::net::peer_ptr_t dst, uint32_t size, uint32 OpBenchmarkGet::OpBenchmarkGet(op_create_as_target_t t) : faodel::LoggingInterface("Opbox", "OpBenchmarkGet"), state(State::start), + warmup_count(0), size(0), count(0), max_inflight(0), started(0), inflight(0), completed(0), first_burst_sent(false), @@ -154,6 +156,10 @@ WaitingType OpBenchmarkGet::UpdateTarget(OpArgs *args) { 0, //Not expecting a reply incoming_msg->src_mailbox); + for (int i=0;isrc_mailbox); + for (int i=0;i dirman.root_node webhook_nodeid +// dirman.root_node_mpi rank ==> dirman.root_node whookie_nodeid // Often you want to designate a certain rank in a job as the root node // for the dirman service, but you don't know what the nodeid is until // you start. If the mpisyncstart service sees the dirman.root_node_mpi @@ -77,7 +77,7 @@ int main(int argc, char **argv) { config.Append("dirman.root_node_mpi", "0"); //Running this should cause the mpisyncstart to translate the - //dirman,root_node_mpi rank to a webhook nodeid. You should + //dirman,root_node_mpi rank to a whookie nodeid. You should //see the update when you faodel::bootstrap::Start(config, config_dump_bootstrap); faodel::bootstrap::Finish(); diff --git a/examples/whookie/CMakeLists.txt b/examples/whookie/CMakeLists.txt index ae8b604..378f840 100644 --- a/examples/whookie/CMakeLists.txt +++ b/examples/whookie/CMakeLists.txt @@ -1,5 +1,5 @@ -set( EXAMPLE_LIBS Faodel::webhook Faodel::common ) +set( EXAMPLE_LIBS Faodel::whookie Faodel::common ) add_subdirectory( simple ) diff --git a/examples/whookie/README.md b/examples/whookie/README.md index 707a147..6ca1040 100644 --- a/examples/whookie/README.md +++ b/examples/whookie/README.md @@ -1,13 +1,13 @@ -WebHook Examples +Whookie Examples ================ -The WebHook project provides a simple way to exchange information with +The Whookie project provides a simple way to exchange information with a running node via an http interface. The current set of examples includes: bootstrap_example ----------------- -This example uses bootstrap to start up a node with a webhook +This example uses bootstrap to start up a node with a whookie server. It prints out a url for a service named "bob" that users can access with a web browser client (eg, curl, lynx, or firefox). The service prints out a simple info page. The application runs for a @@ -16,6 +16,6 @@ short amount of time and then shuts itself down. killit_example -------------- The killit example is similar to the bootstrap example, except that it -demonstrates how a webhook can be used to actively change the state of +demonstrates how a whookie can be used to actively change the state of the application. The killit hook in this example triggers a shutdown of the application. diff --git a/examples/whookie/simple/CMakeLists.txt b/examples/whookie/simple/CMakeLists.txt index 4a67454..19d0b32 100644 --- a/examples/whookie/simple/CMakeLists.txt +++ b/examples/whookie/simple/CMakeLists.txt @@ -1,12 +1,12 @@ -add_executable( webhook_bootstrap_example bootstrap_example.cpp ) -target_link_libraries( webhook_bootstrap_example ${EXAMPLE_LIBS} ) +add_executable( whookie_bootstrap_example bootstrap_example.cpp ) +target_link_libraries( whookie_bootstrap_example ${EXAMPLE_LIBS} ) -add_executable( webhook_killit_example killit_example.cpp ) -target_link_libraries( webhook_killit_example ${EXAMPLE_LIBS} ) +add_executable( whookie_killit_example killit_example.cpp ) +target_link_libraries( whookie_killit_example ${EXAMPLE_LIBS} ) -install(TARGETS webhook_bootstrap_example webhook_killit_example +install(TARGETS whookie_bootstrap_example whookie_killit_example EXPORT faodelExampleTargets RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin ) \ No newline at end of file diff --git a/examples/whookie/simple/bootstrap_example.cpp b/examples/whookie/simple/bootstrap_example.cpp index 2f436aa..e614ccc 100644 --- a/examples/whookie/simple/bootstrap_example.cpp +++ b/examples/whookie/simple/bootstrap_example.cpp @@ -2,9 +2,9 @@ // LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. -// Webhook Bootstrap Example +// Whookie Bootstrap Example // -// Bootstrap is used to start/stop Webhook in an application. Webhook is +// Bootstrap is used to start/stop Whookie in an application. Whookie is // different than other bootstraps in that it goes live when you Init() // it (as opposed to when you call Start()). This is useful because it // makes the rank's nodeid available earlier. @@ -14,7 +14,7 @@ #include #include -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "faodel-common/QuickHTML.hh" #include "faodel-common/Common.hh" @@ -22,16 +22,16 @@ using namespace std; //We use a configuration string to pass common parameters into our -//services. For webhook, the only things we need to worry about -//are the network interface you'd like to launch webhook on (important -//in nodes that have multiple nics) and the tcp port you'd like webhook +//services. For whookie, the only things we need to worry about +//are the network interface you'd like to launch whookie on (important +//in nodes that have multiple nics) and the tcp port you'd like whookie //to start on (note: you may not get that port if someone else //is already using it). You string default_config = R"EOF( -webhook.port 2112 +whookie.port 2112 bootstrap.debug true -webhook.debug true +whookie.debug true )EOF"; @@ -42,21 +42,21 @@ int main(int argc, char* argv[]) { //the information needed for the page inside of a lambda. More //sophisticated handlers should call out to functions in order to make //the core more readable. - webhook::Server::registerHook("/bob", [] (const map &args, stringstream &results){ + whookie::Server::registerHook("/bob", [] (const map &args, stringstream &results){ html::mkHeader(results, "Bob's Page"); html::mkTable(results, args, "Bobs args"); html::mkFooter(results); }); - //In this example, webhook is all we need from the FAODEL - //stack. We need to tell bootstrap that it should launch webhook + //In this example, whookie is all we need from the FAODEL + //stack. We need to tell bootstrap that it should launch whookie //and all of its dependencies faodel::bootstrap::Init(faodel::Configuration(default_config), - webhook::bootstrap); + whookie::bootstrap); //Once it's started, you can retrieve our node id - faodel::nodeid_t nid = webhook::Server::GetNodeID(); + faodel::nodeid_t nid = whookie::Server::GetNodeID(); //You should be able to browse to the web page now. cout<<"Started. Webserver is at: "< #include -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "faodel-common/QuickHTML.hh" #include "faodel-common/Common.hh" @@ -22,10 +22,10 @@ using namespace std; //Standard configuration settings string default_config = R"EOF( -webhook.port 2112 +whookie.port 2112 bootstrap.debug true -webhook.debug true +whookie.debug true )EOF"; bool keep_going=true; @@ -40,17 +40,17 @@ int main(int argc, char* argv[]) { //Register a simple killit page that calls the ShutMeDown function - webhook::Server::registerHook("/killit", [] (const map &args, stringstream &results){ + whookie::Server::registerHook("/killit", [] (const map &args, stringstream &results){ ShutMeDown(); }); - //Startup bootstraps (should only be webhook) + //Startup bootstraps (should only be whookie) faodel::bootstrap::Init(faodel::Configuration(default_config), - webhook::bootstrap); + whookie::bootstrap); //Once it's started, you can retrieve our node id - faodel::nodeid_t nid = webhook::Server::GetNodeID(); + faodel::nodeid_t nid = whookie::Server::GetNodeID(); cout<<"Started Webserver. Go to killit page to kill it: curl "<DefineNewDir(url); +} + //User wants to host a new directory that others can reference. Host the info //locally, and possibly publish the reference to the resource's parent -bool HostNewDir(const DirectoryInfo &dir_info) { +/** + * @brief Define a new resource and mark this node as the reference node + * @param dir_info The directory info for the new resource + * @retval TRUE This was added and is a new item + * @retval FALSE This was not added because the dir's url wasn't valid + * + * @note The url needs to have the reference node set to this node. + * + * User wants to host a new directory that others can reference. Host the + * info locally, and possibly publish the reference to the resource's parent. + */ +bool HostNewDir(const DirectoryInfo &dir_info) { return dirman::internal::Singleton::impl.core->HostNewDir(dir_info); } -bool HostNewDir(const ResourceURL &url) { - return dirman::internal::Singleton::impl.core->HostNewDir(url); } +bool HostNewDir(const ResourceURL &url) { + return dirman::internal::Singleton::impl.core->HostNewDir(url); +} //User wants this node to be used as part of an existing dir (join) or //removed from participation (leave). When joining you can provide a name //for your entity or let the owner generate a name for you. -bool JoinDirWithoutName(const ResourceURL &url, DirectoryInfo *dir_info) { +bool JoinDirWithoutName(const ResourceURL &url, DirectoryInfo *dir_info) { return dirman::internal::Singleton::impl.core->JoinDirWithoutName(url, dir_info); } bool JoinDirWithName(const ResourceURL &url, const string &name, DirectoryInfo *dir_info) { return dirman::internal::Singleton::impl.core->JoinDirWithName(url, name, dir_info); } -bool LeaveDir(const ResourceURL &url, DirectoryInfo *dir_info) { +bool LeaveDir(const ResourceURL &url, DirectoryInfo *dir_info) { return dirman::internal::Singleton::impl.core->LeaveDir(url, dir_info); } +/** + * @brief Tell directory manager to stop hosting information about a particular Dir + * @param url The directory to remove + * @retval TRUE Always completes + * @note This only removes references on the dirman server. It does not shutdown the server or wipe + * out info cached at other nodes in the system + */ +bool DropDir(const faodel::ResourceURL &url) { + return dirman::internal::Singleton::impl.core->DropDir(url); +} + /** * @brief Return info on which node dirman talks to for locating info * @return nodeid_t The id of the node that's in charge (eg root node) @@ -80,6 +121,7 @@ void GetCachedNames(vector *names) { return dirman::internal::Singleton::impl.core->GetCachedNames(names); } + } // namespace dirman diff --git a/src/dirman/DirMan.hh b/src/dirman/DirMan.hh index 25b90ef..cfe3275 100644 --- a/src/dirman/DirMan.hh +++ b/src/dirman/DirMan.hh @@ -22,6 +22,8 @@ bool GetDirectoryInfo(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir bool GetLocalDirectoryInfo(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info); bool GetRemoteDirectoryInfo(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info); +bool DefineNewDir(const faodel::ResourceURL &url); + bool HostNewDir(const faodel::DirectoryInfo &dir_info); bool HostNewDir(const faodel::ResourceURL &url); @@ -30,6 +32,8 @@ bool JoinDirWithName(const faodel::ResourceURL &url, const std::string &name, fa bool LeaveDir(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info=nullptr); +bool DropDir(const faodel::ResourceURL &url); + faodel::nodeid_t GetAuthorityNode(); void GetCachedNames(std::vector *names); diff --git a/src/dirman/README_DirMan.md b/src/dirman/README_DirMan.md index b430263..4a584b2 100644 --- a/src/dirman/README_DirMan.md +++ b/src/dirman/README_DirMan.md @@ -74,7 +74,7 @@ DirMan has the following build dependencies: | -------------- | ----------------------------------- | | FAODEL:SBL | Uses logging capabilities for boost | | FAODEL:Common | Uses bootstrap and `nodeid_t` | -| FAODEL:WebHook | For status info and new connections | +| FAODEL:Whookie | For status info and new connections | | FAODEL:Lunasa | For network memory management | | FAODEL:OpBox | For communication via ops | | Boost | Uses boost and systems components | diff --git a/src/dirman/common/DirectoryCache.cpp b/src/dirman/common/DirectoryCache.cpp index ddae1f9..8344868 100644 --- a/src/dirman/common/DirectoryCache.cpp +++ b/src/dirman/common/DirectoryCache.cpp @@ -78,7 +78,7 @@ bool DirectoryCache::CreateAndLinkParents(const DirectoryInfo &resource){ //Link in the parents if(ok){ - DirectoryInfo *r; + DirectoryInfo *pdir; bool found_existing_parent=false; ResourceURL child_url = resource.url; @@ -86,16 +86,17 @@ bool DirectoryCache::CreateAndLinkParents(const DirectoryInfo &resource){ ResourceURL parent_url = child_url.GetParent(); - found_existing_parent = _lookup(parent_url, &r, nullptr); + found_existing_parent = _lookup(parent_url, &pdir, nullptr); if(!found_existing_parent){ //Didn't find parent, so create it and add to the map - r = new DirectoryInfo(parent_url); - known_resources[parent_url.GetBucketPathName()] = r; + pdir = new DirectoryInfo(parent_url); + known_resources[parent_url.GetBucketPathName()] = pdir; } - //Either way, link to the child - bool tmp_ok = r->Join(child_url.reference_node, child_url.name); - kassert(tmp_ok, "Error creating parent in directory tree"); + + //Either way, link to the child. Note: child may already be here depending + //on what's been searched for earlier. + pdir->Join(child_url.reference_node, child_url.name); child_url=parent_url; //Move up one level } @@ -306,11 +307,20 @@ bool DirectoryCache::Lookup(const faodel::ResourceURL &search_url, DirectoryInfo bool found; DirectoryInfo *r; - dbg("Lookup "+search_url.GetFullURL()); + dbg("Lookup " + search_url.GetBucketPathName()); mutex->ReaderLock(); - found = _lookup(search_url, &r, reference_node); - if(resource_info) *resource_info = (found) ? *r : DirectoryInfo(); //Not found: set to empty + + if(search_url.IsRoot()) { + //Special case: figure out what's in the root directory + found = _lookupRootDir(search_url.bucket, resource_info); //This populates users struct + if((resource_info) && (!found)) *resource_info = DirectoryInfo(); //Send empty if no fields + + } else { + //Normal lookup for a subdirectory + found = _lookup(search_url, &r, reference_node); + if(resource_info) *resource_info = (found) ? *r : DirectoryInfo(); //Not found: set to empty + } mutex->Unlock(); return found; } @@ -329,9 +339,18 @@ bool DirectoryCache::Lookup(const vector &resource_urls, ve mutex->ReaderLock(); for(const auto &resource_url : resource_urls) { DirectoryInfo *r; - found = _lookup(resource_url, &r, nullptr); - if(resource_infos){ - resource_infos->push_back( (found) ? *r : DirectoryInfo() ); + if(resource_url.IsRoot()) { + //Special case: figure out what's in the root directory + DirectoryInfo resource_info; + found = _lookupRootDir(resource_url.bucket, &resource_info); + if(resource_infos) { + resource_infos->push_back( (found) ? resource_info : DirectoryInfo() ); + } + } else { + found = _lookup(resource_url, &r, nullptr); + if(resource_infos){ + resource_infos->push_back( (found) ? *r : DirectoryInfo() ); + } } all_found = all_found && found; } @@ -381,10 +400,10 @@ void DirectoryCache::GetAllNames(vector *names) { mutex->Unlock(); } -void DirectoryCache::webhookInfo(faodel::ReplyStream &rs){ +void DirectoryCache::whookieInfo(faodel::ReplyStream &rs){ rs.tableBegin("DirectoryCache "+GetComponentName()); - rs.tableTop({"Name","ReferenceNode","NumChildren","Info"}); + rs.tableTop({"Name","ReferenceNode","Nummembers","Info"}); mutex->ReaderLock(); for(auto &name_dirptr : known_resources){ stringstream ss; @@ -393,7 +412,7 @@ void DirectoryCache::webhookInfo(faodel::ReplyStream &rs){ rs.tableRow( { ss.str(),//name_dirptr.first, name_dirptr.second->GetReferenceNode().GetHtmlLink(), - to_string(name_dirptr.second->children.size()), + to_string(name_dirptr.second->members.size()), name_dirptr.second->info }); } mutex->Unlock(); @@ -435,7 +454,7 @@ void DirectoryCache::sstr(stringstream &ss, int depth, int indent) const { */ bool DirectoryCache::_lookup(const faodel::ResourceURL &url, DirectoryInfo **resource_info, nodeid_t *reference_node){ - kassert(resource_info!=nullptr, "Invalid resource info"); + kassert(resource_info!=nullptr, "Invalid resource info pointer"); kassert(url.Valid(), "Invalid url given to DC:"+url.GetFullURL()); map::iterator it; @@ -459,15 +478,42 @@ bool DirectoryCache::_lookup(const faodel::ResourceURL &url, DirectoryInfo **res if(reference_node) *reference_node = it->second->url.reference_node; return true; } +bool DirectoryCache::_lookupRootDir(bucket_t bucket, faodel::DirectoryInfo *resource_info) { + bool found=false; + kassert(resource_info!=nullptr, "Invalid resource info pointer"); + + //Set the dir info to root + if(resource_info) { + resource_info->url.bucket = bucket; + resource_info->url.path = "/"; + resource_info->url.name = ""; + } + + + //TODO: Change from scanning all to maintaining a root data structure + + for(auto const &bucketname_diptr: known_resources) { + //cout<<"Found '"<< bucketname_diptr.first << "' isRoot "<< bucketname_diptr.second->url.IsRootLevel()<url.IsRootLevel()) && + (bucketname_diptr.second->url.bucket == bucket) ) { //Only show our bucket + if(resource_info){ + resource_info->members.push_back( NameAndNode(bucketname_diptr.second->url.name, + bucketname_diptr.second->url.reference_node) ); + } + found=true; + } + } +return found; +} /** - * @brief Remove a single directory and report who its children were + * @brief Remove a single directory and report who its members were * @param url - The resource to remove - * @param children - A list of children that this node had + * @param members - A list of members that this node had * @retval TRUE Resource found and removed * @retval FALSE Resource wasn't found. Not removed. */ -bool DirectoryCache::_removeSingleDir(const faodel::ResourceURL &url, std::vector *children){ +bool DirectoryCache::_removeSingleDir(const faodel::ResourceURL &url, std::vector *members){ map::iterator it; string bucket_path_name = url.GetBucketPathName(); @@ -479,10 +525,10 @@ bool DirectoryCache::_removeSingleDir(const faodel::ResourceURL &url, std::vecto DirectoryInfo *r = it->second; known_resources.erase(it); - for( auto &name_node : r->children ){ - if((children!=nullptr) && (!name_node.name.empty())){ + for( auto &name_node : r->members ){ + if((members!=nullptr) && (!name_node.name.empty())){ dbg("_removeSingleDir marking for removal: "+bucket_path_name+"/"+name_node.name); - children->push_back( ResourceURL(bucket_path_name+"/"+name_node.name)); + members->push_back( ResourceURL(bucket_path_name+"/"+name_node.name)); } } delete r; diff --git a/src/dirman/common/DirectoryCache.hh b/src/dirman/common/DirectoryCache.hh index 86c7f4a..2eb453b 100644 --- a/src/dirman/common/DirectoryCache.hh +++ b/src/dirman/common/DirectoryCache.hh @@ -63,7 +63,7 @@ public: size_t NumberOfResources() const { mutex->ReaderLock(); size_t num = known_resources.size(); mutex->Unlock();return num; } - void webhookInfo(faodel::ReplyStream &rs); + void whookieInfo(faodel::ReplyStream &rs); void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; private: @@ -73,7 +73,8 @@ private: bool _write(const faodel::DirectoryInfo &resource_info, bool overwrite_existing); bool _lookup(const faodel::ResourceURL &url, faodel::DirectoryInfo **resource_info, faodel::nodeid_t *reference_node); - bool _removeSingleDir(const faodel::ResourceURL &url, std::vector *children=nullptr); + bool _lookupRootDir(faodel::bucket_t, faodel::DirectoryInfo *resource_info); + bool _removeSingleDir(const faodel::ResourceURL &url, std::vector *members=nullptr); std::map known_resources; faodel::MutexWrapper *mutex; diff --git a/src/dirman/common/DirectoryOwnerCache.cpp b/src/dirman/common/DirectoryOwnerCache.cpp index 3732b52..329c106 100644 --- a/src/dirman/common/DirectoryOwnerCache.cpp +++ b/src/dirman/common/DirectoryOwnerCache.cpp @@ -124,7 +124,7 @@ bool DirectoryOwnerCache::Lookup(const vector &search_urls, // return found; // } -void DirectoryOwnerCache::webhookInfo(faodel::ReplyStream &rs){ +void DirectoryOwnerCache::whookieInfo(faodel::ReplyStream &rs){ rs.tableBegin("DirectoryOwnerCache"); rs.tableTop({"Name","ReferenceNode"}); diff --git a/src/dirman/common/DirectoryOwnerCache.hh b/src/dirman/common/DirectoryOwnerCache.hh index adfbb45..629f7f8 100644 --- a/src/dirman/common/DirectoryOwnerCache.hh +++ b/src/dirman/common/DirectoryOwnerCache.hh @@ -42,7 +42,7 @@ public: size_t NumberOfResources() const { return known_resource_owners.size(); } - void webhookInfo(faodel::ReplyStream &rs); + void whookieInfo(faodel::ReplyStream &rs); void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; private: diff --git a/src/dirman/core/DirManCoreBase.cpp b/src/dirman/core/DirManCoreBase.cpp index 3624479..d7a5a26 100644 --- a/src/dirman/core/DirManCoreBase.cpp +++ b/src/dirman/core/DirManCoreBase.cpp @@ -39,7 +39,6 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string dc_mine("dirman.cache.mine"), doc("dirman.cache.owners"), my_node(NODE_UNSPECIFIED), - debug(false), strict_checking(false) { ConfigureLogging(config); @@ -49,10 +48,10 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string bool am_root; vector predefined_resources; + //Note: dirman.root_node may be set by parseRootNode() in derived classes config.GetLowercaseString(&threading_model, "threading_model", "pthreads"); - config.GetBool(&debug, "dirman.debug", "false"); config.GetBool(&am_root, "dirman.host_root", "false"); - config.GetBool(&strict_checking, "dirman.strict", "strict"); + config.GetBool(&strict_checking, "dirman.strict", "true"); config.GetLowercaseString(&threading_model, "dirman.threading_model", threading_model); config.GetLowercaseString(&mutex_type, "dirman.mutex_type", "rwlock"); config.GetStringVector(&predefined_resources,"dirman.resources"); @@ -63,14 +62,6 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string dc_mine.Init(config, threading_model, mutex_type); doc.Init(config, threading_model, mutex_type); - string fname, ename; - config.GetString(&ename, "dirman.root_node.file.env_name",""); - if(!ename.empty()){ - char *penv = getenv(ename.c_str()); - fname = string(penv); - } - config.GetString(&fname, "dirman.root_node.file",fname); - //Load any references into our database if(!predefined_resources.empty()) { @@ -82,7 +73,7 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string for(int i=predefined_resources.size()-1; i>=0; i--){ dbg("Considering "+predefined_resources[i]); ResourceURL url(predefined_resources[i]); - if(url.resource_type=="ref") continue; //Never add pure references + if(url.IsReference()) continue; //Never add pure references if(url.bucket == BUCKET_UNSPECIFIED) url.bucket = default_bucket; string path_name = url.GetPathName(); if( urls.find(path_name) == urls.end() ){ @@ -93,21 +84,20 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string for(auto &key_url : urls) { DirectoryInfo di(key_url.second); - //Any non-local resource defined here that doesn't have nodes in it //should be removed because it doesn't provide any actionable info. //If you don't do this, non-root nodes get stale info at init don't //bother to update from root. // Example: if we use mpisyncstart to create a dht, the root node - // gets a url with all the children, but the non-root nodes - // get zero children because mpisyncstart doesn't globally + // gets a url with all the members, but the non-root nodes + // get zero members because mpisyncstart doesn't globally // sync everything. - if((di.url.resource_type!="local") && (di.children.size()==0)) { + if((di.url.Type()!="local") && (di.members.size()==0)) { dbg("Not adding predefined resource "+key_url.first+" because it is not local and does not have any members"); throw runtime_error("Abort on adding "+key_url.first); continue; } - dbg("adding predefined resource "+key_url.first+" --> "+di.url.GetFullURL()+" Num Children="+std::to_string(di.children.size())); + dbg("adding predefined resource "+key_url.first+" --> "+di.url.GetFullURL()+" Num Members="+std::to_string(di.members.size())); dc_others.Create(di); //Note: this does not link parents } @@ -115,7 +105,6 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string #if 0 - vector surls_to_host; //urls encoded in config that need hosting vector urls_to_host; vector urls_to_check; @@ -132,7 +121,7 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string faodel::ResourceURL new_url(new_path); new_url.reference_node = my_node; if(new_url.bucket==BUCKET_UNSPECIFIED) new_url.bucket = default_bucket; - if(new_url.resource_type == "") new_url.resource_type = "ref"; + if(new_url.resource_type == "") new_url.resource_type = "ref"; //Do not use! reference detection should be IsReference() urls_to_host.push_back(new_url); dbg("This node now hosts: "+s+" which has url "+new_path); } @@ -197,6 +186,146 @@ DirManCoreBase::DirManCoreBase(const faodel::Configuration &config, const string } DirManCoreBase::~DirManCoreBase() = default; +/** + * @brief Determine which node is the reference node for a particular resource + * @param search_url A reference to a resource + * @param reference_node The node that is the designated owner of the resource + * @retval TRUE Was able to locate resource + * @retval FALSE Did not find the reference + * @note The base implementation currently only looks locally. Derived classes should implement this for remote use + */ +bool DirManCoreBase::Locate(const faodel::ResourceURL &search_url, nodeid_t *reference_node) { + + bool ok = lookupLocal(search_url, nullptr, reference_node); + KWARN("Locate should be global, not just local"); + return ok; +} + + +/** + * @brief Retrieve info about a particular resource directory entry + * @param url - Resource URL to look up + * @param check_local - Check our local cache for answer first + * @param check_remote - Check the remote node responsible for dir (if local not successful) + * @param dir_info - The resulting directory info (set to empty value if no entry) + * @retval TRUE The entry was found + * @retval FALSE The entry was no found + * @note The base implementation currently only looks locally. Derived classes should implement this for remote use + */ +bool DirManCoreBase::GetDirectoryInfo(const faodel::ResourceURL &search_url, bool check_local, bool check_remote, DirectoryInfo *dir_info){ + + if(check_local){ + bool ok = lookupLocal(search_url, dir_info, nullptr); + if(ok || !check_remote) return ok; + } + if(check_remote){ + KWARN("GetDirectoryInfo should be global, not just local"); + return false; + } + return false; +} + + + +/** + * @brief Define a new resource (when no nodes have been allocated yet) + * @param dir_info Information about the resource + * @retval TRUE If success + * @retval FALSE This did not complete because url wasn't valid or the reference wasn't defined + */ +bool DirManCoreBase::DefineNewDir(const DirectoryInfo &dir_info) { + + dbg("DefineNewDir "+dir_info.url.GetFullURL()); + + //Can't host it if it isn't valid + if(!dir_info.url.Valid()){ + error("Attempted to host new resource with invalid url "+dir_info.url.GetFullURL()); + return false; + } + + //If user did mark us as the reference, it's the same as HostNewDir + if(dir_info.url.reference_node == my_node) { + return HostNewDir(dir_info); + } + + // Fail: This default DefineNewDir doesn't know what to do with an undefined reference node. + // A core should define this and do the right thing instead. + + KTODO("Default DefineNewDir does not handle unspecified reference node case. Derived class should implement."); + return false; + +} + +/** + * @brief Define a new resource (when no nodes have been allocated yet) + * @param url Information about the resource + * @retval TRUE If success + * @retval FALSE This did not complete because url wasn't valid + */ +bool DirManCoreBase::DefineNewDir(const faodel::ResourceURL &url) { + dbg("DefineNewDir "+url.GetFullURL()); + if(!url.Valid()){ + error("Attempted to define new resource with invalid url "+url.GetFullURL()); + return false; + } + return DefineNewDir( DirectoryInfo(url)); + +} +/** + * @brief Create a new local resource and update parent + * @param dir_info - All of the info needed to describe this resource + * @retval TRUE This was added and is a new item + * @retval FALSE The dir's url was invalid, the reference node was not this node, or dir already existed + */ +bool DirManCoreBase::HostNewDir(const DirectoryInfo &dir_info){ + + dbg("HostNewDir "+dir_info.url.GetFullURL()); + + //Can't host it if it isn't valid + if(!dir_info.url.Valid()){ + error("Attempted to host new resource with invalid url "+dir_info.url.GetFullURL()); + return false; + } + + //Make sure url points to us + if(dir_info.url.reference_node != my_node){ + error("Attempted to host resource that didn't have our node's id. Had "+ + dir_info.url.reference_node.GetHex() + + " instead of "+my_node.GetHex()); + return false; + } + + //Create the actual resource (if it doesn't exist) and update doc + bool new_resource = dc_mine.Create(dir_info); + if(!new_resource) { + dbg("Attempted to create resource that's already registered?"); + return false; //It's already been registered + } + doc.Register(dir_info.url); //Just to make sure its known locally + + //Bail out here if this is a root: no parent to notify + if(dir_info.url.IsRootLevel()) return true; + + + //See if our parent is hosted here. Is so, join locally. + nodeid_t parent_node; + bool ok = discoverParent(dir_info.url, &parent_node); + dbg("hostresource discovered ok="+to_string(ok)+" parent was "+parent_node.GetHex()); + + kassert(ok,"couldn't discover parent for "+dir_info.url.GetFullURL()); + if((parent_node == my_node)||(parent_node==NODE_LOCALHOST)){ + dbg("hosted resource's parent available here. Joining."); + return dc_mine.Join(dir_info.url); + } + + //Not local, we must join a resource + dbg("hosted resource's parent not available here. remote joining."); + ok = joinRemote(parent_node, dir_info.url); + kassert(ok,"Couldn't host resource, because couldn't join parent?"); + + return true; +} + /** * @brief Create a new local resource and update parent * @param url - The bucket, path, and name of the resource @@ -215,12 +344,6 @@ bool DirManCoreBase::HostNewDir(const faodel::ResourceURL &url){ tmp_url.reference_node = my_node; - //Plug in a default reference in none provided - if(tmp_url.resource_type.empty()){ - kassert(!strict_checking, "HostNewDir url did not specify resource_type"); - tmp_url.resource_type="ref"; - } - return HostNewDir(DirectoryInfo(tmp_url)); } @@ -242,7 +365,6 @@ void DirManCoreBase::GetCachedNames(std::vector *resource_names) { dc_mine.GetAllNames(resource_names); } - /** * @brief Query local resources to see if info exists about a particular resource * @param search_url - search url @@ -305,6 +427,104 @@ bool DirManCoreBase::joinLocal(const faodel::ResourceURL &child_url, DirectoryIn + + +void DirManCoreBase::HandleWhookieStatus(const std::map &args, std::stringstream &results){ + + faodel::ReplyStream rs(args, "Directory Manager", &results); + rs.tableBegin("Directory Manager"); + rs.tableTop({"Parameter","Setting"}); + rs.tableRow({"Type:", GetType()}); + rs.tableRow({"Default Bucket:", default_bucket.GetHex()}); + appendWhookieParameterTable(&rs); + rs.tableEnd(); + + dc_mine.whookieInfo(rs); + dc_others.whookieInfo(rs); + doc.whookieInfo(rs); + + rs.Finish(); +} +void DirManCoreBase::HandleWhookieEntry(const std::map &args, std::stringstream &results){ + faodel::ReplyStream rs(args, "Directory Manager", &results); + auto it = args.find("name"); + if(it==args.end()){ + //Error + } else { + string s = it->second; + DirectoryInfo dir_info; + bool found = lookupLocal(s, &dir_info); + if(found){ + dir_info.whookieInfo(rs); + } + } + rs.Finish(); +} + +/** + * @brief Parse a configuration and figure out what it's root node is (via definition, env var, or file load) + * @param config Configuration file to examine + * @return root_nodeid The root node's id + * @throws runtime_error if parse fails or the parsed root_node is not valid + */ +nodeid_t DirManCoreBase::parseConfigForRootNode(const Configuration &config) const { + + rc_t rc; + string fname, root_node_hex; + + dbg("Parsing condfig for root node info"); + rc = config.GetString(&root_node_hex, "dirman.root_node"); + dbg("Searching for dirman.root_node gave '"+root_node_hex+"'"); + if(rc==ENOENT) { + + //See if we can find a root_node file. Check in this order: + // dirman.root_node.file + // dirman.root_node.file.env_name.if_defined + // dirman.root_node.file.env_name = FAODEL_DIRMAN_ROOT_NODE_FILE + // FAODEL_DIRMAN_ROOT_NODE + rc = config.GetFilename(&fname, "dirman.root_node", "FAODEL_DIRMAN_ROOT_NODE_FILE", ""); + dbg("GetFilename: '"+fname+"'"); + if(rc == 0) { + ifstream f; + f.open(fname); + if(!f.is_open()) { + throw std::runtime_error("dirman root node failed to read from file '"+fname+"'"); + } + getline(f, root_node_hex); + f.close(); + + } else { + + dbg("Searching for env var FAODEL_DIRMAN_ROOT_NODE"); + //Last chance: look for an env var. + const string ename = "FAODEL_DIRMAN_ROOT_NODE"; + char *penv = getenv(ename.c_str()); + if(penv) { + root_node_hex = string(penv); + } + if(root_node_hex.empty()) { + throw std::runtime_error( + "Dirman could not locate a root_node. The following were checked in this order:\n" + " configuration dirman.root_node\n" + " configuration dirman.root_node.file\n" + " configuration dirman.root_node.file.env_name.if_defined\n" + " env var FAODEL_DIRMAN_ROOT_NODE_FILE\n" + " env var FAODEL_DIRMAN_ROOT_NODE\n"); + } + } + } + + dbg("Root node is set to be "+root_node_hex); + + nodeid_t node(root_node_hex); + if(!node.Valid()) { + throw std::runtime_error("Dirman had parse problem with root_node '"+root_node_hex+"'"); + } + + return node; + +} + /** * @brief Reads in urls from one or more files * @param file_names - list of files to read, separated by semicolon @@ -362,6 +582,17 @@ bool DirManCoreBase::writeURLsToFileOrDie(const string file_name, const vector &args, std::stringstream &results){ - - faodel::ReplyStream rs(args, "Directory Manager", &results); - rs.tableBegin("Directory Manager"); - rs.tableTop({"Parameter","Setting"}); - rs.tableRow({"Type:", GetType()}); - rs.tableRow({"Default Bucket:", default_bucket.GetHex()}); - appendWebhookParameterTable(&rs); - rs.tableEnd(); - - dc_mine.webhookInfo(rs); - dc_others.webhookInfo(rs); - doc.webhookInfo(rs); - - rs.Finish(); -} -void DirManCoreBase::HandleWebhookEntry(const std::map &args, std::stringstream &results){ - faodel::ReplyStream rs(args, "Directory Manager", &results); - auto it = args.find("name"); - if(it==args.end()){ - //Error - } else { - string s = it->second; - DirectoryInfo dir_info; - bool found = lookupLocal(s, &dir_info); - if(found){ - dir_info.webhookInfo(rs); - } - } - rs.Finish(); -} /** - * @brief Generate any derived-class info to put into parameter list for DirMan webhook + * @brief Generate any derived-class info to put into parameter list for DirMan whookie * @param rs The reply stream to update. This is already in the middle of a table */ -void DirManCoreBase::appendWebhookParameterTable(faodel::ReplyStream *rs){ +void DirManCoreBase::appendWhookieParameterTable(faodel::ReplyStream *rs){ // ex.. rs->tableRow({"My Param", "My Value"}); } void DirManCoreBase::sstr(std::stringstream &ss, int depth, int indent) const { - ss<0){ dc_mine.sstr(ss, depth-1, indent+2); dc_others.sstr(ss, depth-1, indent+2); diff --git a/src/dirman/core/DirManCoreBase.hh b/src/dirman/core/DirManCoreBase.hh index e903146..2963f62 100644 --- a/src/dirman/core/DirManCoreBase.hh +++ b/src/dirman/core/DirManCoreBase.hh @@ -43,6 +43,10 @@ public: //DirMan Exposed API virtual bool Locate(const faodel::ResourceURL &search_url, faodel::nodeid_t *reference_node=nullptr); virtual bool GetDirectoryInfo(const faodel::ResourceURL &url, bool check_local, bool check_remote, faodel::DirectoryInfo *dir_info=nullptr) = 0; + + virtual bool DefineNewDir(const faodel::DirectoryInfo &dir_info) = 0; + bool DefineNewDir(const faodel::ResourceURL &url); + virtual bool HostNewDir(const faodel::DirectoryInfo &dir_info) = 0; bool HostNewDir(const faodel::ResourceURL &url); @@ -52,53 +56,49 @@ public: virtual bool JoinDirWithName(const faodel::ResourceURL &url, std::string name, faodel::DirectoryInfo *dir_info=nullptr) = 0; virtual bool LeaveDir(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info=nullptr) = 0; + //Remove references to a dir from the dirman server + virtual bool DropDir(const faodel::ResourceURL &url) = 0; + + //Report back who this node talks to to get info virtual faodel::nodeid_t GetAuthorityNode() const = 0; //Get a list of names this node knows about virtual void GetCachedNames(std::vector *resource_names); + //Different ways of looking up local info bool lookupLocal(const std::vector &search_url, std::vector *dir_info=nullptr); //for rpc bool lookupLocal(const faodel::ResourceURL &search_url, faodel::DirectoryInfo *dir_info=nullptr, faodel::nodeid_t *reference_node=nullptr); bool lookupLocal(const std::string &bucket_path, faodel::DirectoryInfo *dir_info=nullptr, faodel::nodeid_t *reference_node=nullptr); - //bool joinLocal(const faodel::ResourceURL &child_url, DirectoryInfo *parent_dir_info=nullptr); //for rpc - void HandleWebhookStatus(const std::map &args, std::stringstream &results); - void HandleWebhookEntry(const std::map &args, std::stringstream &results); + //Provide info back to whookie + void HandleWhookieStatus(const std::map &args, std::stringstream &results); + void HandleWhookieEntry(const std::map &args, std::stringstream &results); //InfoInterface void sstr(std::stringstream &ss, int depth=0, int indent=0) const override = 0; protected: - DirectoryCache dc_others; //Resources others (ie copies from prior lookups) - DirectoryCache dc_mine; //Resources I own (ie master copy in system) - DirectoryOwnerCache doc; //Where to find items - faodel::nodeid_t my_node; //Who I am - + DirectoryCache dc_others; //Resources others (ie copies from prior lookups) + DirectoryCache dc_mine; //Resources I own (ie master copy in system) + DirectoryOwnerCache doc; //Where to find items + faodel::nodeid_t my_node; //Who I am + faodel::bucket_t default_bucket; //Bucket to use + bool strict_checking; //Add additional hooks for checking requests //Internal API for implementing Exposed API virtual bool discoverParent(const faodel::ResourceURL &url, faodel::nodeid_t *reference_node) = 0; virtual bool cacheForeignDir(const faodel::DirectoryInfo &dir_info) = 0; virtual bool lookupRemote(faodel::nodeid_t nodeid, const faodel::ResourceURL &resource_url, faodel::DirectoryInfo *dir_info=nullptr) = 0; virtual bool joinRemote(faodel::nodeid_t parent_node, const faodel::ResourceURL &child_url, bool send_detailed_reply=false) = 0; + virtual void appendWhookieParameterTable(faodel::ReplyStream *rs); + //Helpers + faodel::nodeid_t parseConfigForRootNode(const faodel::Configuration &config) const; std::vector readURLsFromFilesWithRetry(std::string file_names); bool writeURLsToFileOrDie(const std::string file_name, const std::vector &urls); - - virtual void appendWebhookParameterTable(faodel::ReplyStream *rs); - - //bool registerURL(const faodel::ResourceURL url); - //bool registerDirectoryInfo(const DirectoryInfo resource); - - //bool _RegisterDirectoryInfo(std::map &ric, const DirectoryInfo &dir_info); - //bool _LookupDirectoryInfo(std::map &ric, const faodel::ResourceURL &url, DirectoryInfo **dir_info); - - bool debug; - bool strict_checking; - faodel::bucket_t default_bucket; - }; } // namespace internal diff --git a/src/dirman/core/DirManCoreCentralized.cpp b/src/dirman/core/DirManCoreCentralized.cpp index 729a3b3..f8bc506 100644 --- a/src/dirman/core/DirManCoreCentralized.cpp +++ b/src/dirman/core/DirManCoreCentralized.cpp @@ -3,12 +3,13 @@ // the U.S. Government retains certain rights in this software. +#include #include "opbox/OpBox.hh" #include "opbox/net/net.hh" #include "dirman/core/DirManCoreCentralized.hh" #include "dirman/ops/OpDirManCentralized.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; using namespace faodel; @@ -23,29 +24,37 @@ DirManCoreCentralized::DirManCoreCentralized(const faodel::Configuration &config string root_node_hex; + string write_root_filename; config.GetBool(&am_root, "dirman.host_root", "false"); - config.GetLowercaseString(&root_node_hex, "dirman.root_node", ""); + config.GetFilename(&write_root_filename, "dirman.write_root", "", ""); - my_node = webhook::Server::GetNodeID(); + my_node = whookie::Server::GetNodeID(); - if (!root_node_hex.empty()){ - //Query to find the root node - root_id = nodeid_t(root_node_hex); + if(!am_root) { + dbg("Checking for root node"); + root_id = parseConfigForRootNode(config); //May throw if no valid root am_root = (root_id == my_node); - dbg("Setting root node to "+root_id.GetHex()); + dbg("Setting root node to " + root_id.GetHex()); + } - } else if(am_root){ + if(am_root) { dbg("Am hosting root"); root_id = my_node; - //root_id can't be set here because network not up yet - } else if (root_node_hex.empty()){ - error("DirManCoreCentralized: no dirman.root_node provided in configuration"); - KTODO("DMCC handle no root node in config"); + //See if we've been instructed to write to a file + if(!write_root_filename.empty()) { + dbg("Root is writing file "+write_root_filename); + ofstream f; + f.open(write_root_filename); + if(!f.is_open()) { + throw std::runtime_error("dirman root node failed to write_root_filename "+write_root_filename); + } + f << my_node.GetHex() < predefined_urls; @@ -102,13 +111,14 @@ bool DirManCoreCentralized::Locate(const ResourceURL &search_url, nodeid_t *refe */ bool DirManCoreCentralized::GetDirectoryInfo(const faodel::ResourceURL &url, bool check_local, bool check_remote, DirectoryInfo *dir_info) { - dbg("GetDirInfo request to (local="+to_string(check_local)+",remote="+to_string(check_remote)+ ") requesting resource"+url.GetURL()); + dbg("GetDirInfo request to (local="+to_string(check_local)+",remote="+to_string(check_remote)+ ") requesting resource "+url.GetBucketPathName()); //Fixup the url by filling in the bucket faodel::ResourceURL url_mod = localizeURL(url, false); if(am_root){ //We're the root node. Just query local structures to find answer + if(dir_info) dir_info->url.reference_node = root_id; //Ensure we are listed as root bool found = dc_mine.Lookup(url_mod, dir_info); dbg("On-Root local query found: "+to_string(found)); return found; @@ -128,29 +138,49 @@ bool DirManCoreCentralized::GetDirectoryInfo(const faodel::ResourceURL &url, boo } dbg("Off-Root missed local cache. Issue request to root "+root_id.GetHex()+" for "+url_mod.GetPathName()); - //Launch a message - OpDirManCentralized *op = new OpDirManCentralized(OpDirManCentralized::RequestType::GetInfo, root_id, url_mod); - future fut1 = op->GetFuture(); - opbox::LaunchOp(op); - //Block until get a result (could be good or bad) - DirectoryInfo di2 = fut1.get(); + try { + //Launch a message + OpDirManCentralized *op = new OpDirManCentralized(OpDirManCentralized::RequestType::GetInfo, root_id, url_mod); + future fut1 = op->GetFuture(); + opbox::LaunchOp(op); + + //Block until get a result (could be good or bad) + DirectoryInfo di2 = fut1.get(); + + //Skip out if the dirinfo we got back is empty + if(di2.IsEmpty()) { + dbg("GetDirInfo did not get a valid result from root node"); + return false; + } + - //Skip out if the dirinfo we got back is empty - if(di2.IsEmpty()) { - dbg("GetDirInfo did not get a valid result from root node"); + //Pass valid result back + dbg("GetDirInfo Got remote result back: " + di2.to_string() + " members " + to_string(di2.members.size())); + dc_others.CreateAndLinkParents(di2); + if(dir_info) *dir_info = di2; + return true; + + } catch(const std::exception &e) { + //Connect may throw if bad root node + error("DirMan Communication error "+string(e.what())); return false; } - //Pass valid result back - dbg("GetDirInfo Got remote result back: " + di2.to_string() + " children " + to_string(di2.children.size())); - dc_others.CreateAndLinkParents(di2); - if(dir_info) *dir_info = di2; - return true; - } } +/** + * @brief Define a new directory entry (but don't host it) + * @param dir_info Information for new directory entry + * @retval TRUE The resource wasn't known and was created ok + * @retval FALSE The resource already exists and therefore was NOT modified + */ +bool DirManCoreCentralized::DefineNewDir(const DirectoryInfo &dir_info) { + dbg("DefineNewDir "+dir_info.to_string()); + return HostNewDir(dir_info); //Note: Nothing else needed because this sets reference node to root +} + /** * @brief Create a new directory entry. In this case, push info to root node * @param dir_info - Information for new directory entry @@ -231,7 +261,7 @@ bool DirManCoreCentralized::JoinDirWithName(const faodel::ResourceURL &url, stri bool DirManCoreCentralized::LeaveDir(const faodel::ResourceURL &url, DirectoryInfo *dir_info) { dbg("LeaveDir "+url.GetURL()); - //Fixup the dir_info by filling in the bucket. Note + //Fixup the dir_info by filling in the bucket. faodel::ResourceURL url_mod = localizeURL(url, false); if(am_root){ @@ -251,6 +281,37 @@ bool DirManCoreCentralized::LeaveDir(const faodel::ResourceURL &url, DirectoryIn } } +/** + * @brief Instruct the root node to drop a specific directory + * @param url The resource reference to drop + * @return true + * @note This only removes the entry from the local and dirman nodes. It does not shutdown + * the actual resource or remove references to it at other nodes. + */ +bool DirManCoreCentralized::DropDir(const faodel::ResourceURL &url) { + dbg("DropDir "+url.GetURL()); + + //Fixup the dir_info by filling in the bucket. Note + faodel::ResourceURL url_mod = localizeURL(url, false); + + if(am_root) { + return dc_mine.Remove(url_mod); + + } else { + + //Launch a message + OpDirManCentralized *op = new OpDirManCentralized(OpDirManCentralized::RequestType::DropDir, root_id, url_mod); + future fut1 = op->GetFuture(); + opbox::LaunchOp(op); + + //Block for a reply, though we don't need it + DirectoryInfo di2 = fut1.get(); + + return dc_others.Remove(url_mod); + } + +} + bool DirManCoreCentralized::discoverParent(const ResourceURL &resource_url, nodeid_t *parent_node){ dbg("discover parent of "+resource_url.GetFullURL()); @@ -290,9 +351,9 @@ faodel::ResourceURL DirManCoreCentralized::localizeURL(const faodel::ResourceURL return url_mod; } -void DirManCoreCentralized::appendWebhookParameterTable(faodel::ReplyStream *rs){ - rs->tableRow({"Root Node", root_id.GetHtmlLink()}); - rs->tableRow({"Am Root", std::to_string(am_root)}); +void DirManCoreCentralized::appendWhookieParameterTable(faodel::ReplyStream *rs){ + rs->tableRow({"Root Node:", rs->createLink(root_id.GetHex(), root_id.GetHttpLink(), true) }); + rs->tableRow({"Am Root:", ((am_root) ?"True":"False")}); } void DirManCoreCentralized::sstr(stringstream &ss, int depth, int indent) const { diff --git a/src/dirman/core/DirManCoreCentralized.hh b/src/dirman/core/DirManCoreCentralized.hh index 043ec2b..cfaf4f8 100644 --- a/src/dirman/core/DirManCoreCentralized.hh +++ b/src/dirman/core/DirManCoreCentralized.hh @@ -46,9 +46,12 @@ public: std::string GetType() const override { return "centralized"; } bool Locate(const faodel::ResourceURL &search_url, faodel::nodeid_t *reference_node= nullptr) override; bool GetDirectoryInfo(const faodel::ResourceURL &url, bool check_local, bool check_remote, faodel::DirectoryInfo *dir_info) override; + bool DefineNewDir(const faodel::DirectoryInfo &dir_info) override; bool HostNewDir(const faodel::DirectoryInfo &dir_info) override; bool JoinDirWithName(const faodel::ResourceURL &url, std::string name, faodel::DirectoryInfo *dir_info= nullptr) override; bool LeaveDir(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info= nullptr) override; + bool DropDir(const faodel::ResourceURL &url) override; + faodel::nodeid_t GetAuthorityNode() const { return root_id; } @@ -58,7 +61,7 @@ public: bool lookupRemote(faodel::nodeid_t nodeid, const faodel::ResourceURL &resource_url, faodel::DirectoryInfo *dir_info= nullptr) override; bool joinRemote(faodel::nodeid_t parent_node, const faodel::ResourceURL &child_url, bool send_detailed_reply=false) override; - void appendWebhookParameterTable(faodel::ReplyStream *rs) override; + void appendWhookieParameterTable(faodel::ReplyStream *rs) override; //InfoInterface void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/dirman/core/DirManCoreStatic.cpp b/src/dirman/core/DirManCoreStatic.cpp index 8569f7a..a9da7d1 100644 --- a/src/dirman/core/DirManCoreStatic.cpp +++ b/src/dirman/core/DirManCoreStatic.cpp @@ -7,7 +7,7 @@ #include "opbox/net/net.hh" #include "dirman/core/DirManCoreStatic.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; using namespace faodel; @@ -76,9 +76,20 @@ bool DirManCoreStatic::GetDirectoryInfo(const faodel::ResourceURL &url, bool che return found; } +/** + * @brief Define a new directory entry (but don't host it) + * @param dir_info Information for new directory entry + * @retval TRUE The resource wasn't known and was created ok + * @retval FALSE The resource already exists and therefore was NOT modified + */ +bool DirManCoreStatic::DefineNewDir(const DirectoryInfo &dir_info) { + dbg("DefineNewDir "+dir_info.to_string()); + return HostNewDir(dir_info); +} + /** * @brief Create a new directory entry. In this case, push info to root node - * @param dir_info - Information for new directory entry + * @param dir_info Information for new directory entry * @retval TRUE The resource wasn't known and was created ok * @retval FALSE The resource already exists and therefore was NOT modified */ @@ -129,7 +140,21 @@ bool DirManCoreStatic::LeaveDir(const faodel::ResourceURL &url, DirectoryInfo *d faodel::ResourceURL url_mod = localizeURL(url, false); return dc_mine.Leave(url_mod, dir_info); +} + +/** + * @brief Instruct the root node to drop a specific directory + * @param url The resource reference to drop + * @return true + * @note This only removes the entry from the local node. It doesn't remove references elsewhere + */ +bool DirManCoreStatic::DropDir(const faodel::ResourceURL &url) { + dbg("DropDir "+url.GetURL()); + + //Fixup the dir_info by filling in the bucket. Note + faodel::ResourceURL url_mod = localizeURL(url, false); + return dc_mine.Remove(url_mod); } bool DirManCoreStatic::discoverParent(const ResourceURL &resource_url, nodeid_t *parent_node){ @@ -165,7 +190,7 @@ faodel::ResourceURL DirManCoreStatic::localizeURL(const faodel::ResourceURL &url return url_mod; } -void DirManCoreStatic::appendWebhookParameterTable(faodel::ReplyStream *rs){ +void DirManCoreStatic::appendWhookieParameterTable(faodel::ReplyStream *rs){ } void DirManCoreStatic::sstr(stringstream &ss, int depth, int indent) const { diff --git a/src/dirman/core/DirManCoreStatic.hh b/src/dirman/core/DirManCoreStatic.hh index 32fb727..3108411 100644 --- a/src/dirman/core/DirManCoreStatic.hh +++ b/src/dirman/core/DirManCoreStatic.hh @@ -43,9 +43,11 @@ public: std::string GetType() const override { return "Static"; } bool Locate(const faodel::ResourceURL &search_url, faodel::nodeid_t *reference_node= nullptr) override; bool GetDirectoryInfo(const faodel::ResourceURL &url, bool check_local, bool check_remote, faodel::DirectoryInfo *dir_info) override; + bool DefineNewDir(const faodel::DirectoryInfo &dir_info) override; bool HostNewDir(const faodel::DirectoryInfo &dir_info) override; bool JoinDirWithName(const faodel::ResourceURL &url, std::string name, faodel::DirectoryInfo *dir_info= nullptr) override; bool LeaveDir(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info= nullptr) override; + bool DropDir(const faodel::ResourceURL &url) override; faodel::nodeid_t GetAuthorityNode() const { return my_node; } @@ -55,7 +57,7 @@ public: bool lookupRemote(faodel::nodeid_t nodeid, const faodel::ResourceURL &resource_url, faodel::DirectoryInfo *dir_info= nullptr) override; bool joinRemote(faodel::nodeid_t parent_node, const faodel::ResourceURL &child_url, bool send_detailed_reply=false) override; - void appendWebhookParameterTable(faodel::ReplyStream *rs) override; + void appendWhookieParameterTable(faodel::ReplyStream *rs) override; //InfoInterface void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/dirman/core/DirManCoreUnconfigured.cpp b/src/dirman/core/DirManCoreUnconfigured.cpp index 9f1f591..896539d 100644 --- a/src/dirman/core/DirManCoreUnconfigured.cpp +++ b/src/dirman/core/DirManCoreUnconfigured.cpp @@ -9,7 +9,7 @@ -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; using namespace faodel; @@ -32,10 +32,11 @@ void DirManCoreUnconfigured::finish() bool DirManCoreUnconfigured::Locate(const ResourceURL &u, nodeid_t *r) { return Panic("Locate"); } bool DirManCoreUnconfigured::GetDirectoryInfo(const ResourceURL &u, bool l, bool r, DirectoryInfo *d) { return Panic("GetDirectoryInfo"); } +bool DirManCoreUnconfigured::DefineNewDir(const DirectoryInfo &d) { return Panic("DefineNewDir"); } bool DirManCoreUnconfigured::HostNewDir(const DirectoryInfo &d) { return Panic("HostNewDir"); } bool DirManCoreUnconfigured::JoinDirWithName(const ResourceURL &u, string name, DirectoryInfo *d) { return Panic("JoinDirWithName"); } bool DirManCoreUnconfigured::LeaveDir(const ResourceURL &u, DirectoryInfo *d) { return Panic("LeaveDir"); } - +bool DirManCoreUnconfigured::DropDir(const ResourceURL &u) { return Panic("DropDir");} //Internal API for implementing Exposed API. Most of these call Panic() to catch unconfigured system bool DirManCoreUnconfigured::discoverParent(const ResourceURL &u, nodeid_t *r) { return Panic("discoverParent"); } bool DirManCoreUnconfigured::cacheForeignDir(const DirectoryInfo &d) { return Panic("cacheForeignDir"); } diff --git a/src/dirman/core/DirManCoreUnconfigured.hh b/src/dirman/core/DirManCoreUnconfigured.hh index 84b9209..ba4b6ea 100644 --- a/src/dirman/core/DirManCoreUnconfigured.hh +++ b/src/dirman/core/DirManCoreUnconfigured.hh @@ -30,9 +30,11 @@ public: std::string GetType() const override { return "unconfigured"; }; bool Locate(const faodel::ResourceURL &search_url, faodel::nodeid_t *reference_node=nullptr) override; bool GetDirectoryInfo(const faodel::ResourceURL &url, bool check_local, bool check_remote, faodel::DirectoryInfo *dir_info) override; + bool DefineNewDir(const faodel::DirectoryInfo &dir_info) override; bool HostNewDir(const faodel::DirectoryInfo &dir_info) override; bool JoinDirWithName(const faodel::ResourceURL &url, std::string name, faodel::DirectoryInfo *dir_info=nullptr) override; bool LeaveDir(const faodel::ResourceURL &url, faodel::DirectoryInfo *dir_info=nullptr) override; + bool DropDir(const faodel::ResourceURL &url) override; faodel::nodeid_t GetAuthorityNode() const { return faodel::NODE_UNSPECIFIED; } diff --git a/src/dirman/core/Singleton.cpp b/src/dirman/core/Singleton.cpp index 0136cb3..3d89d6b 100644 --- a/src/dirman/core/Singleton.cpp +++ b/src/dirman/core/Singleton.cpp @@ -8,8 +8,8 @@ #include "dirman/core/DirManCoreStatic.hh" #include "dirman/core/DirManCoreCentralized.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" using namespace std; @@ -51,7 +51,7 @@ void SingletonImpl::GetBootstrapDependencies( vector &optional) const { name = "dirman"; requires = {"opbox"}; - optional = {"webhook", "mpisyncstart"}; + optional = {"whookie", "mpisyncstart"}; } @@ -85,11 +85,11 @@ void SingletonImpl::Init(const faodel::Configuration &config){ exit(-1); } - webhook::Server::updateHook("/dirman", [this] (const map &args, stringstream &results) { - return core->HandleWebhookStatus(args, results); + whookie::Server::updateHook("/dirman", [this] (const map &args, stringstream &results) { + return core->HandleWhookieStatus(args, results); }); - webhook::Server::updateHook("/dirman/entry", [this] (const map &args, stringstream &results) { - return core->HandleWebhookEntry(args, results); + whookie::Server::updateHook("/dirman/entry", [this] (const map &args, stringstream &results) { + return core->HandleWhookieEntry(args, results); }); } @@ -117,7 +117,7 @@ void SingletonImpl::Finish() { dirman_service_none=false; return; } - webhook::Server::deregisterHook("/dirman"); + whookie::Server::deregisterHook("/dirman"); if(IsUnconfigured()){ diff --git a/src/dirman/ops/OpDirManCentralized.cpp b/src/dirman/ops/OpDirManCentralized.cpp index 8c0c8fb..6c19adf 100644 --- a/src/dirman/ops/OpDirManCentralized.cpp +++ b/src/dirman/ops/OpDirManCentralized.cpp @@ -52,13 +52,16 @@ OpDirManCentralized::OpDirManCentralized(RequestType req_type, faodel::nodeid_t OpDirManCentralized::OpDirManCentralized(RequestType req_type, faodel::nodeid_t root_id, faodel::ResourceURL url) : Op(true), state(State::start), ldo_msg(), request_type(req_type) { - kassert((req_type == RequestType::GetInfo) || - (req_type == RequestType::JoinDir) || + kassert((req_type == RequestType::GetInfo) || + (req_type == RequestType::JoinDir) || (req_type == RequestType::LeaveDir) || + (req_type == RequestType::DropDir) || (req_type == RequestType::ReturnDirInfo), "Request type not handled"); int rc = opbox::net::Connect(&peer, root_id); //Retrieve the root's peer ptr - kassert((rc==0), "Connect failed?"); + if(rc!=0) { + throw std::runtime_error("DirMan could not connect to server "+root_id.GetHex()+" - "+root_id.GetHttpLink()); + } msg_dirman::AllocateRequest(ldo_msg, req_type, diff --git a/src/dirman/ops/OpDirManCentralized.hh b/src/dirman/ops/OpDirManCentralized.hh index 6237a9a..38d5cc9 100644 --- a/src/dirman/ops/OpDirManCentralized.hh +++ b/src/dirman/ops/OpDirManCentralized.hh @@ -44,6 +44,7 @@ public: GetInfo = 0x02, JoinDir = 0x03, LeaveDir = 0x04, + DropDir = 0x05, ReturnDirInfo = 0x15 }; diff --git a/src/dirman/ops/OpDirManCentralized_Target.cpp b/src/dirman/ops/OpDirManCentralized_Target.cpp index 153f753..856526f 100644 --- a/src/dirman/ops/OpDirManCentralized_Target.cpp +++ b/src/dirman/ops/OpDirManCentralized_Target.cpp @@ -59,6 +59,9 @@ WaitingType OpDirManCentralized::UpdateTarget(OpArgs *args) { case RequestType::LeaveDir: dirman::LeaveDir(url, &result_dir_info); break; + case RequestType::DropDir: + dirman::DropDir(url); + break; default: //Unknown? throw std::invalid_argument("Unknown case condition"); } diff --git a/src/dirman/ops/msg_dirman.hh b/src/dirman/ops/msg_dirman.hh index a94c830..1620696 100644 --- a/src/dirman/ops/msg_dirman.hh +++ b/src/dirman/ops/msg_dirman.hh @@ -9,6 +9,9 @@ namespace dirman { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * @brief A message data structure used by dirman ops * @@ -65,6 +68,8 @@ typedef struct msg_dirman { } msg_dirman_t; +#pragma GCC diagnostic pop + } // namespace dirman diff --git a/src/faodel-common/Bootstrap.cpp b/src/faodel-common/Bootstrap.cpp index e97efeb..ae27266 100644 --- a/src/faodel-common/Bootstrap.cpp +++ b/src/faodel-common/Bootstrap.cpp @@ -111,10 +111,10 @@ vector GetStartupOrder() { } /** - * @brief Call for webhook to set our nodeid + * @brief Call for whookie to set our nodeid * * @param[in] iuo A marker signifying that this is only for internal use - * @param[in] nodeid The webhook nodeid for this node + * @param[in] nodeid The whookie nodeid for this node * @note This function is intended for internal use only. */ void setNodeID(internal_use_only_t iuo, NodeID nodeid) { @@ -207,7 +207,7 @@ Configuration GetConfiguration() { } /** - * @brief Webhook for dumping info about the bootstraps + * @brief Whookie for dumping info about the bootstraps * @param rs ReplyStream to update with info */ void dumpInfo(faodel::ReplyStream &rs) { diff --git a/src/faodel-common/BootstrapImplementation.cpp b/src/faodel-common/BootstrapImplementation.cpp index bd9ee60..714033d 100644 --- a/src/faodel-common/BootstrapImplementation.cpp +++ b/src/faodel-common/BootstrapImplementation.cpp @@ -13,7 +13,7 @@ #include -#include +#include #include "faodel-common/BootstrapImplementation.hh" @@ -150,7 +150,7 @@ void Bootstrap::Init(const Configuration &config) { bs.init_function(&configuration); } - //Note: we can't install bootstrap webhook here because of build order issues. Do it in webhook + //Note: we can't install bootstrap whookie here because of build order issues. Do it in whookie dbg("Completed Init'ing services. Moved to 'initialized' state."); state = State::INITIALIZED; diff --git a/src/faodel-common/Configuration.cpp b/src/faodel-common/Configuration.cpp index f46f645..e080e52 100644 --- a/src/faodel-common/Configuration.cpp +++ b/src/faodel-common/Configuration.cpp @@ -68,8 +68,8 @@ Configuration::Configuration(const string &configuration_string, Append("config.additional_files.env_name.if_defined", env_variable_for_extra_settings); } if(!configuration_string.empty()) { - int rc = Append(configuration_string); - kassert(rc == 0, "Configuration string had problems"); + rc_t rc = Append(configuration_string); + if(rc!=0) throw std::runtime_error("Configuration's initialization string had errors"); } } @@ -131,11 +131,11 @@ rc_t Configuration::AppendFromFile(const string &file_name) { * * - **config.additional_files.env_name ABC;DEF**: * Read file names from environment variables ABC and DEF and append the data - * from the files specified by ABC and DEF to Configuration. Exit if these + * from the files specified by ABC and DEF to Configuration. Exception if these * environment variables are not defined. * * - **config.additional_files.env_name.if_defined ABC;DEF**: - * Do the same as config.additional_files.env_name, but do not exit if the + * Do the same as config.additional_files.env_name, but do not throw exception if the * environment variables don't exist. * * @note The config.additional_files markers are removed from Configuration @@ -162,10 +162,8 @@ rc_t Configuration::AppendFromReferences() { if(env_name1!="") { char *config_file = getenv(env_name1.c_str()); if(config_file== nullptr) { - cerr <<"Configuration error: config.additional_files.env set to "<second; + if(val) *val = it->second; return 0; } @@ -571,8 +580,10 @@ rc_t Configuration::GetPtr(void **val, const string &name, * @brief Retrieve a filename from Config (or the environment) * @param[out] fname The resulting file name * @param[in] name The item to look up (name+".file") + * @param[in] default_env_var The default environment var to use if none specified * @param[in] default_value The default filename to use if not found * @retval 0 If filename was resolved + * @throws runtime_error if required env var is missing * * This function attempts to retrieve a filename from the config or * the environment. The name supplied to this function will be appended @@ -591,23 +602,41 @@ rc_t Configuration::GetPtr(void **val, const string &name, * - myfile.file.env_name.if_defined XYZ: The user wants to check * the environment variable XYZ for the filename. If that variable * doesn't exist, use myfile.file from Configuration + * + * Order of processing: + * 1. name.file + * 2. name.file.env_name.if_defined + * 3. name.file.env_name (can fail) + * 4. default_env_var + * 5. default_file */ rc_t Configuration::GetFilename(string *fname, const string &name, - const string &default_value) const { + const string &default_env_var, + const string &default_file) const { + string tmp_fname; string ename; char *penv; + //First choice: See if file is explicitly defined. No default value yet. + rc_t rc = GetString(&tmp_fname, name+".file"); + if(rc==0) { + string expanded = ExpandPathSafely(tmp_fname); + if(expanded.length() > 0) { + if(fname) *fname = expanded; + return 0; + } + } + //Look for an optional env var - GetString(&ename, name+".file.env_name.if_defined",""); + GetString(&ename, name+".file.env_name.if_defined", ""); if(!ename.empty()) { - //They asked to check for an env var and use if exists penv = getenv(ename.c_str()); if(penv) { //Env var exists, use it string expanded = ExpandPathSafely(string(penv)); if (expanded.length() > 0) { - *fname = expanded; + if(fname) *fname = expanded; return 0; } } @@ -623,19 +652,35 @@ rc_t Configuration::GetFilename(string *fname, const string &name, //Env var exists, use it string expanded = ExpandPathSafely(string(penv)); if (expanded.length() > 0) { - *fname = expanded; + if(fname) *fname = expanded; return 0; } - cerr <<"Configuration requested " - < 0) { + if(fname) *fname = expanded; + return 0; + } + } + //Not mandatory, so fall through + } + + //Last try: see if default value set + if(!default_file.empty()) { + string expanded = ExpandPathSafely(default_file); + if(fname) *fname = expanded; + return 0; + } + + return ENOENT; } /** @@ -716,6 +761,32 @@ map Configuration::GetComponentSettings(const string &component_n return results; } +/** + * @brief Determin if a component's logging settings are enabled. + * @param[out] dbg_enabled either component.debug or component.log.debug is true + * @param[out] info_enabled either component.debug or component.log.info is true + * @param[out] warn_enabled either component.debug or component.log.warn is true + * @param[in] component_name Name of component to look up in Configuration + * @retval 0 + */ +rc_t Configuration::GetComponentLoggingSettings(bool *dbg_enabled, bool *info_enabled, bool *warn_enabled, const string &component_name) const { + + + //Allow user to do "component.debug" instead of "component.log.debug" + GetBool(dbg_enabled, component_name+".debug", "false"); + + string default_setting="false"; + if((dbg_enabled!=nullptr) && (*dbg_enabled)) { + default_setting="true"; + } + + //But still trust log.debug as an override. + GetBool(dbg_enabled, component_name+".log.debug", default_setting); + GetBool(info_enabled, component_name+".log.info", default_setting); + GetBool(warn_enabled, component_name+".log.warn", default_setting); + return 0; +} + /** * @brief Get entire list of configuration settings as a string table for debug * @param[out] results A vector of k/v pairs diff --git a/src/faodel-common/Configuration.hh b/src/faodel-common/Configuration.hh index 0ee5562..2ce0d6b 100644 --- a/src/faodel-common/Configuration.hh +++ b/src/faodel-common/Configuration.hh @@ -111,6 +111,8 @@ public: rc_t Unset(const std::string &name); + bool Contains(const std::string &name) const; + //Get values out of the config. Returns 0 if ok, -1 if not found rc_t GetString(std::string *val, const std::string &name, const std::string &default_value="") const; rc_t GetLowercaseString(std::string *val, const std::string &name, const std::string &default_value="") const; @@ -118,7 +120,7 @@ public: rc_t GetUInt(uint64_t *uval, const std::string &name, const std::string &default_value="0") const; rc_t GetBool(bool *val, const std::string &name, const std::string &default_value="") const; rc_t GetPtr(void **val, const std::string &name, void *default_value=nullptr) const; - rc_t GetFilename(std::string *fname, const std::string &name, const std::string &default_value) const; + rc_t GetFilename(std::string *fname, const std::string &name, const std::string &default_env_var, const std::string &default_value) const; //Get multiple items int GetStringVector(std::vector *vals, const std::string &name) const; @@ -127,6 +129,9 @@ public: rc_t GetComponentSettings(std::map *results, const std::string &component_name) const; std::map GetComponentSettings(const std::string &component_name) const; + //Get logging info for a component. either mything.debug or mything.log.debug,mything.log.info, mything.log.warn + rc_t GetComponentLoggingSettings(bool *debug, bool *info, bool *warn, const std::string &component_name) const; + //Get all entries rc_t GetAllSettings(std::vector> *results) const; diff --git a/src/faodel-common/DirectoryInfo.cpp b/src/faodel-common/DirectoryInfo.cpp index 7e9dcfb..98d7527 100644 --- a/src/faodel-common/DirectoryInfo.cpp +++ b/src/faodel-common/DirectoryInfo.cpp @@ -18,26 +18,40 @@ namespace faodel { /** * @brief Configuration sometimes holds all info about a dirinfo in a url. Unpack it and turn into a direntry - * @param new_url In addition to normal resources, contains "num=" (childredn) and "ag0=0x123" for child 0's + * @param new_url In addition to normal resources, contains "num=" (members), "ag0=0x123" for child 0's, + * and "min_members" for minimum number of nodes for this to be functional */ -DirectoryInfo::DirectoryInfo(faodel::ResourceURL new_url) { +DirectoryInfo::DirectoryInfo(faodel::ResourceURL new_url) : min_members(0) { info = ExpandPunycode(new_url.GetOption("info")); new_url.RemoveOption("info"); + //Convert Min members option if available + string s_min_members = new_url.GetOption("min_members"); + new_url.RemoveOption("min_members"); + if(!s_min_members.empty()) { + int rc = faodel::StringToUInt32(&min_members, s_min_members); + if(rc != 0) { + KWARN("DirectoryInfo had parse error when extracting 'min_members' from url '"+new_url.GetFullURL()+"'"); + } + } + + //Extract number of included members string s = new_url.GetOption("num"); if(s!="") { new_url.RemoveOption("num"); - int64_t num_children; - int rc = faodel::StringToInt64(&num_children, s); + int64_t num_members; + int rc = faodel::StringToInt64(&num_members, s); if(rc==0) { - for(int64_t i=0; i0){ - for(auto &name_and_node : children){ + for(auto &name_and_node : members){ ss< children; //!< A list of all the nodes that are part of resource (name and nodeid) + faodel::ResourceURL url; //!< URL for entry. Should include refnode and bucket + std::string info; //!< Human text about what this is + uint32_t min_members; //!< Minimum number of members for this to be viable + std::vector members; //!< A list of all the nodes that are part of resource (name and nodeid) - DirectoryInfo() : url(), info(), children() {} + DirectoryInfo() : url(), info(), min_members(0), members() {} explicit DirectoryInfo(faodel::ResourceURL new_url); explicit DirectoryInfo(std::string s_url) @@ -36,7 +37,7 @@ public: } DirectoryInfo(std::string s_url, std::string s_info) - : url(faodel::ResourceURL(s_url)), info(s_info), children() { + : url(faodel::ResourceURL(s_url)), info(s_info), min_members(0), members() { url.RemoveOption("info"); } @@ -48,7 +49,8 @@ public: faodel::nodeid_t GetReferenceNode() { return url.reference_node; } bool Valid() const { return url.Valid(); } - bool IsEmpty() const; //True when no url, info, or children. Sometimes used to pass back "no info" + bool IsEmpty() const; //True when no url, info, or members. Sometimes used to pass back "no info" + bool MeetsMinimumSize() const { return members.size() >= min_members; } bool GetChildReferenceNode(const std::string &child_name, faodel::nodeid_t *reference_node= nullptr) const; @@ -61,14 +63,15 @@ public: bool ContainsNode(faodel::nodeid_t node) const; - void webhookInfo(faodel::ReplyStream &rs); + void whookieInfo(faodel::ReplyStream &rs); //Serialization hook template void serialize(Archive &ar, const unsigned int version){ ar & url; ar & info; - ar & children; + ar & min_members; + ar & members; } std::string to_string() const; diff --git a/src/faodel-common/LoggingInterface.cpp b/src/faodel-common/LoggingInterface.cpp index 86bf0ef..fbcc861 100644 --- a/src/faodel-common/LoggingInterface.cpp +++ b/src/faodel-common/LoggingInterface.cpp @@ -88,14 +88,7 @@ void LoggingInterface::ConfigureLogging(const Configuration &config) { //However, we also want to be able to be lazy and just tag a //component as being in debug mode. - //Allow user to do "component.debug" instead of "component.log.debug" - config.GetBool(&debug_enabled, component_name+".debug", "false"); - string default_setting=(debug_enabled) ? "true" : "false"; - - //But still trust log.debug as an override. - config.GetBool(&debug_enabled, component_name+".log.debug", default_setting); - config.GetBool(&info_enabled, component_name+".log.info", default_setting); - config.GetBool(&warn_enabled, component_name+".log.warn", default_setting); + config.GetComponentLoggingSettings(&debug_enabled, &info_enabled, &warn_enabled, component_name); #if Faodel_LOGGINGINTERFACE_DISABLED==0 && Faodel_LOGGINGINTERFACE_USE_SBL==1 string logfile; @@ -118,14 +111,7 @@ int LoggingInterface::GetLoggingLevelFromConfiguration(const Configuration &conf //However, we also want to be able to be lazy and just tag a //component as being in debug mode. bool dbg_enabled, nfo_enabled, wrn_enabled; - //Allow user to do "component.debug" instead of "component.log.debug" - config.GetBool(&dbg_enabled, component_name+".debug", "false"); - string default_setting=(dbg_enabled) ? "true" : "false"; - - //But still trust log.debug as an override. - config.GetBool(&dbg_enabled, component_name+".log.debug", default_setting); - config.GetBool(&nfo_enabled, component_name+".log.info", default_setting); - config.GetBool(&wrn_enabled, component_name+".log.warn", default_setting); + config.GetComponentLoggingSettings(&dbg_enabled, &nfo_enabled, &wrn_enabled, component_name); int loglevel = 0; if(dbg_enabled) loglevel = 0x01; diff --git a/src/faodel-common/NodeID.hh b/src/faodel-common/NodeID.hh index ce93c18..818d97c 100644 --- a/src/faodel-common/NodeID.hh +++ b/src/faodel-common/NodeID.hh @@ -21,7 +21,7 @@ namespace faodel { * FAODEL components often need a simple way to reference different * ranks that are running in the system. The nodeid_t provides a simple and * concise (64b) value for referencing other ranks. Internally, it is just - * the IP address and port number a particular rank uses for its Webhook + * the IP address and port number a particular rank uses for its Whookie * instance. Users are expected to pass the nodeid_t around by itself without * looking into its internal value. * diff --git a/src/faodel-common/README_Common.md b/src/faodel-common/README_Common.md index a4b6f49..bb93e1b 100644 --- a/src/faodel-common/README_Common.md +++ b/src/faodel-common/README_Common.md @@ -11,7 +11,7 @@ interest are: - bucket_t: Hashed value of a string, used for data namespaces - ResourceURL: String for referencing different system resources - DirectoryInfo: Provides a way to describe info about a FAODEL - resource (eg, url and info, plus any children of this resource). + resource (eg, url and info, plus any members of this resource). - **Configuration**: Configuration is a class for defining how to configure different components in a runtime. It uses a key/value diff --git a/src/faodel-common/ReplyStream.cpp b/src/faodel-common/ReplyStream.cpp index cb5eed0..7e9184d 100644 --- a/src/faodel-common/ReplyStream.cpp +++ b/src/faodel-common/ReplyStream.cpp @@ -302,6 +302,47 @@ void ReplyStream::mkList(const vector &entries, const string &label) { } +/** + * @brief Encode a link + * @param[in] name The human text for the item + * @param[in] link The html link for the item + * @param[in] link_is_important In TEXT mode, whether or not to include the http link + * @return The encoded string + */ +string ReplyStream::createLink(std::string name, std::string link, bool link_is_important) { + + switch (format) { + case ReplyStreamType::TEXT: + if(!link_is_important) return name; + return name + "[" + link + "]"; + break; + + case ReplyStreamType::HTML: + return html::mkLink(name,link); + + default: + cerr << "Unsupported format in ReplyStream\n"; + exit(-1); + } + +} + +/** + * @brief Add bold markings around some text + * @param text The input text to make bold + * @return The text with bold markings + */ +string ReplyStream::createBold(std::string text) { + switch (format) { + case ReplyStreamType::TEXT: return text; + case ReplyStreamType::HTML: return ""+text+""; + default: + cerr << "Unsupported format in ReplyStream\n"; + exit(-1); + } + +} + /** * @brief Close out a replystream (appends any footer markup) */ diff --git a/src/faodel-common/ReplyStream.hh b/src/faodel-common/ReplyStream.hh index f5ac3e3..eb610e8 100644 --- a/src/faodel-common/ReplyStream.hh +++ b/src/faodel-common/ReplyStream.hh @@ -9,15 +9,16 @@ #include #include #include +#include "faodel-common/NodeID.hh" namespace faodel { enum class ReplyStreamType { TEXT, HTML, JSON }; /** - * @brief Provides a way for hooks to pass results back to Webhook + * @brief Provides a way for hooks to pass results back to Whookie * - * Webhook needs a way for hooks to provide reply data back to users. A + * Whookie needs a way for hooks to provide reply data back to users. A * ReplyStream is a wrapper around stringstream that makes it easier * to append webpage structure. * @@ -45,6 +46,9 @@ public: void mkList( const std::vector &entries, const std::string &label=""); + + std::string createLink(std::string name, std::string link, bool link_is_important=false); + std::string createBold(std::string text); void Finish(); diff --git a/src/faodel-common/ResourceURL.cpp b/src/faodel-common/ResourceURL.cpp index 0f335eb..422cadd 100644 --- a/src/faodel-common/ResourceURL.cpp +++ b/src/faodel-common/ResourceURL.cpp @@ -12,22 +12,27 @@ // Format: resource_type:[bucket]/my/path/name&option1=x&option2=y - - //Example URLs -// peer:/my/path -// peer:/my/path&put=blocking,remote,remote2,local&get=remote -// peer:[mybucket]/my/path -// dht:[mybucket]/my/path&member_count=25&replication=1 -// local: - - - -// rm:<0x0123>[mybucket]/my/path -// dht:<0x0123>[0x1234]/my/path&num_nodes=4 - -// bucket: when prepended with 0x, treat the value as the actual hash. Otherwise hash it. - +// 1. peer:/my/path/mynodename +// You can use the peer type to label a specific node in the system. This +// is handy if you want to register ranks from one sim and use them in another +// +// 2. ref:/my/path/mydht +// If you know the name of a resource but not how its defined, you can +// issue a lookup to DirMan to retrieve its contents +// +// 3. local: +// In kelpie you can talk directly to your local kv. You can append options +// to customize how you want interface with it. +// +// 4. dht:[mybucket]/my/path/mydht&min_members=4&behavior=writetoremote_writetoiom_readtolocal&iom=myiom +// You can define a resource and add options to specify how it behaves. In +// this example we have a dht named /my/path/mydht that +// needs four member nodes to work. A Publish operation will write data to the +// remote node's memory and an associated iom named myiom. Wants/Needs will only +// cache the data at the requesting node. This example does not include +// the actual list of nodes in the pool. +// // Note: a root level item like "/myroot" has path="/" and name "myroot" // the same way "/a/b/c" has path "/a/b" and name "c" @@ -93,7 +98,10 @@ bool ResourceURL::IsFullURL() const { */ string ResourceURL::GetURL(bool include_type, bool include_node, bool include_bucket, bool include_options) const { stringstream ss; - if(include_type) ss<"; if(include_bucket) ss<<"["</my/path&num_nodes=4&replication=2 + * - dht:[mybucket]<0xAABB90>/my/path&min_members=4&replication=2 + * - dht:[mybucket]<0xAABB90>/my/dataset&min_members=2&num=2&ag0=0xAAB1&ag1=0xAAB2 * - peer:[mybucket]<0xAABB90>/nodes/my_server * - local: */ @@ -50,29 +51,32 @@ public: ResourceURL(std::string resource_type, nodeid_t reference_node, bucket_t bucket, std::string path, std::string name, std::string options) - : resource_type(resource_type), reference_node(reference_node), + : reference_node(reference_node), bucket(bucket), path((path.empty())?"/":path), name(name), - options(options) {} + options(options), + resource_type(resource_type) {} ~ResourceURL() override = default; - std::string resource_type; //eg ref, dht + std::string Type() const { return ((IsReference()) ? "ref" : resource_type); } nodeid_t reference_node; //The node that is the PoC for this resource bucket_t bucket; //hashed version [bucket] std::string path; //eg /root/rack0 std::string name; //eg mydht - std::string options; //eg member_count=25&replication=1 + std::string options; //eg min_members=16&replication=1 - bool Valid() const { return (!path.empty()) && (!name.empty());} //!< True if there is at least both a path and a name - bool IsRootLevel() const { return (path=="/"); } //!< True if this lives in the root directory (eg "/mything") + bool Valid() const { return (((!path.empty()) && (!name.empty())) || IsRoot());} //!< True if there is at least both a path and a name, or is the root + bool IsRootLevel() const { return (path=="/"); } //!< True if this lives in the root directory (eg "/mything") + bool IsRoot() const { return ((path=="/") && (name=="")); } //!< True if this is the root (ie, "/") bool IsFullURL() const; bool IsEmpty() const; + bool IsReference() const { return resource_type.empty(); } //!< True if this is a reference to a resource (ie ref:) rc_t SetURL( const std::string& url ); std::string GetURL(bool include_type=false, bool include_node=false, bool include_bucket=false, bool include_options=false) const; std::string GetPathName() const { return GetURL(false,false,false,false); } //!< Get the path/name: /root/rack0/mydht std::string GetBucketPathName() const { return GetURL(false,false,true,false); } //!< Get Bucket/path name: [a23]/root/rack0/mydht - std::string GetFullURL() const { return GetURL(true,true,true,true); } //!< Get full encoding "[a23]/root/rack0/mydht&num=2&thing=4" + std::string GetFullURL() const { return GetURL(true,true,true,true); } //!< Get full encoding "[a23]/root/rack0/mydht&min_members=2&thing=4" //Manipulate paths void PushDir(std::string next_dir); @@ -131,6 +135,8 @@ protected: private: + std::string resource_type; //eg ref, local, dht.. Empty means ref + }; diff --git a/src/faodel-common/StringHelpers.cpp b/src/faodel-common/StringHelpers.cpp index 65d5f91..e592f1f 100644 --- a/src/faodel-common/StringHelpers.cpp +++ b/src/faodel-common/StringHelpers.cpp @@ -106,6 +106,38 @@ bool IsValidIPString(const string &hostname) { return (!has_digits); //ignore if out-of-range digits } +/** + * @brief Convert a numerical string (eg "100" "4K") into an int32 value (eg 100, 4096) + * @param[out] val The output variable to set + * @param[in] name Input string to parse + * @retval 0 If input could be parsed + * @retval EINVAL If input string couldn't be parsed + */ +int StringToInt32(int32_t *val, const std::string &name) { + kassert(val,"Null val ptr handed to StringToInt32"); + int64_t val64; + int rc = StringToInt64(&val64, name); + if(rc!=0) return rc; + *val = (val64 & 0x0FFFFFFFFL); + return 0; +} + +/** + * @brief Convert a numerical string (eg "100" "4K") into an uint32 value (eg 100, 4096) + * @param[out] val The output variable to set + * @param[in] name Input string to parse + * @retval 0 If input could be parsed + * @retval EINVAL If input string couldn't be parsed + */ +int StringToUInt32(uint32_t *val, const std::string &name) { + kassert(val,"Null val ptr handed to StringToUInt32"); + uint64_t val64; + int rc = StringToUInt64(&val64, name); + if(rc!=0) return rc; + *val = (val64 & 0x0FFFFFFFFL); + return 0; +} + /** * @brief Convert a numerical string (eg "100" "4K") into an int64 value (eg 100, 4096) * @param[out] val The output variable to set diff --git a/src/faodel-common/StringHelpers.hh b/src/faodel-common/StringHelpers.hh index 1797a76..4effcc8 100644 --- a/src/faodel-common/StringHelpers.hh +++ b/src/faodel-common/StringHelpers.hh @@ -18,6 +18,8 @@ std::string MakePunycode(std::string const &s); std::string ExpandPunycode(std::string const &s); bool IsValidIPString(const std::string &hostname); +int StringToInt32(int32_t *val, const std::string &name); +int StringToUInt32(uint32_t *val, const std::string &name); int StringToInt64(int64_t *val, const std::string &name); int StringToUInt64(uint64_t *val, const std::string &name); int StringToPtr(void **val, const std::string &sval); diff --git a/src/faodel-services/CMakeLists.txt b/src/faodel-services/CMakeLists.txt index 8af2d42..a8a6031 100644 --- a/src/faodel-services/CMakeLists.txt +++ b/src/faodel-services/CMakeLists.txt @@ -14,7 +14,7 @@ set(SOURCES MPISyncStart.cpp ) -LIST( APPEND Services_imports webhook common ) +LIST( APPEND Services_imports whookie common ) if( Faodel_ENABLE_MPI_SUPPORT ) LIST( APPEND Services_imports MPI::MPI_CXX ) endif() diff --git a/src/faodel-services/MPISyncStart.cpp b/src/faodel-services/MPISyncStart.cpp index 5650ee3..fe6cb09 100644 --- a/src/faodel-services/MPISyncStart.cpp +++ b/src/faodel-services/MPISyncStart.cpp @@ -11,7 +11,7 @@ #include "faodel-services/MPISyncStart.hh" #include "faodel-common/Configuration.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #ifdef Faodel_ENABLE_MPI_SUPPORT #include @@ -72,7 +72,7 @@ void MPISyncStart::InitAndModifyConfiguration(Configuration *config) { } - nodeid_t my_id = webhook::Server::GetNodeID(); + nodeid_t my_id = whookie::Server::GetNodeID(); //See if we just need a barrier if((dirman_root_mpi==-1) && (dirman_resources_mpi.empty())) { @@ -84,7 +84,7 @@ void MPISyncStart::InitAndModifyConfiguration(Configuration *config) { //An MPI rank specified for the dirman root. Find the ID of the node if(dirman_root_mpi != -1) { - dbg("Dirman Root specified as rank "+std::to_string(dirman_root_mpi)+". Perform bcast to learn webhook root."); + dbg("Dirman Root specified as rank "+std::to_string(dirman_root_mpi)+". Perform bcast to learn whookie root."); kassert(dirman_root_mpi < mpi_size, "dirman.root_node_mpi value is larger than mpi ranks"); nodeid_t root_node = my_id; MPI_Bcast(&root_node, sizeof(nodeid_t), MPI_CHAR, dirman_root_mpi, MPI_COMM_WORLD); @@ -144,7 +144,7 @@ void MPISyncStart::GetBootstrapDependencies(string &name, vector &requires, vector &optional) const { name = "mpisyncstart"; - requires = {"webhook"}; + requires = {"whookie"}; optional = {}; } @@ -167,7 +167,7 @@ void MPISyncStart::Start() { std::string bootstrap() { static MPISyncStart mpisyncstart; - webhook::bootstrap(); + whookie::bootstrap(); faodel::bootstrap::RegisterComponent(&mpisyncstart, true); return "mpisyncstart"; } diff --git a/src/kelpie/CMakeLists.txt b/src/kelpie/CMakeLists.txt index 7f957a7..74aefa7 100644 --- a/src/kelpie/CMakeLists.txt +++ b/src/kelpie/CMakeLists.txt @@ -15,7 +15,6 @@ set( HEADERS_PUBLIC localkv/LocalKVCell.hh localkv/LocalKVRow.hh pools/Pool.hh - services/PoolServerDriver.hh ) set( HEADERS @@ -28,6 +27,7 @@ set( HEADERS ops/direct/msg_direct.hh ops/direct/OpKelpieGetBounded.hh ops/direct/OpKelpieGetUnbounded.hh + ops/direct/OpKelpieList.hh ops/direct/OpKelpieMeta.hh ops/direct/OpKelpiePublish.hh ioms/IomPosixIndividualObjects.hh @@ -35,8 +35,7 @@ set( HEADERS pools/LocalPool/LocalPool.hh pools/DHTPool/DHTPool.hh pools/PoolRegistry.hh - pools/UnconfiguredPool/UnconfiguredPool.hh - + pools/UnconfiguredPool/UnconfiguredPool.hh ) set( SOURCES @@ -59,6 +58,7 @@ set( SOURCES ops/direct/msg_direct.cpp ops/direct/OpKelpieGetBounded.cpp ops/direct/OpKelpieGetUnbounded.cpp + ops/direct/OpKelpieList.cpp ops/direct/OpKelpieMeta.cpp ops/direct/OpKelpiePublish.cpp pools/LocalPool/LocalPool.cpp @@ -67,24 +67,21 @@ set( SOURCES pools/PoolBase.cpp pools/PoolRegistry.cpp pools/UnconfiguredPool/UnconfiguredPool.cpp + ) if( Faodel_ENABLE_MPI_SUPPORT ) - LIST( APPEND HEADERS - pools/RFTPool/RFTPool.hh - services/PoolServerDriver.hh - ) - LIST( APPEND SOURCES - pools/RFTPool/RFTPool.cpp - services/PoolServerDriver.cpp - ) -endif( Faodel_ENABLE_MPI_SUPPORT ) + LIST( APPEND HEADERS pools/RFTPool/RFTPool.hh) + LIST( APPEND SOURCES pools/RFTPool/RFTPool.cpp) + LIST( APPEND HEADERS services/PoolServerDriver.hh) + LIST( APPEND SOURCES services/PoolServerDriver.cpp) +endif() LIST( APPEND Kelpie_imports dirman opbox ${NETLIB_TARGETS} - webhook common sbl + whookie common sbl Boost::program_options ) @@ -105,6 +102,13 @@ if( FAODEL_HAVE_HDF5 AND Faodel_ENABLE_IOM_HDF5 ) LIST( APPEND SOURCES ioms/IomHDF5.cpp ) endif() +if( FAODEL_HAVE_CASSANDRA AND Faodel_ENABLE_IOM_CASSANDRA ) + LIST( APPEND Kelpie_imports Faodel::Cassandra ) + LIST( APPEND Kelpie_extra_compile_defs "FAODEL_HAVE_CASSANDRA" ) + LIST( APPEND HEADERS ioms/IomCassandra.hh ) + LIST( APPEND SOURCES ioms/IomCassandra.cpp ) +endif() + # All things go in kelpie lib now add_library( kelpie ${HEADERS} ${SOURCES}) if( Kelpie_extra_compile_defs ) diff --git a/src/kelpie/Key.hh b/src/kelpie/Key.hh index 0ca8ecc..ceccd3a 100644 --- a/src/kelpie/Key.hh +++ b/src/kelpie/Key.hh @@ -32,7 +32,7 @@ namespace kelpie { * * @note Kelpie allows users to pass binary data in as key values. However, * users should be aware that keys with binary data will likely break - * printing functions in various places (eg webhook) + * printing functions in various places (eg whookie) */ class Key { public: diff --git a/src/kelpie/README_Kelpie.md b/src/kelpie/README_Kelpie.md index 2236a78..66a48eb 100644 --- a/src/kelpie/README_Kelpie.md +++ b/src/kelpie/README_Kelpie.md @@ -110,7 +110,7 @@ Kelpie has a few library dependencies: | --------------- | ----------------------------------- | | FAODEL:SBL | Uses logging capabilities for boost | | FAODEL:Common | Uses bootstrap and nodeid_t | -| FAODEL:WebHook | For status info and new connections | +| FAODEL:Whookie | For status info and new connections | | FAODEL:Lunasa | For network memory management | | FAODEL:OpBox | For communication state machines | | Network Lib | Either FAODEL:NNTI or libfabric | diff --git a/src/kelpie/common/Types.cpp b/src/kelpie/common/Types.cpp index c474cea..e981707 100644 --- a/src/kelpie/common/Types.cpp +++ b/src/kelpie/common/Types.cpp @@ -24,6 +24,15 @@ string availability_to_string(const Availability &a){ } } +/** + * @brief Clear out all data values in this data structure + */ +void kv_row_info_t::Wipe(){ + row_bytes=0; + num_cols_in_row=num_row_receiver_nodes=num_row_dependencies=0; + availability = Availability::Unavailable; +} + string kv_row_info_t::str(){ std::stringstream ss; ss<<"RowInfo: " @@ -42,6 +51,15 @@ void kv_row_info_t::ChangeAvailabilityFromLocalToRemote(){ availability = Availability::InRemoteMemory; } +/** + * @brief Clear out all data values in this data structure + */ +void kv_col_info_t::Wipe(){ + node_origin=faodel::NODE_UNSPECIFIED; + num_bytes=0; + num_col_receiver_nodes=num_col_dependencies=0; + availability = Availability::Unavailable; +} string kv_col_info_t::str(){ std::stringstream ss; @@ -91,20 +109,20 @@ pool_behavior_t PoolBehavior::ParseString(string parse_line) { pool_behavior_t f=0; for(auto &s : syms) { - if (s=="writetolocal") f |= PoolBehavior::WriteToLocal; - else if (s=="writetoremote") f |= PoolBehavior::WriteToRemote; - else if (s=="writetoiom") f |= PoolBehavior::WriteToIOM; - else if (s=="readtolocal") f |= PoolBehavior::ReadToLocal; - else if (s=="readtoremote") f |= PoolBehavior::ReadToRemote; - else if (s=="writearound") f |= PoolBehavior::WriteAround; - else if (s=="writeall") f |= PoolBehavior::WriteToAll; - else if (s=="readtonone") f |= PoolBehavior::ReadToNone; - else if (s=="defaultiom") f |= PoolBehavior::DefaultIOM; - else if (s=="defaultlocaliom") f |= PoolBehavior::DefaultLocalIOM; - else if (s=="defaultremoteiom") f |= PoolBehavior::DefaultRemoteIOM; + if (s=="writetolocal") f |= PoolBehavior::WriteToLocal; + else if (s=="writetoremote") f |= PoolBehavior::WriteToRemote; + else if (s=="writetoiom") f |= PoolBehavior::WriteToIOM; + else if (s=="readtolocal") f |= PoolBehavior::ReadToLocal; + else if (s=="readtoremote") f |= PoolBehavior::ReadToRemote; + else if (s=="writearound") f |= PoolBehavior::WriteAround; + else if (s=="writeall") f |= PoolBehavior::WriteToAll; + else if (s=="readtonone") f |= PoolBehavior::ReadToNone; + else if (s=="defaultiom") f |= PoolBehavior::DefaultIOM; + else if (s=="defaultlocaliom") f |= PoolBehavior::DefaultLocalIOM; + else if (s=="defaultremoteiom") f |= PoolBehavior::DefaultRemoteIOM; else if (s=="defaultcachingiom") f |= PoolBehavior::DefaultCachingIOM; else { - throw runtime_error("Unable to parse Action string token "+s+" inside "+parse_line); + throw runtime_error("Unable to parse behavior string token "+s+" inside "+parse_line); } } return f; @@ -112,12 +130,12 @@ pool_behavior_t PoolBehavior::ParseString(string parse_line) { std::string PoolBehavior::GetString(pool_behavior_t f) { vector names; - if(f & PoolBehavior:: WriteToLocal) names.push_back("WriteToLocal"); + if(f & PoolBehavior:: WriteToLocal) names.push_back("WriteToLocal"); if(f & PoolBehavior:: WriteToRemote) names.push_back("WriteToRemote"); - if(f & PoolBehavior:: WriteToIOM) names.push_back("WriteToIOM"); - if(f & PoolBehavior:: ReadToLocal) names.push_back("ReadToLocal"); - if(f & PoolBehavior:: ReadToRemote) names.push_back("ReadToRemote"); - if(f & PoolBehavior:: ReadToLocal) names.push_back("ReadToLocal"); + if(f & PoolBehavior:: WriteToIOM) names.push_back("WriteToIOM"); + if(f & PoolBehavior:: ReadToLocal) names.push_back("ReadToLocal"); + if(f & PoolBehavior:: ReadToRemote) names.push_back("ReadToRemote"); + if(f & PoolBehavior:: ReadToLocal) names.push_back("ReadToLocal"); string s = faodel::Join(names,' '); return s; diff --git a/src/kelpie/common/Types.hh b/src/kelpie/common/Types.hh index ab663e1..a5aa3ea 100644 --- a/src/kelpie/common/Types.hh +++ b/src/kelpie/common/Types.hh @@ -10,12 +10,15 @@ #include #include + #include "lunasa/DataObject.hh" #include "kelpie/Key.hh" +#include //For packing ObjectCapacities + -//Forward references.. plugg +//Forward references namespace faodel { class ResourceURL; } @@ -83,6 +86,7 @@ struct kv_row_info_t { uint32_t num_row_receiver_nodes; //!< How many nodes are waiting on updates to this row uint32_t num_row_dependencies; //!< How many actions are waiting if this row has activities Availability availability; //!< Where the row is available + void Wipe(); std::string str(); void ChangeAvailabilityFromLocalToRemote(); }; @@ -96,6 +100,7 @@ struct kv_col_info_t { uint32_t num_col_receiver_nodes; //!< How many nodes are waiting on updates to this column uint32_t num_col_dependencies; //!< How many local actions are waiting on this col Availability availability; //!< Whether item is available locally + void Wipe(); std::string str(); void ChangeAvailabilityFromLocalToRemote(); }; @@ -133,6 +138,25 @@ struct PoolBehavior { }; +class ObjectCapacities { +public: + std::vector keys; + std::vector capacities; + + void Append(const ObjectCapacities &other) { + keys.insert(keys.end(), other.keys.begin(), other.keys.end()); + capacities.insert(capacities.end(), other.capacities.begin(), other.capacities.end()); + } + //Serialization hook + template + void serialize(Archive &ar, const unsigned int version){ + ar & keys; + ar & capacities; + } +}; + + + //Pool callbacks using fn_publish_callback_t = std::function; using fn_want_callback_t = std::function; diff --git a/src/kelpie/core/KelpieCoreNoNet.cpp b/src/kelpie/core/KelpieCoreNoNet.cpp index 53380aa..f40534f 100644 --- a/src/kelpie/core/KelpieCoreNoNet.cpp +++ b/src/kelpie/core/KelpieCoreNoNet.cpp @@ -35,9 +35,9 @@ void KelpieCoreNoNet::init(const faodel::Configuration &config) { pool_registry.RegisterPoolConstructor("local", &LocalPoolCreate); pool_registry.RegisterPoolConstructor("lkv", &LocalPoolCreate); - //Register webhook - webhook::Server::updateHook("/kelpie", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + //Register whookie + whookie::Server::updateHook("/kelpie", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); } @@ -50,7 +50,7 @@ void KelpieCoreNoNet::start(){ void KelpieCoreNoNet::finish(){ pool_registry.finish(); iom_registry.finish(); - webhook::Server::deregisterHook("/kelpie"); + whookie::Server::deregisterHook("/kelpie"); } void KelpieCoreNoNet::RegisterPoolConstructor(std::string pool_name, fn_PoolCreate_t ctor_function) { @@ -65,7 +65,7 @@ void KelpieCoreNoNet::RegisterIomConstructor(std::string type, fn_IomConstructor iom_registry.RegisterIomConstructor(type, ctor_function); } -void KelpieCoreNoNet::HandleWebhookStatus(const std::map &args, std::stringstream &results) { +void KelpieCoreNoNet::HandleWhookieStatus(const std::map &args, std::stringstream &results) { faodel::ReplyStream rs(args, "Kelpie Status", &results); @@ -73,7 +73,7 @@ void KelpieCoreNoNet::HandleWebhookStatus(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); //InfoInterface function diff --git a/src/kelpie/core/KelpieCoreStandard.cpp b/src/kelpie/core/KelpieCoreStandard.cpp index 152a840..7df7e31 100644 --- a/src/kelpie/core/KelpieCoreStandard.cpp +++ b/src/kelpie/core/KelpieCoreStandard.cpp @@ -16,6 +16,8 @@ #include "kelpie/ops/direct/OpKelpiePublish.hh" #include "kelpie/ops/direct/OpKelpieGetBounded.hh" #include "kelpie/ops/direct/OpKelpieGetUnbounded.hh" +#include "kelpie/ops/direct/OpKelpieList.hh" + using namespace std; @@ -54,13 +56,16 @@ void KelpieCoreStandard::init(const faodel::Configuration &config) { opbox::RegisterOp(); opbox::RegisterOp(); opbox::RegisterOp(); + opbox::RegisterOp(); OpKelpieMeta::configure(faodel::internal_use_only, &lkv); OpKelpiePublish::configure(faodel::internal_use_only, &lkv); OpKelpieGetBounded::configure(faodel::internal_use_only, &lkv); OpKelpieGetUnbounded::configure(faodel::internal_use_only, &lkv); + OpKelpieList::configure(faodel::internal_use_only, &config, &lkv); + - webhook::Server::updateHook("/kelpie", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/kelpie", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); @@ -71,11 +76,12 @@ void KelpieCoreStandard::start(){ } void KelpieCoreStandard::finish(){ - webhook::Server::deregisterHook("/kelpie"); + whookie::Server::deregisterHook("/kelpie"); OpKelpieMeta::configure(faodel::internal_use_only, nullptr); //TODO: Would be nice to be a dummy lkv OpKelpiePublish::configure(faodel::internal_use_only, nullptr); //TODO: Would be nice to be a dummy lkv OpKelpieGetBounded::configure(faodel::internal_use_only, nullptr); //TODO: Would be nice to be a dummy lkv OpKelpieGetUnbounded::configure(faodel::internal_use_only, nullptr); //TODO: Would be nice to be a dummy lkv + OpKelpieList::configure(faodel::internal_use_only, nullptr, nullptr); pool_registry.finish(); iom_registry.finish(); } @@ -96,13 +102,13 @@ IomBase * KelpieCoreStandard::FindIOM(iom_hash_t iom_hash) { return iom_registry.Find(iom_hash); } -void KelpieCoreStandard::HandleWebhookStatus(const std::map &args, std::stringstream &results) { +void KelpieCoreStandard::HandleWhookieStatus(const std::map &args, std::stringstream &results) { faodel::ReplyStream rs(args, "Kelpie Status", &results); vector> stats; stats.push_back(pair("Core Type", GetType())); rs.mkTable(stats, "Kelpie Status"); - lkv.webhookInfo(rs); + lkv.whookieInfo(rs); rs.Finish(); } diff --git a/src/kelpie/core/KelpieCoreStandard.hh b/src/kelpie/core/KelpieCoreStandard.hh index f050346..819aa29 100644 --- a/src/kelpie/core/KelpieCoreStandard.hh +++ b/src/kelpie/core/KelpieCoreStandard.hh @@ -46,7 +46,7 @@ public: std::string GetType() const override { return "standard"; } void getLKV(LocalKV **localkv_ptr) override { *localkv_ptr = &lkv; } - void HandleWebhookStatus(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); //InfoInterface function void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/kelpie/core/Singleton.cpp b/src/kelpie/core/Singleton.cpp index 37368c8..9b153fe 100644 --- a/src/kelpie/core/Singleton.cpp +++ b/src/kelpie/core/Singleton.cpp @@ -55,7 +55,7 @@ void SingletonImpl::GetBootstrapDependencies( vector &optional) const { name = "kelpie"; requires = {"opbox","dirman"}; - optional = {"webhook"}; + optional = {"whookie"}; } diff --git a/src/kelpie/ioms/IomBase.hh b/src/kelpie/ioms/IomBase.hh index e5cb641..2bc474a 100644 --- a/src/kelpie/ioms/IomBase.hh +++ b/src/kelpie/ioms/IomBase.hh @@ -46,12 +46,14 @@ public: // Only keep settings that are valid for( auto&& s : valid_settings ) { auto found = new_settings.find( s ); - settings[s] = found == new_settings.end() ? "" : found->second; + if( found != new_settings.end() ) { + settings[s] = found->second; + } } }; - ~IomBase() override { } + virtual ~IomBase() override { } std::string Name() const { return name; } diff --git a/src/kelpie/ioms/IomCassandra.cpp b/src/kelpie/ioms/IomCassandra.cpp new file mode 100644 index 0000000..f495329 --- /dev/null +++ b/src/kelpie/ioms/IomCassandra.cpp @@ -0,0 +1,432 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#include +#include +#include +#include +#include + +#include "kelpie/ioms/IomCassandra.hh" + + + +namespace kelpie { + + namespace internal { + + constexpr char IomCassandra::type_str[]; + + // Convenience function to handle CassFuture* in std::cerr output + std::ostream& + operator<<( std::ostream& ostr, CassFuture* future ) + { + const char* msg; + size_t msg_len; + cass_future_error_message( future, &msg, &msg_len ); + ostr << std::string( msg, msg_len ); + return ostr; + } + + + IomCassandra::IomCassandra( const std::string& name, + const std::map< std::string, std::string >& new_settings ) + : IomBase( name, new_settings, {"endpoint","keyspace","table","teardown","cass-replication-class","cass-replication-factor"} ) + { + const char* msg; + size_t msg_len; + std::string cass_replication_class, cass_replication_factor; + + auto ai = settings.find( "endpoint" ); + if( ai == settings.end() ) { + throw std::runtime_error( "IOM " + name + " was not given a setting for 'endpoint'" ); + } + cluster_endpoint_ = ai->second; + + ai = settings.find( "keyspace" ); + keyspace_ = ( ai == settings.end() ) ? "faodel" : ai->second; + ai = settings.find( "table" ); + table_ = ( ai == settings.end() ) ? "ldo" : ai->second; + keyspace_table_ = keyspace_ + "." + table_; + + teardown_ = false; + ai = settings.find( "teardown" ); + if( ai != settings.end() ) { + teardown_ = ( ai->second == "true" + or ai->second == "yes" + or ai->second == "TRUE" + or ai->second == "YES" ); + } + + ai = settings.find( "cass-replication-class" ); + cass_replication_class = ( ai == settings.end() ) ? "SimpleStrategy" : ai->second; + ai = settings.find( "cass-replication-factor" ); + cass_replication_factor = ( ai == settings.end() ) ? "1" : ai->second; + + cluster_ = cass_cluster_new(); + session_ = cass_session_new(); + + cass_cluster_set_contact_points( cluster_, cluster_endpoint_.c_str() ); + + CassFuture* future = cass_session_connect( session_, cluster_ ); + cass_future_wait( future ); + if( cass_future_error_code( future ) != CASS_OK ) { + cass_future_error_message( future, &msg, &msg_len ); + throw std::runtime_error( std::string("Unable to connect to Cassandra cluster instance: ") + std::string( msg, msg_len ) ); + } + cass_future_free( future ); + + /* + * Set up the database keyspace and table if they do not already exist + */ + const std::string keyspace_create_cql_ = + "create keyspace if not exists " + + keyspace_ + + " with replication = {'class':" + + "'" + cass_replication_class + "'" + + ",'replication_factor':" + + cass_replication_factor +"};"; + CassStatement* stmt = cass_statement_new( keyspace_create_cql_.c_str(), 0 ); + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + if( cass_future_error_code( future ) not_eq CASS_OK ) { + cass_future_error_message( future, &msg, &msg_len ); + throw std::runtime_error( std::string( "IomCassandra: unable to create keyspace: " ) + std::string( msg, msg_len ) ); + } + cass_statement_free( stmt ); + cass_future_free( future ); + + const std::string table_create_cql_ = + "create table if not exists " + + keyspace_table_ + + " ( bucket text, key text, type tinyint, meta_size bigint, data_size bigint, payload blob, primary key ( bucket, key ) );"; + stmt = cass_statement_new( table_create_cql_.c_str(), 0 ); + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + if( cass_future_error_code( future ) not_eq CASS_OK ) { + cass_future_error_message( future, &msg, &msg_len ); + throw std::runtime_error( std::string( "IomCassandra: unable to create table: " ) + std::string( msg, msg_len ) ); + } + cass_statement_free( stmt ); + cass_future_free( future ); + } + + IomCassandra::~IomCassandra() { + + if( teardown_ ) { + /* sufficient to just drop the keyspace, that'll kill any contained tables */ + const std::string drop_keyspace_cql = "drop keyspace " + keyspace_ + ";"; + CassStatement* stmt = cass_statement_new( drop_keyspace_cql.c_str(), 0 ); + CassFuture* future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + if( cass_future_error_code( future ) not_eq CASS_OK ) { + std::cerr << "IomCassandra: unable to drop keyspace: " << future << std::endl; + } + cass_statement_free( stmt ); + cass_future_free( future ); + } + + cass_cluster_free( cluster_ ); + cass_session_free( session_ ); + } + + + /* + The Cassandra LDO table schema (table faodel.ldo): + const char* bucket + const char* key + cass_int8_t type + cass_int64_t meta_size + cass_int64_t data_size + const cass_byte_t* payload + */ + + void + IomCassandra::WriteObject( faodel::bucket_t bucket, + const kelpie::Key& key, + const lunasa::DataObject &ldo ) { + WriteObjects( bucket, { kvpair( key, ldo ) } ); + } + + + void + IomCassandra::WriteObjects( faodel::bucket_t bucket, + const std::vector< kvpair >& kvpairs ) + { + size_t wr_amt = 0; + CassError rc = CASS_OK; + CassStatement* stmt = nullptr; + CassFuture* fut = nullptr; + CassBatch* batch; + std::string insert_cql = "INSERT INTO " + keyspace_table_ + " (bucket, key, type, meta_size, data_size, payload) VALUES (?, ?, ?, ?, ?, ?);"; + + fut = cass_session_prepare( session_, insert_cql.c_str() ); + cass_future_wait( fut ); + rc = cass_future_error_code( fut ); + if( rc not_eq CASS_OK ) { + std::cerr << "Cassandra batch write preparation failed: " << fut << std::endl; + return; + } + + const CassPrepared* prep = cass_future_get_prepared( fut ); + cass_future_free( fut ); + + batch = cass_batch_new( CASS_BATCH_TYPE_LOGGED ); + + for( auto &&kv : kvpairs ) { + const kelpie::Key& key = kv.first; + const lunasa::DataObject& ldo = kv.second; + + stmt = cass_prepared_bind( prep ); + cass_statement_bind_string( stmt, 0, bucket.GetHex().c_str() ); + cass_statement_bind_string( stmt, 1, key.str().c_str() ); + cass_statement_bind_int8( stmt, 2, ldo.GetTypeID() ); + cass_statement_bind_int64( stmt, 3, ldo.GetMetaSize() ); + cass_statement_bind_int64( stmt, 4, ldo.GetDataSize() ); + cass_statement_bind_bytes( stmt, 5, reinterpret_cast( ldo.GetMetaPtr() ), ldo.GetUserSize() ); + wr_amt += ldo.GetUserSize(); + + cass_batch_add_statement( batch, stmt ); + cass_statement_free( stmt ); + } + + fut = cass_session_execute_batch( session_, batch ); + cass_future_wait( fut ); + rc = cass_future_error_code( fut ); + if( rc not_eq CASS_OK ) { + std::cerr << "Cassandra batch write failed: " << fut << std::endl; + } else { + stat_wr_requests += kvpairs.size(); + stat_wr_bytes += wr_amt; + } + + cass_future_free( fut ); + cass_batch_free( batch ); + } + + + rc_t + IomCassandra::ReadObject( faodel::bucket_t bucket, + const kelpie::Key &key, + lunasa::DataObject *ldo ) + { + rc_t krc = KELPIE_OK; + CassError crc = CASS_OK; + CassFuture* future = nullptr; + CassStatement* stmt = nullptr; + std::string select_cql = "SELECT bucket, key, type, meta_size, data_size, payload FROM " + keyspace_table_ + " WHERE bucket = ? AND key = ?"; + + stmt = cass_statement_new( select_cql.c_str(), 2 ); + cass_statement_bind_string( stmt, 0, bucket.GetHex().c_str() ); + cass_statement_bind_string( stmt, 1, key.str().c_str() ); + + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + crc = cass_future_error_code( future ); + if( crc not_eq CASS_OK ) { + std::cerr << "Cassandra read operation failed: " << future << std::endl; + } else { + cass_int64_t ds, ms; + int8_t ldo_type; + const cass_byte_t *buf; + size_t buf_size; + + const CassResult* result = cass_future_get_result( future ); + CassIterator* iterator = cass_iterator_from_result( result ); + if( cass_iterator_next( iterator ) ) { + const CassRow* row = cass_iterator_get_row( iterator ); + cass_value_get_int64( cass_row_get_column( row, 3 ), &ms ); + cass_value_get_int64( cass_row_get_column( row, 4 ), &ds ); + *ldo = lunasa::DataObject( ms, ds, lunasa::DataObject::AllocatorType::eager ); + cass_value_get_bytes( cass_row_get_column( row, 5 ), &buf, &buf_size ); + std::memcpy( ldo->GetMetaPtr(), buf, buf_size ); + cass_value_get_int8( cass_row_get_column( row, 2 ), &ldo_type ); + ldo->SetTypeID( ldo_type ); + stat_rd_requests++; + stat_rd_bytes += buf_size + sizeof( int8_t ) + ( 2 * sizeof( uint64_t ) ) + bucket.GetHex().size() + key.str().size(); + } else { + krc = KELPIE_ENOENT; + } + + cass_iterator_free( iterator ); + cass_result_free( result ); + } + + cass_future_free( future ); + cass_statement_free( stmt ); + return krc; + } + + void + IomCassandra::sstr( std::stringstream &ss, int depth, int index ) const + { + ss << std::string( index, ' ' ) + "IomCassandra cluster: " << cluster_endpoint_ << std::endl; + } + + void + IomCassandra::AppendWebInfo( faodel::ReplyStream rs, + const std::string reference_link, + const std::map< std::string, std::string > &args) + { + std::vector > items = + { + {"Setting", "Value"}, + {"Name", name}, + {"Cluster endpoint", cluster_endpoint_}, + }; + + rs.mkTable(items, "Basic Information"); + rs.tableBegin("Initial Configuration Parameters"); + rs.tableTop({"Setting", "Value"}); + for(auto &&nvpair : settings) { + rs.tableRow({nvpair.first, nvpair.second}); + } + rs.tableEnd(); + + auto ai = args.find("details"); + if(ai not_eq args.end() and ai->second == "true") { + CassError rc = CASS_OK; + CassStatement* stmt = nullptr; + CassFuture* future = nullptr; + + auto ai2 = args.find("bucket"); + if(ai2 == args.end() or ai2->second.empty()) { + // we were not given a bucket, list them all + std::string select_cql = "SELECT bucket FROM " + keyspace_table_; + + stmt = cass_statement_new( select_cql.c_str(), 0 ); + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + rc = cass_future_error_code( future ); + if( rc not_eq CASS_OK ) { + std::cerr << "AppendWebInfo: Cassandra SELECT failed: " << future << std::endl; + } else { + // Get the list of buckets + std::vector links; + const CassResult* result = cass_future_get_result( future ); + CassIterator* iterator = cass_iterator_from_result( result ); + while( cass_iterator_next( iterator ) ) { + const char* bucket_str; + size_t bucket_str_len; + + cass_value_get_string( cass_row_get_column( cass_iterator_get_row( iterator ), 0 ), + &bucket_str, + &bucket_str_len ); + std::string t( bucket_str, bucket_str_len ); + links.push_back("" + t + + ""); + + rs.mkList(links, "On-disk buckets"); + } + } + } else { + // iterate the bucket + const std::string& bucket = ai2->second; + std::string select_cql = "SELECT * FROM " + keyspace_table_ + " WHERE bucket = " + bucket; + stmt = cass_statement_new( select_cql.c_str(), 0 ); + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + rc = cass_future_error_code( future ); + if( rc not_eq CASS_OK ) { + std::cerr << "AppendWebInfo: Cassandra select failed: " << future << std::endl; + } else { + std::vector< std::pair < std::string, std::string > > blobs; + blobs.push_back( std::make_pair( "Key", "Size" ) ); + const CassResult* result = cass_future_get_result( future ); + CassIterator* iterator = cass_iterator_from_result( result ); + + while( cass_iterator_next( iterator ) ) { + const char* key_str; + cass_int64_t ds, ms; + size_t key_str_len; + const CassRow* row = cass_iterator_get_row( iterator ); + cass_value_get_string( cass_row_get_column( row, 2 ), &key_str, &key_str_len ); + cass_value_get_int64( cass_row_get_column( row, 3 ), &ms ); + cass_value_get_int64( cass_row_get_column( row, 4 ), &ds ); + + blobs.push_back( std::make_pair( std::string( key_str, key_str_len ), std::to_string( ms + ds ) ) ); + } + + rs.mkTable( blobs, "Objects in Bucket " + bucket ); + cass_iterator_free( iterator ); + cass_result_free( result ); + } + + cass_future_free( future ); + cass_statement_free( stmt ); + } + } + } + + + rc_t + IomCassandra::GetInfo( faodel::bucket_t bucket, const kelpie::Key& key, kv_col_info_t* col_info ) + { + rc_t krc; + CassError crc = CASS_OK; + CassFuture* future = nullptr; + CassStatement* stmt = nullptr; + std::string select_cql = "SELECT meta_size, data_size FROM " + keyspace_table_ + " WHERE bucket = ? AND key = ?"; + + // currently makes zero sense to call this with a null col_info pointer, so zero the struct + // if the pointer is not null and bail out otherwise + if( col_info not_eq nullptr ) { + col_info->Wipe(); + } else { + return KELPIE_EINVAL; + } + + stmt = cass_statement_new( select_cql.c_str(), 2 ); + cass_statement_bind_string( stmt, 0, bucket.GetHex().c_str() ); + cass_statement_bind_string( stmt, 1, key.str().c_str() ); + future = cass_session_execute( session_, stmt ); + cass_future_wait( future ); + crc = cass_future_error_code( future ); + if( crc not_eq CASS_OK ) { + std::cerr << "GetInfo: Cassandra select failed: " << future << std::endl; + } else { + const CassResult* result = cass_future_get_result( future ); + cass_int64_t ms, ds; + + if( cass_result_row_count not_eq 0 ) { + const CassRow* row = cass_result_first_row( result ); + cass_value_get_int64( cass_row_get_column( row, 0 ), &ms ); + cass_value_get_int64( cass_row_get_column( row, 1 ), &ds ); + col_info->num_bytes = ms + ds; + col_info->availability = kelpie::Availability::InDisk; + krc = KELPIE_OK; + } else { + col_info->availability = kelpie::Availability::Unavailable; + krc = KELPIE_ENOENT; + } + + cass_result_free( result ); + } + + cass_statement_free( stmt ); + cass_future_free( future ); + + return krc; + } + + } +} + + + + + + + + + + + + + diff --git a/src/kelpie/ioms/IomCassandra.hh b/src/kelpie/ioms/IomCassandra.hh new file mode 100644 index 0000000..671f50e --- /dev/null +++ b/src/kelpie/ioms/IomCassandra.hh @@ -0,0 +1,76 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#ifndef KELPIE_IOMCASSANDRA_HH +#define KELPIE_IOMCASSANDRA_HH + + +#include "cassandra.h" + +#include "kelpie/ioms/IomBase.hh" + +namespace kelpie { + namespace internal { + + class IomCassandra + : public IomBase, + public faodel::InfoInterface { + + public: + + using kvpair = std::pair< kelpie::Key, lunasa::DataObject >; + + IomCassandra() = delete; + + IomCassandra( const std::string& name, const std::map< std::string, std::string >& new_settings ); + + virtual ~IomCassandra(); + + rc_t GetInfo( faodel::bucket_t bucket, + const kelpie::Key &key, + kv_col_info_t *col_info ) override; + + void WriteObject( faodel::bucket_t bucket, + const kelpie::Key &key, + const lunasa::DataObject &ldo ) override; + + void WriteObjects( faodel::bucket_t bucket, + const std::vector< kvpair >& kvpairs ); + + rc_t ReadObject( faodel::bucket_t bucket, + const kelpie::Key& key, + lunasa::DataObject* ldo ) override; + + constexpr static char type_str[] = "cassandra"; + + std::string Type() const override { return IomCassandra::type_str; } + + void AppendWebInfo( faodel::ReplyStream rs, + std::string reference_link, + const std::map< std::string, std::string >& args ) override; + + void sstr( std::stringstream& ss, int depth = 0, int indent = 0 ) const override; + + + protected: + + std::string cluster_endpoint_; + std::string keyspace_; + std::string table_; + std::string keyspace_table_; + + bool teardown_; + + CassCluster* cluster_; + CassSession* session_; + + }; + + } // namespace internal +} // namespace kelpie + +#endif // KELPIE_IOMCASSANDRA_HH + + + diff --git a/src/kelpie/ioms/IomHDF5.cpp b/src/kelpie/ioms/IomHDF5.cpp index e61d0f9..00b27da 100644 --- a/src/kelpie/ioms/IomHDF5.cpp +++ b/src/kelpie/ioms/IomHDF5.cpp @@ -351,7 +351,7 @@ rc_t IomHDF5::GetInfo(faodel::bucket_t bucket, const kelpie::Key &key, kv_col_in rc_t rc; if(col_info not_eq nullptr) - std::memset(col_info, 0, sizeof *col_info); + col_info->Wipe(); std::string target = "/" + bucket.GetHex() + "/" + key.str(); diff --git a/src/kelpie/ioms/IomLevelDB.cpp b/src/kelpie/ioms/IomLevelDB.cpp index edfa0d0..ba46df0 100644 --- a/src/kelpie/ioms/IomLevelDB.cpp +++ b/src/kelpie/ioms/IomLevelDB.cpp @@ -219,7 +219,7 @@ rc_t IomLevelDB::GetInfo(faodel::bucket_t bucket, const kelpie::Key &key, kv_col std::string val; if(col_info not_eq nullptr) - std::memset(col_info, 0, sizeof *col_info); + col_info->Wipe(); leveldb::DB *db = bucketToDB(bucket); leveldb::Status s = db->Get(leveldb::ReadOptions(), key.str(), &val); diff --git a/src/kelpie/ioms/IomPosixIndividualObjects.cpp b/src/kelpie/ioms/IomPosixIndividualObjects.cpp index 4835602..33c646e 100644 --- a/src/kelpie/ioms/IomPosixIndividualObjects.cpp +++ b/src/kelpie/ioms/IomPosixIndividualObjects.cpp @@ -9,7 +9,6 @@ #include #include -#include //memset #include #include "faodel-common/StringHelpers.hh" @@ -79,8 +78,8 @@ rc_t IomPosixIndividualObjects::GetInfo(faodel::bucket_t bucket, const Key &key, rc_t rc; dbg("GetInfo for "+key.str()); if(col_info) - memset(col_info, 0, sizeof(*col_info)); - + col_info->Wipe(); + string fname = genBucketPathFile(bucket, key); struct stat sb; if((stat(fname.c_str(), &sb)==0) && (S_ISREG(sb.st_mode))){ diff --git a/src/kelpie/ioms/IomRegistry.cpp b/src/kelpie/ioms/IomRegistry.cpp index ccc4e84..a3df245 100644 --- a/src/kelpie/ioms/IomRegistry.cpp +++ b/src/kelpie/ioms/IomRegistry.cpp @@ -9,7 +9,7 @@ #include "faodel-common/MutexWrapper.hh" #include "faodel-common/StringHelpers.hh" #include "faodel-common/LoggingInterface.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "kelpie/ioms/IomRegistry.hh" @@ -20,6 +20,9 @@ #ifdef FAODEL_HAVE_HDF5 #include "kelpie/ioms/IomHDF5.hh" #endif +#ifdef FAODEL_HAVE_CASSANDRA +#include "kelpie/ioms/IomCassandra.hh" +#endif using namespace std; @@ -144,6 +147,13 @@ void IomRegistry::init(const faodel::Configuration &config) { RegisterIomConstructor( "hdf5", fn_hdf5 ); #endif +#ifdef FAODEL_HAVE_CASSANDRA + fn_IomConstructor_t fn_cassandra = [] (string name, const map< string, string > &settings) -> IomBase * { + return new IomCassandra( name, settings ); + }; + RegisterIomConstructor( "cassandra", fn_cassandra ); +#endif + //Get the list of Ioms this Configuration wants to use string s,role; @@ -182,8 +192,8 @@ void IomRegistry::init(const faodel::Configuration &config) { } } - webhook::Server::updateHook("/kelpie/iom_registry", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/kelpie/iom_registry", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); } @@ -194,7 +204,7 @@ void IomRegistry::init(const faodel::Configuration &config) { void IomRegistry::finish() { dbg("Finishing"); - webhook::Server::deregisterHook("kelpie/iom_registry"); + whookie::Server::deregisterHook("kelpie/iom_registry"); //Tell all ioms to shutdown (may trigger some close operations) for(auto &name_iomptr : ioms_by_hash_pre) { @@ -243,11 +253,11 @@ IomBase * IomRegistry::Find(iom_hash_t iom_hash) { } /** - * @brief Webhook for dumping info about known IOMs - * @param args Incoming webhook args + * @brief Whookie for dumping info about known IOMs + * @param args Incoming whookie args * @param results Results handed back */ -void IomRegistry::HandleWebhookStatus(const std::map &args, std::stringstream &results) { +void IomRegistry::HandleWhookieStatus(const std::map &args, std::stringstream &results) { auto ii=args.find("iom_name"); diff --git a/src/kelpie/ioms/IomRegistry.hh b/src/kelpie/ioms/IomRegistry.hh index 9fc672c..3aada57 100644 --- a/src/kelpie/ioms/IomRegistry.hh +++ b/src/kelpie/ioms/IomRegistry.hh @@ -61,7 +61,7 @@ private: std::map iom_ctors; - void HandleWebhookStatus(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); }; } // namespace internal diff --git a/src/kelpie/localkv/LocalKV.cpp b/src/kelpie/localkv/LocalKV.cpp index f9e25f5..7bc8a66 100644 --- a/src/kelpie/localkv/LocalKV.cpp +++ b/src/kelpie/localkv/LocalKV.cpp @@ -26,9 +26,9 @@ LocalKV::~LocalKV(){ //Caution: assumes lkv lives inside something like kelpieCore, which uses bootstrap to preserve shutdown // standalone tests of lkv must perform the same kind of order-preserving shutdown - webhook::Server::deregisterHook("/kelpie/lkv/cell"); - webhook::Server::deregisterHook("/kelpie/lkv/row"); - webhook::Server::deregisterHook("/kelpie/lkv"); + whookie::Server::deregisterHook("/kelpie/lkv/cell"); + whookie::Server::deregisterHook("/kelpie/lkv/row"); + whookie::Server::deregisterHook("/kelpie/lkv"); if(configured){ wipeAll(faodel::internal_use_only); @@ -57,15 +57,15 @@ rc_t LocalKV::Init(const Configuration &config){ table_mutex = config.GenerateComponentMutex("kelpie.lkv", "rwlock"); configured=true; - //Register webhooks - webhook::Server::updateHook("/kelpie/lkv", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + //Register whookies + whookie::Server::updateHook("/kelpie/lkv", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); - webhook::Server::updateHook("/kelpie/lkv/row", [this] (const map &args, stringstream &results) { - return HandleWebhookRow(args, results); + whookie::Server::updateHook("/kelpie/lkv/row", [this] (const map &args, stringstream &results) { + return HandleWhookieRow(args, results); }); - webhook::Server::updateHook("/kelpie/lkv/cell", [this] (const map &args, stringstream &results) { - return HandleWebhookCell(args, results); + whookie::Server::updateHook("/kelpie/lkv/cell", [this] (const map &args, stringstream &results) { + return HandleWhookieCell(args, results); }); return KELPIE_OK; @@ -406,6 +406,78 @@ rc_t LocalKV::drop(bucket_t bucket, const Key &key){ return rc; } +/** + * @brief Perform a search for keys that match a specific pattern + * @param[in] bucket The bucket id we should limit the search to + * @param[in] key_prefix The key to search for. Row and/or Key may end in '*' for prefix matching + * @param[out] opject_capacities The results in this localkv that matched the key_prefix + * @retval KELPIE_OK Found matches + * @retval KELPIE_ENOENT Did not find matches + */ +rc_t LocalKV::list(bucket_t bucket, const Key &key_prefix, + ObjectCapacities *object_capacities) { + + + kassert(key_prefix.valid(), "list given an invalid key"); + + dbg("List "+key_prefix.str()); + + bool row_wildcard = StringEndsWith(key_prefix.K1(), "*"); + bool found_items=false; + + if(!row_wildcard) { + //Exact row known. Search on the columns + + vector col_names; + + //Exact match on K1, partial match on K2 + /*rc_t rc =*/ doRowOp(bucket, key_prefix, + false, + nullptr, + [&key_prefix, &col_names, &object_capacities] (LocalKVRow &row, bool previously_existed) { + row.getActiveColumnNamesCapacities(key_prefix.K2(), &col_names, &object_capacities->capacities); + return KELPIE_OK; + }); + + //Convert row's column names into full keys + for(auto &col_name : col_names) { + object_capacities->keys.push_back(Key(key_prefix.K1(), col_name)); + } + found_items = (col_names.size()!=0); //Only report what we found in this function + + } else { + //Fuzzy Row Name + + //Build the full row name we'll use + string prefix = makeRowname(bucket, key_prefix.K1()); + prefix.erase(prefix.size()-1); //remove the trailing * + + + //Find all the row names that match and query each one + table_mutex->ReaderLock(); + for(auto name_rowptr = rows.lower_bound(prefix); name_rowptr!=rows.end(); ++name_rowptr) { + if(StringBeginsWith(name_rowptr->first, prefix)) { + vector col_names; + + LocalKVRow *row = name_rowptr->second; + row->lock(); + /*int num_found =*/ row->getActiveColumnNamesCapacities(key_prefix.K2(), &col_names, &object_capacities->capacities); + string row_name = row->rowname; + row->unlock(); + + //Append all keys for each column to the output + for(auto c : col_names) { + object_capacities->keys.push_back(Key(row_name, c)); + } + found_items |= (col_names.size()!=0); //Only report what we found in this function + } + } + table_mutex->Unlock(); + } + return (found_items) ? KELPIE_OK : KELPIE_ENOENT; +} + + /** * @brief Use a lambda to manipulate a column inside the localkv * @param[in] bucket The user id that is marked as the bucket of this data @@ -434,8 +506,8 @@ rc_t LocalKV::doColOp(bucket_t bucket, const Key &key, if(!create_if_missing){ //done: bail out - if(row_info) memset(row_info, 0, sizeof(kv_row_info_t)); - if(col_info) memset(col_info, 0, sizeof(kv_col_info_t)); + if(row_info) row_info->Wipe(); + if(col_info) col_info->Wipe(); return KELPIE_ENOENT; } @@ -487,7 +559,7 @@ rc_t LocalKV::doRowOp(bucket_t bucket, const Key &key, if(!create_if_missing){ //done: bail out - if(row_info) memset(row_info, 0, sizeof(kv_row_info_t)); + if(row_info) row_info->Wipe(); return KELPIE_ENOENT; } @@ -552,7 +624,7 @@ rc_t LocalKV::getRowInfo(bucket_t bucket, const Key &key, kv_row_info_t *row_inf } -string LocalKV::makeRowname(bucket_t bucket, const Key &key){ +string LocalKV::makeRowname(bucket_t bucket, const Key &key) { stringstream ss; ss << bucket.GetHex(); ss << key.K1(); @@ -603,12 +675,12 @@ void LocalKV::wipeAll(faodel::internal_use_only_t iuo){ * @param[in] args Key/Value list of args the user passed us * @param[in] results A stringstream for this function to write its results */ -void LocalKV::HandleWebhookStatus(const std::map &args, std::stringstream &results){ +void LocalKV::HandleWhookieStatus(const std::map &args, std::stringstream &results){ faodel::ReplyStream rs(args, "Kelpie LocalKV Status", &results); auto it = args.find("detail"); bool detailed = (it != args.end()); - webhookInfo(rs,detailed); + whookieInfo(rs,detailed); rs.Finish(); } @@ -618,7 +690,7 @@ void LocalKV::HandleWebhookStatus(const std::map &args, * @param[in] rs The ReplyStream to be written to * @param[in] detailed Whether to include detailed information or not */ -void LocalKV::webhookInfo(faodel::ReplyStream &rs, bool detailed){ +void LocalKV::whookieInfo(faodel::ReplyStream &rs, bool detailed){ rs.tableBegin("LocalKV"); rs.tableTop({"Parameter","Setting"}); @@ -707,7 +779,7 @@ void LocalKV::webhookInfo(faodel::ReplyStream &rs, bool detailed){ * @param[in] args Key/Value list of args the user passed us * @param[in] results A stringstream for this function to write its results */ -void LocalKV::HandleWebhookRow(const std::map &args, std::stringstream &results){ +void LocalKV::HandleWhookieRow(const std::map &args, std::stringstream &results){ faodel::ReplyStream rs(args, "Kelpie LocalKV Row", &results); string rname=""; @@ -767,7 +839,7 @@ void LocalKV::HandleWebhookRow(const std::map &args, st * @param[in] args Key/Value list of args the user passed us * @param[in] results A stringstream for this function to write its results */ -void LocalKV::HandleWebhookCell(const std::map &args, std::stringstream &results){ +void LocalKV::HandleWhookieCell(const std::map &args, std::stringstream &results){ faodel::ReplyStream rs(args, "Kelpie LocalKV Cell", &results); string rname=""; @@ -840,8 +912,8 @@ void LocalKV::sstr(stringstream &ss, int depth, int indent) const { ss << string(indent,' ') << "[LKV] Number of Rows: " << rows.size() <0){ - for(map::const_iterator ii=rows.begin(); ii!=rows.end(); ++ii){ - ss << string(indent+1,' ') << ii->first << " "; + for(map::const_iterator ii=rows.begin(); ii!=rows.end(); ++ii) { + //ss << string(indent+1,' ') << ii->first << " " <<" Number Cols: "<second->getNumCols()<second->sstr(ss, depth-1, indent+1); } } diff --git a/src/kelpie/localkv/LocalKV.hh b/src/kelpie/localkv/LocalKV.hh index 5fb3a65..d057ebf 100644 --- a/src/kelpie/localkv/LocalKV.hh +++ b/src/kelpie/localkv/LocalKV.hh @@ -20,8 +20,8 @@ #include "kelpie/localkv/LocalKVRow.hh" #include "kelpie/pools/Pool.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/DataObject.hh" @@ -108,6 +108,10 @@ public: //Drop a particular item rc_t drop(faodel::bucket_t bucket, const Key &key); + //Locate info about one or more keys + rc_t list(faodel::bucket_t bucket, const Key &key_prefix, + ObjectCapacities *object_capacities); + //Get info for a particular item rc_t getColInfo(faodel::bucket_t bucket, const Key &key, kv_col_info_t *col_info); rc_t getRowInfo(faodel::bucket_t bucket, const Key &key, kv_row_info_t *row_info); @@ -129,11 +133,11 @@ public: //Internal: Delete everything void wipeAll(faodel::internal_use_only_t iuo); - //Webhook helpers - void HandleWebhookStatus(const std::map &args, std::stringstream &results); - void HandleWebhookRow(const std::map &args, std::stringstream &results); - void HandleWebhookCell(const std::map &args, std::stringstream &results); - void webhookInfo(faodel::ReplyStream &rs, bool detailed=false); + //Whookie helpers + void HandleWhookieStatus(const std::map &args, std::stringstream &results); + void HandleWhookieRow(const std::map &args, std::stringstream &results); + void HandleWhookieCell(const std::map &args, std::stringstream &results); + void whookieInfo(faodel::ReplyStream &rs, bool detailed=false); //InfoInterface function void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/kelpie/localkv/LocalKVCell.cpp b/src/kelpie/localkv/LocalKVCell.cpp index a0c8994..6240794 100644 --- a/src/kelpie/localkv/LocalKVCell.cpp +++ b/src/kelpie/localkv/LocalKVCell.cpp @@ -187,21 +187,13 @@ void LocalKVCell::dispatchCallbacksAndNotifications(LocalKVRow *row, const Key & */ void LocalKVCell::sstr(stringstream &ss, int depth, int indent) const { int t = (int) time(NULL); - KTODO("Fix LDO capacity function"); //Fix LDO Capacity func ss << string(indent, ' ') - // << " Bytes: " << ldo.capacity() - //<< " InMem/Disk: " << ((in_memory)?"T":"F") << ((in_disk) ?"T":"F") + << " Bytes: " << getUserSize() << " Age: " << time_posted - t - << " SinceAccess: " << time_accessed - t; - - //if(depth>0){ - // char *tmp = ldo.GetDataPtr(); - // ss << " Data: 0x"; - // KTODO("Fix LDO capacity function"); //Fix ldo Capacity - // //for(size_t i=0; (i<16) && (iavailability=Availability::Unavailable; //just to make sure - } + if(col_info) col_info->Wipe(); return KELPIE_ENOENT; } } @@ -128,6 +125,13 @@ string LocalKVRow::getFirstColumnName(){ auto itr = cols.begin(); return itr->first; } + +size_t LocalKVRow::getFirstColumnUserSize() { + if(col_single) return col_single->getUserSize(); + auto itr = cols.begin(); + return itr->second->getUserSize(); +} + /** * @brief Search the row for a particular column. If it doesn't exist, create it * @param[in] key Key to look for (key.K2 is only part used) @@ -153,6 +157,47 @@ LocalKVCell * LocalKVRow::getOrCreateCol(const Key &key, bool *previously_existe } +/** + * @brief Perform a search for columns in this row that match a search string + * @param[in] search_string The column to search for. Will prefix match if string ends in '*' + * @param[out] names The column names that matched the search + * @param[out] capacities The LDO user capacities for the matching column(s) + * @return The number of matches found + */ +int LocalKVRow::getActiveColumnNamesCapacities(const string &search_string, vector *names, vector *capacities) { + + bool col_wildcard = StringEndsWith(search_string, "*"); + + if(!col_wildcard) { + //Find exact match + LocalKVCell *cell = getCol(search_string); + if(cell==nullptr) return 0; + if(names) names->push_back(search_string); + if(capacities) capacities->push_back(cell->getUserSize()); + return 1; + + } else { + //Do a wildcard search + int num_found=0; + + //Adjust the search name so it doesn't end in * + string prefix=search_string; + prefix.erase(prefix.size()-1); + + //Walk through all entries and see if we have a hit + for(auto name_colptr = cols.lower_bound(prefix); name_colptr!=cols.end(); ++name_colptr) { + if(name_colptr->second->getUserSize()) { //Only entries with data + if(StringBeginsWith(name_colptr->first, prefix)) { + if(names) names->push_back(name_colptr->first); + if(capacities) capacities->push_back(name_colptr->second->getUserSize()); + num_found++; + } + } + } + return num_found; + } +} + /** * @brief Insert a node into the waiting list for a particular cell. * @param[in] colname out column name @@ -296,14 +341,16 @@ void LocalKVRow::getInfo(kv_row_info_t *row_info){ */ void LocalKVRow::sstr(stringstream &ss, int depth, int indent) const { - ss << string(indent,' ')+"[Row] Name='" << rowname << "' Cols: " << cols.size() << endl; - if(col_single!=nullptr){ - ss << string(indent+1,' ') << "Colname=''"; + int num_cols = cols.size() + (col_single!=nullptr); + + ss << string(indent,' ')+"[Row] '" << rowname; + if(col_single!=nullptr) { + ss << string(indent,' ') << "[Col] ''"; if(depth>0) col_single->sstr(ss, depth-1, indent+1); else ss << endl; } for(auto &name_cellptr : cols){ - ss << string(indent+1,' ') << "Colname='" << name_cellptr.first << "'"; + ss << string(indent,' ') << "[Col] '" << name_cellptr.first << "'"; if(depth>0) name_cellptr.second->sstr(ss, depth-1, indent+1); else ss << endl; } diff --git a/src/kelpie/localkv/LocalKVRow.hh b/src/kelpie/localkv/LocalKVRow.hh index f4b7da9..ded6694 100644 --- a/src/kelpie/localkv/LocalKVRow.hh +++ b/src/kelpie/localkv/LocalKVRow.hh @@ -65,6 +65,8 @@ public: LocalKVCell * getCol(const Key &key); LocalKVCell * getCol(const std::string colname); std::string getFirstColumnName(); + size_t getFirstColumnUserSize(); + int getActiveColumnNamesCapacities(const std::string &search_string, std::vector *names, std::vector *capacities); private: LocalKVCell * getOrCreateCol(const Key &key, bool *previously_existed); diff --git a/src/kelpie/ops/direct/OpKelpieGetBounded.cpp b/src/kelpie/ops/direct/OpKelpieGetBounded.cpp index 3f73963..fc454e4 100644 --- a/src/kelpie/ops/direct/OpKelpieGetBounded.cpp +++ b/src/kelpie/ops/direct/OpKelpieGetBounded.cpp @@ -57,11 +57,10 @@ OpKelpieGetBounded::OpKelpieGetBounded( const iom_hash_t iom_hash, const pool_behavior_t behavior_flags, fn_opget_result_t cb_result) - : state(State::orig_getbounded_send), - bucket(bucket), key(key), peer(target_ptr), - cb_opget_result(cb_result), Op(true) { - - bool exceeds; + : Op(true), + state(State::orig_getbounded_send), peer(target_ptr), + bucket(bucket), key(key), + cb_opget_result(cb_result) { kassert(expected_ldo_user_size>0, "GetBounded op given a zero byte ldo?"); @@ -69,7 +68,7 @@ OpKelpieGetBounded::OpKelpieGetBounded( ldo_data = lunasa::DataObject(0, expected_ldo_user_size, lunasa::DataObject::AllocatorType::eager); //Create the outgoing message - exceeds = msg_direct_buffer_t::Alloc(ldo_msg, op_id, + msg_direct_buffer_t::Alloc(ldo_msg, op_id, DirectFlags::CMD_GET_BOUNDED, target_node, GetAssignedMailbox(), opbox::MAILBOX_UNSPECIFIED, bucket, key, iom_hash, behavior_flags, @@ -84,7 +83,7 @@ OpKelpieGetBounded::OpKelpieGetBounded( * @return OpKelpieGetBounded */ OpKelpieGetBounded::OpKelpieGetBounded(Op::op_create_as_target_t t) - : state(State::trgt_getbounded_start), ldo_msg(), Op(t) { + : Op(t), state(State::trgt_getbounded_start), ldo_msg() { //No work to do - done in target's state machine peer = 0; GetAssignedMailbox(); //For safety, get a mailbox. Not needed everywhere? diff --git a/src/kelpie/ops/direct/OpKelpieGetBounded.hh b/src/kelpie/ops/direct/OpKelpieGetBounded.hh index 5aab09f..aeaee6b 100644 --- a/src/kelpie/ops/direct/OpKelpieGetBounded.hh +++ b/src/kelpie/ops/direct/OpKelpieGetBounded.hh @@ -35,14 +35,14 @@ public: //Get item with a known size OpKelpieGetBounded( - const faodel::nodeid_t target_node, - const net::peer_ptr_t target_ptr, - const faodel::bucket_t bucket, - const Key &key, - const size_t expected_ldo_user_size, - const iom_hash_t iom_hash, - const pool_behavior_t behavior_flags, - fn_opget_result_t cb_result); + const faodel::nodeid_t target_node, + const net::peer_ptr_t target_ptr, + const faodel::bucket_t bucket, + const Key &key, + const size_t expected_ldo_user_size, + const iom_hash_t iom_hash, + const pool_behavior_t behavior_flags, + fn_opget_result_t cb_result); //A target starts off the same way no matter what command OpKelpieGetBounded(Op::op_create_as_target_t t); diff --git a/src/kelpie/ops/direct/OpKelpieGetUnbounded.cpp b/src/kelpie/ops/direct/OpKelpieGetUnbounded.cpp index a9a9eeb..d59e8ca 100644 --- a/src/kelpie/ops/direct/OpKelpieGetUnbounded.cpp +++ b/src/kelpie/ops/direct/OpKelpieGetUnbounded.cpp @@ -57,8 +57,8 @@ OpKelpieGetUnbounded::OpKelpieGetUnbounded( const pool_behavior_t behavior_flags, fn_opget_result_t cb_result) : state(State::orig_getunbounded_send), - bucket(bucket), key(key), peer(target_ptr), - cb_opget_result(cb_result), Op(true) { + Op(true), peer(target_ptr), bucket(bucket), key(key), + cb_opget_result(cb_result) { bool exceeds; @@ -78,7 +78,7 @@ OpKelpieGetUnbounded::OpKelpieGetUnbounded( * @return OpKelpieGetUnbounded */ OpKelpieGetUnbounded::OpKelpieGetUnbounded(Op::op_create_as_target_t t) - : state(State::trgt_getunbounded_start), ldo_msg(), Op(t) { + : Op(t), state(State::trgt_getunbounded_start), ldo_msg() { //No work to do - done in target's state machine peer = 0; GetAssignedMailbox(); //For safety, get a mailbox. Not needed everywhere? diff --git a/src/kelpie/ops/direct/OpKelpieList.cpp b/src/kelpie/ops/direct/OpKelpieList.cpp new file mode 100644 index 0000000..85a3019 --- /dev/null +++ b/src/kelpie/ops/direct/OpKelpieList.cpp @@ -0,0 +1,166 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#include +#include "OpKelpieList.hh" + +using namespace std; +using namespace faodel; +using namespace kelpie; + +//Statics: Standard id/name info for an op +const unsigned int OpKelpieList::op_id = const_hash("OpKelpieList"); +const string OpKelpieList::op_name = "OpKelpieList"; +bool OpKelpieList::debug_enabled = false; + +//Statics: This op has a static localkv pointer. lkv lives inside KelpieCore instance +LocalKV * OpKelpieList::lkv = nullptr; + +/** + * @brief Internal startup command for setting static variables + * + * @param[in] iuo Designates this function is for internal use only + * @param[in] new_lkv A pointer to the kelpie localkv we should uses_allocator + */ +void kelpie::OpKelpieList::configure(faodel::internal_use_only_t iuo, const faodel::Configuration *config, kelpie::LocalKV *new_lkv) { + lkv = new_lkv; + if(config) { + config->GetComponentLoggingSettings(&OpKelpieList::debug_enabled, nullptr, nullptr, "OpKelpieList"); + } +} + + + + +kelpie::OpKelpieList::OpKelpieList( + const vector> targets, + const faodel::bucket_t bucket, const kelpie::Key &search_key, + ObjectCapacities *object_capacities, + condition_variable *cv, int *num_targets_left) + + : Op(true), targets(targets), bucket(bucket), search_key(search_key), + user_object_capacities(object_capacities), + user_cv(cv), user_num_targets_left(num_targets_left), + state(State::orig_list_send) { + + + //Defer message creation until send state +} + +OpKelpieList::OpKelpieList(Op::op_create_as_target_t t) + : Op(t), state(State::trgt_list_start) { + GetAssignedMailbox(); //For safety, get a mailbox. Not needed everywhere? +} + + +kelpie::OpKelpieList::~OpKelpieList() { + if(state!=State::done){ + KTODO("Dtor when state not done"); + } +} + +WaitingType OpKelpieList::smo_List_Send() { + + //Remember how many nodes we have left + *user_num_targets_left = targets.size(); + if(*user_num_targets_left<1) { + dbg("Bail: op didn't have any targets"); + return updateStateDone(); + } + + auto mbox = GetAssignedMailbox(); + + for(auto &node_peerptr : targets) { + dbg("Sending to target "+node_peerptr.first.GetHex()); + + lunasa::DataObject ldo; + msg_direct_buffer_t::Alloc( + ldo, + op_id, DirectFlags::CMD_LIST, node_peerptr.first, + mbox, opbox::MAILBOX_UNSPECIFIED, + bucket, search_key, + 0, PoolBehavior::NoAction, //TODO: Add iom and behavior flags + nullptr); + net::SendMsg(node_peerptr.second, std::move(ldo)); + } + return updateState(State::orig_list_wait_for_results, WaitingType::waiting_on_cq); + +} + +WaitingType OpKelpieList::smt_List_Start(opbox::OpArgs *args) { + + + net::peer_ptr_t peer; + auto imsg = args->ExpectMessageOrDie(&peer); + search_key = imsg->ExtractKey(); + + dbg("Target1 received a request for "+search_key.str()); + + ObjectCapacities found_object_capacities; + + rc_t rc = lkv->list(imsg->bucket, search_key, &found_object_capacities); + uint16_t simple_rc = (rc==KELPIE_OK) ? 0 : 1; + + //Create a reply message. For simplicity just boost pack it in the payload + lunasa::DataObject ldo_out; + AllocateBoostReplyMessage(ldo_out, &imsg->hdr, simple_rc, found_object_capacities); + + net::SendMsg(peer, std::move(ldo_out)); + + return updateStateDone(); +} + +WaitingType OpKelpieList::smo_List_WaitForResults(opbox::OpArgs *args) { + + net::peer_ptr_t peer; + auto imsg = args->ExpectMessageOrDie(&peer); + + //Annoyingly, we extract to a temp struct and then append the users + auto found_object_capacities = UnpackBoostMessage(imsg); + user_object_capacities->Append(found_object_capacities); + + (*user_num_targets_left)--; + dbg("Origing received response. num_left="+std::to_string(*user_num_targets_left)); + + if(*user_num_targets_left<1) { + dbg("Received last item. Notifying user of result"); + user_cv->notify_one(); + return updateStateDone(); + } + + //More results expected. Stay here + return updateState(State::orig_list_wait_for_results, WaitingType::waiting_on_cq); +} + +WaitingType OpKelpieList::Update(OpArgs *args) { + dbg("Got an update. Processing state "+GetStateName()); + switch(state) { + case State::orig_list_send: return smo_List_Send(); + case State::trgt_list_start: return smt_List_Start(args); + case State::orig_list_wait_for_results: return smo_List_WaitForResults(args); + case State::done: return updateStateDone(); + } + KFAIL(); + return WaitingType::error; +} + +/** + * @brief Get a string name for the current state + * @retval string Human-readable name for state + */ +std::string OpKelpieList::GetStateName() const { + + switch(state) { + case State::orig_list_send: return "Origin-List-Send"; + case State::trgt_list_start: return "Target-List-Start"; + case State::orig_list_wait_for_results: return "Origin-List-WaitForResults"; + case State::done: return "Done"; + } + KFAIL(); +} + + + + + diff --git a/src/kelpie/ops/direct/OpKelpieList.hh b/src/kelpie/ops/direct/OpKelpieList.hh new file mode 100644 index 0000000..323e101 --- /dev/null +++ b/src/kelpie/ops/direct/OpKelpieList.hh @@ -0,0 +1,114 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#ifndef KELPIE_OPKELPIELIST_HH +#define KELPIE_OPKELPIELIST_HH + +#include + +#include "opbox/OpBox.hh" +#include "opbox/ops/OpHelpers.hh" +#include "lunasa/Lunasa.hh" +#include "lunasa/DataObject.hh" + +#include "kelpie/Kelpie.hh" +#include "kelpie/localkv/LocalKV.hh" + +#include "kelpie/ops/direct/msg_direct.hh" + +namespace kelpie { + +/** + * @brief An OpBox state machine for getting info for objects matching a key + */ +class OpKelpieList : public opbox::Op { + + //States + enum class State : int { + orig_list_send = 0, + trgt_list_start, + orig_list_wait_for_results, + done + }; + +public: + + OpKelpieList( + const std::vector> targets, + const faodel::bucket_t bucket, + const Key &search_key, + ObjectCapacities *object_capacities, + std::condition_variable *cv, + int *num_targets_left); + + //A target starts off the same way no matter what command + OpKelpieList(Op::op_create_as_target_t t); + + ~OpKelpieList() override; + + //Unique name and id for this op + const static unsigned int op_id; + const static std::string op_name; + static bool debug_enabled; //!< Dump debug messages + + unsigned int getOpID() const override { return op_id; } + std::string getOpName() const override { return op_name; } + + WaitingType Update(OpArgs *args) override; //Combined use + WaitingType UpdateOrigin(OpArgs *args) override { return WaitingType::error; } //Remove + WaitingType UpdateTarget(OpArgs *args) override { return WaitingType::error; } //Remove + + std::string GetStateName() const override; + + static void configure(faodel::internal_use_only_t iuo, const faodel::Configuration *config, LocalKV *new_lkv); + +private: + + //todo: put this in a standard form so it can be reused + #if Faodel_LOGGINGINTERFACE_DISABLED==0 + void dbg(std::string s) const { + if(OpKelpieList::debug_enabled) { + std::cout << "\033[1;31mD " << op_name << ":\033[0m " << (s) << std::endl; + } + } + #else + void dbg(std::string s) const {} + #endif + + + + static LocalKV *lkv; //Pointer back to the lkv, set at start time + + //These get used in the sender start state + std::vector> targets; + faodel::bucket_t bucket; + Key search_key; + + ObjectCapacities *user_object_capacities; + std::condition_variable *user_cv; //FIXME + int *user_num_targets_left; + + + State state; + + + //Origin/Target States (in order) + WaitingType smo_List_Send(); + WaitingType smt_List_Start(opbox::OpArgs *args); + WaitingType smo_List_WaitForResults(opbox::OpArgs *args); + + WaitingType updateState(State new_state, WaitingType waiting_conditions) { + state=new_state; + return waiting_conditions; + } + WaitingType updateStateDone(){ + state=State::done; + return WaitingType::done_and_destroy; + } + +}; + +} // namespace kelpie + +#endif //KELPIE_OPKELPIELIST_HH diff --git a/src/kelpie/ops/direct/OpKelpieMeta.cpp b/src/kelpie/ops/direct/OpKelpieMeta.cpp index ad51709..f0480d7 100644 --- a/src/kelpie/ops/direct/OpKelpieMeta.cpp +++ b/src/kelpie/ops/direct/OpKelpieMeta.cpp @@ -50,11 +50,12 @@ OpKelpieMeta::OpKelpieMeta( const iom_hash_t iom_hash, fn_publish_callback_t cb_result) : + Op(true), peer(target_ptr), - cb_info_result(cb_result), Op(true) { + cb_info_result(cb_result) { - bool exceeds = msg_direct_buffer_t::Alloc(ldo_msg, op_id, xferdirect_command, target_node, GetAssignedMailbox(), - opbox::MAILBOX_UNSPECIFIED, bucket, key, iom_hash, PoolBehavior::NoAction, nullptr); + msg_direct_buffer_t::Alloc(ldo_msg, op_id, xferdirect_command, target_node, GetAssignedMailbox(), + opbox::MAILBOX_UNSPECIFIED, bucket, key, iom_hash, PoolBehavior::NoAction, nullptr); state_after_start = State::orig_colinfo_wait_for_ack; @@ -70,7 +71,7 @@ OpKelpieMeta::OpKelpieMeta( * @return OpKelpieMeta */ OpKelpieMeta::OpKelpieMeta(Op::op_create_as_target_t t) - : state(State::trgt_meta_start), ldo_msg(), Op(t) { + : Op(t), state(State::trgt_meta_start), ldo_msg() { //No work to do - done in target's state machine peer = 0; diff --git a/src/kelpie/ops/direct/OpKelpiePublish.cpp b/src/kelpie/ops/direct/OpKelpiePublish.cpp index fd7f17a..f16e10b 100644 --- a/src/kelpie/ops/direct/OpKelpiePublish.cpp +++ b/src/kelpie/ops/direct/OpKelpiePublish.cpp @@ -50,9 +50,9 @@ OpKelpiePublish::OpKelpiePublish( const iom_hash_t iom_hash, const pool_behavior_t behavior_flags, fn_publish_callback_t cb_result) - : state(State::orig_pub_send), + : Op(true), state(State::orig_pub_send), peer(target_ptr), - cb_info_result(cb_result), Op(true) { + cb_info_result(cb_result) { ldo_data = ldo_users_data; //We need to keep a copy of the ldo until sent @@ -68,7 +68,7 @@ OpKelpiePublish::OpKelpiePublish( * @return OpKelpiePublish */ OpKelpiePublish::OpKelpiePublish(Op::op_create_as_target_t t) - : state(State::trgt_pub_start), ldo_msg(), Op(t) { + : Op(t), state(State::trgt_pub_start), ldo_msg(){ //No work to do - done in target's state machine peer = 0; diff --git a/src/kelpie/ops/direct/msg_direct.cpp b/src/kelpie/ops/direct/msg_direct.cpp index 3bb1bcf..da180ab 100644 --- a/src/kelpie/ops/direct/msg_direct.cpp +++ b/src/kelpie/ops/direct/msg_direct.cpp @@ -9,6 +9,8 @@ #include "kelpie/ops/direct/msg_direct.hh" +using namespace std; + namespace kelpie { @@ -150,4 +152,7 @@ msg_direct_status_t * msg_direct_status_t::Alloc( } + + + } // namespace kelpie diff --git a/src/kelpie/ops/direct/msg_direct.hh b/src/kelpie/ops/direct/msg_direct.hh index 07b1f43..0a2a1cc 100644 --- a/src/kelpie/ops/direct/msg_direct.hh +++ b/src/kelpie/ops/direct/msg_direct.hh @@ -55,12 +55,14 @@ namespace kelpie { */ struct DirectFlags { static constexpr uint16_t CMD_MASK = 0x00F0; + static constexpr uint16_t CMD_PUBLISH = 0x0090; static constexpr uint16_t CMD_GET_BOUNDED = 0x00A0; static constexpr uint16_t CMD_GET_UNBOUNDED = 0x00B0; static constexpr uint16_t CMD_GET_COLINFO = 0x00C0; static constexpr uint16_t CMD_DELETE = 0x00D0; + static constexpr uint16_t CMD_LIST = 0x00E0; static constexpr uint16_t CMD_STATUS_ACK = 0x0011; static constexpr uint16_t CMD_STATUS_NACK = 0x0010; @@ -79,6 +81,9 @@ struct DirectFlags { }; +//We use array[0] notation in structs, so we have to tell compiler it's ok +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" /** * @brief Message format for sending commands in Direct messages involving keys or rdma pointers @@ -132,7 +137,6 @@ struct msg_direct_buffer_t { }; - /** * @brief Provide a short status message back to a sender with row/col info * @note Call an Alloc function to create an appropriately-sized LDO and @@ -174,6 +178,25 @@ struct msg_direct_status_t { }; + + +struct msg_direct_list_result_t { + opbox::message_t hdr; //!< Standard header field + int remote_rc; + int num_entries; + char *packed_data; + + static msg_direct_list_result_t * Alloc( + lunasa::DataObject &new_ldo_ptr, + message_t *origin_msg_hdr, + std::vector &keys, + std::vector *capacities); + +}; + + +#pragma GCC diagnostic pop //For ignoring array[0] kinds of allocation + } // namespace kelpie #endif // KELPIE_MSG_DIRECT_HH diff --git a/src/kelpie/pools/DHTPool/DHTPool.cpp b/src/kelpie/pools/DHTPool/DHTPool.cpp index 314fcb2..df9c1d3 100644 --- a/src/kelpie/pools/DHTPool/DHTPool.cpp +++ b/src/kelpie/pools/DHTPool/DHTPool.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "faodel-common/Common.hh" #include "faodel-common/Debug.hh" @@ -39,13 +40,13 @@ DHTPool::DHTPool(const ResourceURL &pool_url) } //Allocate peers and connect to each one - for(int i=0; i(dir_info.children[i].node, peer)); + nodes.push_back(pair(dir_info.members[i].node, peer)); } //Pull out iom info in order to associate the iom with this local pool. While iom option was @@ -243,6 +244,10 @@ rc_t DHTPool::Need(const Key &key, size_t expected_ldo_user_bytes, lunasa::DataO }); + if(rc!=KELPIE_OK){ + KTODO("DHTPool could not issue Need"); + } + while(!is_found) cv.wait(lk); @@ -294,7 +299,7 @@ rc_t DHTPool::Info(const Key &key, kv_col_info_t *col_info){ if(result==0){ *col_info = ci; } else { - memset(col_info, 0, sizeof(kv_col_info_t)); + col_info->Wipe(); } } got_result=true; @@ -351,6 +356,37 @@ rc_t DHTPool::Drop(const Key &key) { return KELPIE_TODO; } + +/** + * @brief Perform a search for keys that match a specific pattern + * @param[in] search_key The key to search for. Row and/or Key may end in '*' for prefix matching + * @param[out] object_capacities Info about the objects that match this key search + * @retval KELPIE_OK Found matches + * @retval KELPIE_ENOENT Did not find matches + */ +rc_t DHTPool::List(const kelpie::Key &search_key, ObjectCapacities *object_capacities) { + + dbg("List key "+search_key.str()); + + //No local search - always goes out to nodes + + //Wasn't available locally. Prepare to do some messaging + std::mutex m; + std::condition_variable cv; + std::unique_lock lk(m); + int num_targets_left=nodes.size(); + + opbox::LaunchOp(new OpKelpieList(nodes, default_bucket, search_key, object_capacities, + &cv, &num_targets_left)); + + while(num_targets_left) { + cv.wait(lk); + } + + return KELPIE_OK; + +} + /** * @brief Use the key's row info to determine which node is responsible for the data * @param key The Key label for the blob (only ROW portion used) diff --git a/src/kelpie/pools/DHTPool/DHTPool.hh b/src/kelpie/pools/DHTPool/DHTPool.hh index 5eca56c..6394249 100644 --- a/src/kelpie/pools/DHTPool/DHTPool.hh +++ b/src/kelpie/pools/DHTPool/DHTPool.hh @@ -63,6 +63,7 @@ public: rc_t Info(const Key &key, kv_col_info_t *col_info) override; rc_t RowInfo(const Key &key, kv_row_info_t *row_info) override; rc_t Drop(const Key &key) override; + rc_t List(const Key &search_key, ObjectCapacities *object_capacities=nullptr) override; int FindTargetNode(const Key &key, faodel::nodeid_t *node_id=nullptr, net::peer_ptr_t *peer_ptr=nullptr) override; diff --git a/src/kelpie/pools/LocalPool/LocalPool.cpp b/src/kelpie/pools/LocalPool/LocalPool.cpp index 9adcfc6..384d844 100644 --- a/src/kelpie/pools/LocalPool/LocalPool.cpp +++ b/src/kelpie/pools/LocalPool/LocalPool.cpp @@ -237,6 +237,20 @@ rc_t LocalPool::Drop(const Key &key){ return lkv->drop(default_bucket, key); } +/** + * @brief Perform a search for keys that match a specific pattern + * @param[in] bucket The bucket id we should limit the search to + * @param[in] key_prefix The key to search for. Row and/or Key may end in '*' for prefix matching + * @param[out] object_capacities Info about the objects that match this key search + * @retval KELPIE_OK Found matches + * @retval KELPIE_ENOENT Did not find matches + */ +rc_t LocalPool::List(const kelpie::Key &search_key, ObjectCapacities *object_capacities) { + dbg("List key "+search_key.str()); + + return lkv->list(default_bucket, search_key, object_capacities); +} + /** * @brief Use the key's row info to determine which node is responsible for the data * @param key The Key label for the blob (only ROW portion used) diff --git a/src/kelpie/pools/LocalPool/LocalPool.hh b/src/kelpie/pools/LocalPool/LocalPool.hh index 8b02f26..e07136e 100644 --- a/src/kelpie/pools/LocalPool/LocalPool.hh +++ b/src/kelpie/pools/LocalPool/LocalPool.hh @@ -52,6 +52,7 @@ public: rc_t Info(const Key &key, kv_col_info_t *col_info) override; rc_t RowInfo(const Key &key, kv_row_info_t *row_info) override; rc_t Drop(const Key &key) override; + rc_t List(const Key &search_key, ObjectCapacities *object_capacities=nullptr) override; int FindTargetNode(const Key &key, faodel::nodeid_t *node_id, net::peer_ptr_t *peer_ptr) override; diff --git a/src/kelpie/pools/Pool.cpp b/src/kelpie/pools/Pool.cpp index cce1abc..3ca6f14 100644 --- a/src/kelpie/pools/Pool.cpp +++ b/src/kelpie/pools/Pool.cpp @@ -187,6 +187,18 @@ rc_t Pool::RowInfo(const Key &key, kv_row_info_t *row_info){ rc_t Pool::Drop(const Key &key) { return impl->Drop(key); } + +/** + * @brief Perform a search for keys in this pool that match a specific pattern + * @param[in] search_key The key to search for. Row and/or Key may end in '*' for prefix matching + * @param[out] object_capacities Info about the objects that match this key search + * @retval KELPIE_OK Found matches + * @retval KELPIE_ENOENT Did not find matches + */ +rc_t Pool::List(const Key &search_key, ObjectCapacities *object_capacities) { + return impl->List(search_key, object_capacities); +} + /** * @brief Locate info about the node in the pool that is responsible for hosting this key * @param key The key of the item that needs to be found diff --git a/src/kelpie/pools/Pool.hh b/src/kelpie/pools/Pool.hh index d1e0d15..66fcb00 100644 --- a/src/kelpie/pools/Pool.hh +++ b/src/kelpie/pools/Pool.hh @@ -54,8 +54,9 @@ public: rc_t Info(const Key &key, kv_col_info_t *col_info); rc_t RowInfo(const Key &key, kv_row_info_t *row_info); - rc_t Drop(const Key &key); + rc_t Drop(const Key &key); + rc_t List(const Key &search_key, ObjectCapacities *object_capacities=nullptr); //Locate where this pool would store the data int FindTargetNode(const Key &key, faodel::nodeid_t *node_id=nullptr, net::peer_ptr_t *peer_ptr=nullptr); diff --git a/src/kelpie/pools/PoolBase.cpp b/src/kelpie/pools/PoolBase.cpp index 8b9b921..9f1dc24 100644 --- a/src/kelpie/pools/PoolBase.cpp +++ b/src/kelpie/pools/PoolBase.cpp @@ -21,7 +21,7 @@ PoolBase::PoolBase(const faodel::ResourceURL &pool_url) behavior_flags(PoolBehavior::DefaultBaseClass) { //Unconfigure pool is empty - if(pool_url.resource_type == "unconfigured") return; + if(pool_url.Type() == "unconfigured") return; kelpie::internal::getLKV(&lkv); diff --git a/src/kelpie/pools/PoolBase.hh b/src/kelpie/pools/PoolBase.hh index b3f3ae6..805a0e0 100644 --- a/src/kelpie/pools/PoolBase.hh +++ b/src/kelpie/pools/PoolBase.hh @@ -56,8 +56,11 @@ public: virtual rc_t Info(const Key &key, kv_col_info_t *col_info) = 0; virtual rc_t RowInfo(const Key &key, kv_row_info_t *row_info) = 0; + virtual rc_t Drop(const Key &key) = 0; + virtual rc_t List(const Key &search_key, ObjectCapacities *object_capacities=nullptr) = 0; + virtual int FindTargetNode(const Key &key, faodel::nodeid_t *node_id, net::peer_ptr_t *peer_ptr) = 0; virtual std::string TypeName() const = 0; diff --git a/src/kelpie/pools/PoolRegistry.cpp b/src/kelpie/pools/PoolRegistry.cpp index f3679e1..b1fcf78 100644 --- a/src/kelpie/pools/PoolRegistry.cpp +++ b/src/kelpie/pools/PoolRegistry.cpp @@ -33,8 +33,8 @@ void PoolRegistry::init(const faodel::Configuration &config) { config.GetDefaultSecurityBucket(&default_bucket); default_pool_logging_level = LoggingInterface::GetLoggingLevelFromConfiguration(config, "kelpie.pool"); - webhook::Server::updateHook("/kelpie/pool_registry", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/kelpie/pool_registry", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); } @@ -43,7 +43,7 @@ void PoolRegistry::start(){ } void PoolRegistry::finish() { - webhook::Server::deregisterHook("/kelpie/pool_registry"); + whookie::Server::deregisterHook("/kelpie/pool_registry"); pool_create_fns.clear(); mutex->Lock(); @@ -71,9 +71,9 @@ Pool PoolRegistry::Connect(const ResourceURL &pool_url){ src_url.bucket = default_bucket; } - //cout <<"Input url is '"<Unlock(); throw std::runtime_error("Pool registry could not find ctor for pool "+src_url.GetURL()); @@ -140,7 +140,7 @@ Pool PoolRegistry::Connect(const ResourceURL &pool_url){ -void PoolRegistry::HandleWebhookStatus(const std::map &args, std::stringstream &results) { +void PoolRegistry::HandleWhookieStatus(const std::map &args, std::stringstream &results) { faodel::ReplyStream rs(args, "Kelpie Pool Registry", &results); vector> stats; @@ -175,7 +175,7 @@ void PoolRegistry::HandleWebhookStatus(const std::map & row.push_back(to_string(url_bptr.second.use_count())); auto di = url_bptr.second->GetDirectoryInfo(); - row.push_back(std::to_string(di.children.size())); + row.push_back(std::to_string(di.members.size())); row.push_back(di.info); row.push_back(url_bptr.first); existing_pools.push_back(row); diff --git a/src/kelpie/pools/PoolRegistry.hh b/src/kelpie/pools/PoolRegistry.hh index c860da3..19a10b4 100644 --- a/src/kelpie/pools/PoolRegistry.hh +++ b/src/kelpie/pools/PoolRegistry.hh @@ -50,7 +50,7 @@ class PoolRegistry { std::string makeKnownPoolKey(const faodel::ResourceURL &url) const { return url.bucket.GetHex()+url.path+"/"+url.name+"&"+url.GetSortedOptions(); } - void HandleWebhookStatus(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); }; } // namespace internal diff --git a/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.cpp b/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.cpp index 4e2c0ea..9e13c27 100644 --- a/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.cpp +++ b/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.cpp @@ -8,6 +8,8 @@ #include "kelpie/pools/UnconfiguredPool/UnconfiguredPool.hh" +using namespace std; + namespace kelpie { UnconfiguredPool::UnconfiguredPool() @@ -22,12 +24,14 @@ rc_t UnconfiguredPool::Publish(const Key &key, const lunasa::DataObject &user_ld rc_t UnconfiguredPool::Want(const Key &key, size_t expected_ldo_user_bytes, fn_want_callback_t callback) { Panic("Want"); return KELPIE_OK; } rc_t UnconfiguredPool::Need(const Key &key, size_t expected_ldo_user_bytes, - lunasa::DataObject *user_ldo) { Panic("Need"); return KELPIE_OK; } + lunasa::DataObject *user_ldo) { Panic("Need"); return KELPIE_OK; } rc_t UnconfiguredPool::Info(const Key &key, kv_col_info_t *col_info) { Panic("Info"); return KELPIE_OK; } rc_t UnconfiguredPool::RowInfo(const Key &key, kv_row_info_t *row_info) { Panic("RowInfo"); return KELPIE_OK; } rc_t UnconfiguredPool::Drop(const Key &key) { Panic("Drop"); return KELPIE_OK; } +rc_t UnconfiguredPool::List(const Key &search_key, ObjectCapacities *capacities) { Panic("List"); return KELPIE_OK; } + int UnconfiguredPool::FindTargetNode(const Key &key, faodel::nodeid_t *node_id, - net::peer_ptr_t *peer_ptr) { Panic("FindTargetNode"); return 0; } + net::peer_ptr_t *peer_ptr) { Panic("FindTargetNode"); return 0; } void UnconfiguredPool::Panic(std::string caller) const { std::stringstream ss; diff --git a/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.hh b/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.hh index 6a5ccac..c242755 100644 --- a/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.hh +++ b/src/kelpie/pools/UnconfiguredPool/UnconfiguredPool.hh @@ -49,6 +49,7 @@ public: rc_t Info(const Key &key, kv_col_info_t *col_info) override; rc_t RowInfo(const Key &key, kv_row_info_t *row_info) override; rc_t Drop(const Key &key) override; + rc_t List(const Key &search_key, ObjectCapacities *object_capacities=nullptr) override; int FindTargetNode(const Key &key, faodel::nodeid_t *node_id=nullptr, net::peer_ptr_t *peer_ptr=nullptr) override; std::string TypeName() const override { return "unconfigured"; } diff --git a/src/kelpie/services/PoolServerDriver.cpp b/src/kelpie/services/PoolServerDriver.cpp index 40aae2c..0f0c0f7 100644 --- a/src/kelpie/services/PoolServerDriver.cpp +++ b/src/kelpie/services/PoolServerDriver.cpp @@ -11,8 +11,8 @@ #include #include "faodel-common/Common.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" #include "opbox/OpBox.hh" #include "dirman/DirMan.hh" @@ -35,9 +35,9 @@ dirman.root_node_mpi 0 PoolServerDriver::PoolServerDriver(int argc, char **argv) : op_desc("options"), - argc(argc), argv(argv) {}; + argc(argc), argv(argv) {} -PoolServerDriver::~PoolServerDriver() {}; +PoolServerDriver::~PoolServerDriver() {} void PoolServerDriver::command_line_options() { @@ -108,12 +108,12 @@ void PoolServerDriver::start_dirman() { faodel::bootstrap::Start(config, kelpie::bootstrap); - webhook_killswitch(); + whookie_killswitch(); } -void PoolServerDriver::webhook_killswitch() { - webhook::Server::registerHook("/killme", +void PoolServerDriver::whookie_killswitch() { + whookie::Server::registerHook("/killme", [&](const std::map &args, std::stringstream &results) { keep_going = false; } @@ -124,9 +124,9 @@ void PoolServerDriver::stop_dirman() { faodel::DirectoryInfo dirinfo(pool_url_string, pool_info); if(mpi_rank == dirroot_rank_) { dirman::GetRemoteDirectoryInfo(faodel::ResourceURL(pool_url_string), &dirinfo); - for(auto &&name_node : dirinfo.children) + for(auto &&name_node : dirinfo.members) if(name_node.name not_eq "root") - webhook::retrieveData(name_node.node, "/killme", nullptr); + whookie::retrieveData(name_node.node, "/killme", nullptr); } MPI_Barrier(MPI_COMM_WORLD); diff --git a/src/kelpie/services/PoolServerDriver.hh b/src/kelpie/services/PoolServerDriver.hh index 4f3e1c6..182cb9f 100644 --- a/src/kelpie/services/PoolServerDriver.hh +++ b/src/kelpie/services/PoolServerDriver.hh @@ -34,7 +34,7 @@ protected: void stop_dirman(); - void webhook_killswitch(); + void whookie_killswitch(); opts::options_description op_desc; opts::variables_map op_map; diff --git a/src/lunasa/CMakeLists.txt b/src/lunasa/CMakeLists.txt index 96ac2bb..cb32b18 100644 --- a/src/lunasa/CMakeLists.txt +++ b/src/lunasa/CMakeLists.txt @@ -46,10 +46,10 @@ if( Faodel_ENABLE_TCMALLOC ) #add_definitions(-DFaodel_ENABLE_TCMALLOC) endif() -# This list has to explicitly contain webhook, common, and sbl +# This list has to explicitly contain whookie, common, and sbl # This is because NETLIB_TARGETS might end up being libfabric and friends, -# and in that case we don't get webhook et al. through the nnti target. -LIST( APPEND Lunasa_IMPORTS webhook common sbl ${FaodelNetlib_TARGETS} ) +# and in that case we don't get whookie et al. through the nnti target. +LIST( APPEND Lunasa_IMPORTS whookie common sbl ${FaodelNetlib_TARGETS} ) if( Faodel_ENABLE_TCMALLOC ) LIST( APPEND Lunasa_IMPORTS tcmalloc spinlock ) endif() diff --git a/src/lunasa/DataObject.cpp b/src/lunasa/DataObject.cpp index d41fd43..a37e166 100644 --- a/src/lunasa/DataObject.cpp +++ b/src/lunasa/DataObject.cpp @@ -101,6 +101,17 @@ DataObject::DataObject(uint32_t dataCapacity) : DataObject::DataObject(0, dataCapacity, DataObject::AllocatorType::eager) { } +/** + * @brief Minimal ctor that allocates a single chunk of user data + metadata from eager memory + * @param metaCapacity How much memory to use for metadata + * @param dataCapacity How much memory to use for user data + * @note Allocates from EAGER memory + * @note Users may adjust the meta/data sizes later + */ +DataObject::DataObject(uint16_t metaCapacity, uint32_t dataCapacity) + : DataObject::DataObject(metaCapacity, dataCapacity, DataObject::AllocatorType::eager) { +} + // === Create an LDO from user-allocated memory === // // The current use-case for "User" LDOs is that a user wants to be able to @@ -281,6 +292,33 @@ uint32_t DataObject::readFromFile(const char *filename) { return 0; } +/** + * @brief Set the contents of the metadata field to zero. + * @return void + */ +void DataObject::WipeMeta() { + // Using functions to maintain consistent definition of how size and pointer are determined + memset(GetMetaPtr(), 0, GetMetaSize()); +} + +/** + * @brief Set the contents of the user data field to zero. + * @return void + */ +void DataObject::WipeData() { + // Using functions to maintain consistent definition of how size and pointer are determined + memset(GetDataPtr(), 0, GetDataSize()); +} + +/** + * @brief Set the contents of the user data and metadata fields to zero. + * @return void + */ +void DataObject::WipeUser() { //Meta + Data + // Using functions to maintain consistent definition of how size and pointer are determined + memset(GetMetaPtr(), 0, GetUserSize()); +} + /** * @brief Get the meta_tag (an id for this particular LDO type) from the header * @return meta_tag A hash id value that can be used to verify an LDO is an expected LDO diff --git a/src/lunasa/DataObject.hh b/src/lunasa/DataObject.hh index 65130cf..c0c1e10 100644 --- a/src/lunasa/DataObject.hh +++ b/src/lunasa/DataObject.hh @@ -50,11 +50,16 @@ public: - DataObject(uint32_t dataCapacity); // Shortcut for creating a new LDO + // Shortcut for creating new LDOs using default allocator + DataObject(uint32_t dataCapacity); + DataObject(uint16_t metaCapacity, uint32_t dataCapacity); + DataObject(void *userMemory, uint16_t metaCapacity, uint32_t dataCapacity, void (*userCleanupFunc)(void *)); // Create an LDO from pre-allocated memory + // NOTE: DataObjects are not currently designed to be subclassed. As a result, we've decided to + // leave this destructor as non-virtual for now. ~DataObject() override; @@ -93,6 +98,10 @@ public: uint32_t writeToFile(const char *filename) const; //Write header/meta/data to file uint32_t readFromFile(const char *filename); //Read header/meta/data from file + // clear the contents of the LDO + void WipeMeta(); + void WipeData(); + void WipeUser(); //Meta + Data /*! @brief Determine whether memory is registered * diff --git a/src/lunasa/Lunasa.cpp b/src/lunasa/Lunasa.cpp index bf6b25e..27596fa 100644 --- a/src/lunasa/Lunasa.cpp +++ b/src/lunasa/Lunasa.cpp @@ -11,8 +11,8 @@ #include "faodelConfig.h" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" @@ -52,7 +52,7 @@ void RegisterPinUnpin(net_pin_fn pin, net_unpin_fn unpin) { * @brief Update Lunasa with information about how to display a particular DataObject type * @param tag The integer id for a particular user data type (usually a hash of the name) * @param name The name for this data type (usually hashed to make the tag) - * @param dump_func The function for dumping this DataObject type to a webhook replystream + * @param dump_func The function for dumping this DataObject type to a whookie replystream */ void RegisterDataObjectType(dataobject_type_t tag, std::string name, fn_DataObjectDump_t dump_func) { internal::Singleton::impl.dataobject_type_registry.RegisterDataObjectType(tag, name, dump_func); @@ -69,7 +69,7 @@ void DeregisterDataObjectType(dataobject_type_t tag) { /** * @brief Dump info about the DataObject to a reply stream. If unregistered, dump generic hex data * @param ldo The DataObject that is to be dumped - * @param rs The webhook ReplyStream that is appended + * @param rs The whookie ReplyStream that is appended * @retval TRUE A user-defined dump function was found for dumping the DataObject type * @retval FALSE No user-defined dump function available for this DataObject. Dumped using hex output */ diff --git a/src/lunasa/README_Lunasa.md b/src/lunasa/README_Lunasa.md index 111d897..9e7fc78 100644 --- a/src/lunasa/README_Lunasa.md +++ b/src/lunasa/README_Lunasa.md @@ -79,7 +79,7 @@ Lunasa has a few library dependencies: | --------------- | ----------------------------------- | | FAODEL:SBL | Uses logging capabilities for boost | | FAODEL:Common | Uses bootstrap and nodeid_t | -| FAODEL:WebHook | For status info | +| FAODEL:Whookie | For status info | Compile-Time Options -------------------- @@ -92,10 +92,11 @@ Run-Time Options When started, Lunasa examines the Configuration passed to it for the following variables: -| Property | Type | Default | Description | -| --------------------------- | ----------- | -------- | ------------------------------------------------- | -| lunasa.eager_memory_manager | string | tcmalloc | Select memory allocator used on eager allocations | -| lunasa.lazy_memory_manager | string | malloc | Select memory allocator used on lazy allocations | +| Property | Type | Default | Description | +| -------------------------------- | ----------- | -------- | ------------------------------------------------- | +| lunasa.eager_memory_manager | string | tcmalloc | Select memory allocator used on eager allocations | +| lunasa.lazy_memory_manager | string | malloc | Select memory allocator used on lazy allocations | +| lunasa.tcmalloc.min_system_alloc | size | - | Override tcmalloc's minimum allocation size | Lunasa's eager and lazy memory allocations manage separate pools of memory. diff --git a/src/lunasa/allocators/AllocatorBase.cpp b/src/lunasa/allocators/AllocatorBase.cpp index f0cdf42..f93f528 100644 --- a/src/lunasa/allocators/AllocatorBase.cpp +++ b/src/lunasa/allocators/AllocatorBase.cpp @@ -129,7 +129,7 @@ bool AllocatorBase::UsingLazyRegistration() { } -void AllocatorBase::webhookStatus(faodel::ReplyStream &rs, const std::string &allocator_name) { +void AllocatorBase::whookieStatus(faodel::ReplyStream &rs, const std::string &allocator_name) { rs.tableBegin("Lunasa " + allocator_name + " Allocator"); rs.tableTop({"Parameter", "Setting"}); rs.tableRow({"Allocator Type", AllocatorType()}); @@ -140,7 +140,7 @@ void AllocatorBase::webhookStatus(faodel::ReplyStream &rs, const std::string &al rs.tableEnd(); } -void AllocatorBase::webhookMemoryAllocations(faodel::ReplyStream &rs, const string &allocator_name) { +void AllocatorBase::whookieMemoryAllocations(faodel::ReplyStream &rs, const string &allocator_name) { rs.mkSection("Lunasa " + allocator_name + " Memory Allocations"); rs.mkText("Allocator does not provide listing support"); } diff --git a/src/lunasa/allocators/AllocatorBase.hh b/src/lunasa/allocators/AllocatorBase.hh index 87bce21..aa03d6b 100644 --- a/src/lunasa/allocators/AllocatorBase.hh +++ b/src/lunasa/allocators/AllocatorBase.hh @@ -14,8 +14,8 @@ #include "faodel-common/Common.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" #include "lunasa/DataObject.hh" @@ -95,10 +95,10 @@ public: virtual std::string AllocatorType() const = 0; - //Webhook helpers - virtual void webhookStatus(faodel::ReplyStream &rs, const std::string &allocator_name); + //Whookie helpers + virtual void whookieStatus(faodel::ReplyStream &rs, const std::string &allocator_name); - virtual void webhookMemoryAllocations(faodel::ReplyStream &rs, const std::string &allocator_name); + virtual void whookieMemoryAllocations(faodel::ReplyStream &rs, const std::string &allocator_name); //InfoInterface void sstr(std::stringstream &ss, int depth = 0, int indent = 0) const override; diff --git a/src/lunasa/allocators/AllocatorTcmalloc.cpp b/src/lunasa/allocators/AllocatorTcmalloc.cpp index 9138d95..3a224b0 100644 --- a/src/lunasa/allocators/AllocatorTcmalloc.cpp +++ b/src/lunasa/allocators/AllocatorTcmalloc.cpp @@ -54,11 +54,18 @@ AllocatorTcmalloc::AllocatorTcmalloc(const faodel::Configuration &config, bool e size_t bytes_allocated = 0; size_t bytes_managed = 0; + string min_system_alloc_string; + size_t min_system_alloc; SysAllocator *sa; AllocatorTcmalloc::TcmallocSysAllocator *tcmalloc_allocator; MallocExtension::instance()->GetNumericProperty("generic.current_allocated_bytes", &bytes_allocated); MallocExtension::instance()->GetNumericProperty("generic.heap_size", &bytes_managed); + faodel::rc_t rc = config.GetLowercaseString(&min_system_alloc_string, "lunasa.tcmalloc.min_system_alloc", ""); + if( rc != ENOENT ) { + min_system_alloc = (size_t)std::stol(min_system_alloc_string); + MallocExtension::instance()->SetNumericProperty("tcmalloc.min_system_alloc", min_system_alloc); + } sa = MallocExtension::instance()->GetSystemAllocator(); tcmalloc_allocator = dynamic_cast(sa); @@ -291,7 +298,7 @@ size_t AllocatorTcmalloc::TotalFree() const { return (mTotalManaged - mTotalUsed); } -void AllocatorTcmalloc::webhookMemoryAllocations(faodel::ReplyStream &rs, const string &allocator_name) { +void AllocatorTcmalloc::whookieMemoryAllocations(faodel::ReplyStream &rs, const string &allocator_name) { rs.tableBegin("Lunasa " + allocator_name + " Memory Allocations"); rs.tableTop({"Allocated Bytes", "RefCount", "MetaBytes", "DataBytes"}); mutex->ReaderLock(); diff --git a/src/lunasa/allocators/AllocatorTcmalloc.hh b/src/lunasa/allocators/AllocatorTcmalloc.hh index 1d5f15f..baea2e5 100644 --- a/src/lunasa/allocators/AllocatorTcmalloc.hh +++ b/src/lunasa/allocators/AllocatorTcmalloc.hh @@ -47,7 +47,7 @@ public: bool HasActiveAllocations() const override; - void webhookMemoryAllocations(faodel::ReplyStream &rs, const std::string &allocator_name) override; + void whookieMemoryAllocations(faodel::ReplyStream &rs, const std::string &allocator_name) override; void AddPinnedRegion(void *addr, size_t size, void *pinned_addr); diff --git a/src/lunasa/common/Allocation.hh b/src/lunasa/common/Allocation.hh index 9416a47..cd31a6d 100644 --- a/src/lunasa/common/Allocation.hh +++ b/src/lunasa/common/Allocation.hh @@ -81,7 +81,11 @@ typedef struct { struct Allocation { allocation_local_t local; //!< Pointers and bookkeeping only available on local node allocation_header_t header; //!< Start of raw data, includes lengths + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" uint8_t user[0]; //!< Start of user's meta/data + #pragma GCC diagnostic pop void setHeader(int initial_ref_count, uint16_t meta_size, uint32_t data_size, @@ -111,8 +115,7 @@ struct Allocation { int DecrRef() { //Issues a dealloc of this when reaches zero assert(local.ref_count > 0 && "LunasaDataObject refcount decremented to below zero"); - --local.ref_count; - int num_left = local.ref_count.load(); + int num_left = --local.ref_count; if(num_left == 0) { if( local.user_data_segments != nullptr ) { diff --git a/src/lunasa/common/DataObjectTypeRegistry.cpp b/src/lunasa/common/DataObjectTypeRegistry.cpp index 1b144aa..7e0ff8d 100644 --- a/src/lunasa/common/DataObjectTypeRegistry.cpp +++ b/src/lunasa/common/DataObjectTypeRegistry.cpp @@ -143,7 +143,7 @@ bool DataObjectTypeRegistry::DumpDataObject(const DataObject &ldo, faodel::Reply } /** - * @brief Webhook function for dumping out information about the registry to a replystream + * @brief Whookie function for dumping out information about the registry to a replystream * @param rs The ReplyStream to append * @note This does not finish the reply stream */ diff --git a/src/lunasa/core/LunasaCoreBase.cpp b/src/lunasa/core/LunasaCoreBase.cpp index ec27210..fc77126 100644 --- a/src/lunasa/core/LunasaCoreBase.cpp +++ b/src/lunasa/core/LunasaCoreBase.cpp @@ -33,17 +33,17 @@ void LunasaCoreBase::init(const faodel::Configuration &config) #endif string lmm_name, emm_name, def_mm; - bool use_webhook; + bool use_whookie; config.GetLowercaseString(&lmm_name, "lunasa.lazy_memory_manager", "malloc"); config.GetLowercaseString(&emm_name, "lunasa.eager_memory_manager", default_eager_memory_manager); config.GetLowercaseString(&def_mm, "lunasa.default_mm_style", "lazy"); - config.GetBool(&use_webhook, "lunasa.use_webhook", "true"); + config.GetBool(&use_whookie, "lunasa.use_whookie", "true"); ConfigureLogging(config); //Pull out any logging options dbg("New lunasacore "+GetType()+" initializing. LazyMem: "+lmm_name+" EagerMem: "+emm_name+" DefStyle: "+def_mm); - init(lmm_name, emm_name, use_webhook, config); + init(lmm_name, emm_name, use_whookie, config); configured=true; } diff --git a/src/lunasa/core/LunasaCoreBase.hh b/src/lunasa/core/LunasaCoreBase.hh index 910a792..c661bc3 100644 --- a/src/lunasa/core/LunasaCoreBase.hh +++ b/src/lunasa/core/LunasaCoreBase.hh @@ -31,7 +31,7 @@ public: virtual void start()=0; virtual void finish()=0; - virtual void init(std::string lmm_name, std::string emm_name, bool use_webhook, + virtual void init(std::string lmm_name, std::string emm_name, bool use_whookie, const faodel::Configuration &config) = 0; virtual void RegisterPinUnpin(net_pin_fn pin, net_unpin_fn unpin) = 0; diff --git a/src/lunasa/core/LunasaCoreSplit.cpp b/src/lunasa/core/LunasaCoreSplit.cpp index eafff70..10b8f7a 100644 --- a/src/lunasa/core/LunasaCoreSplit.cpp +++ b/src/lunasa/core/LunasaCoreSplit.cpp @@ -10,8 +10,8 @@ #include "lunasa/core/LunasaCoreSplit.hh" #include "lunasa/core/Singleton.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" using namespace std; @@ -31,7 +31,7 @@ LunasaCoreSplit::~LunasaCoreSplit() { eager_allocator->DecrRef(); } -void LunasaCoreSplit::init(string lmm_name, string emm_name, bool use_webhook, +void LunasaCoreSplit::init(string lmm_name, string emm_name, bool use_whookie, const faodel::Configuration &config) { dbg("LunasaCoreSplit init. Lazy: "+lmm_name+" Eager: "+emm_name); @@ -51,23 +51,23 @@ void LunasaCoreSplit::init(string lmm_name, string emm_name, bool use_webhook, lazy_allocator = new_lazy_allocator; eager_allocator = new_eager_allocator; - dbg("LunasaCoreSplit allocators created. Updating webhook"); + dbg("LunasaCoreSplit allocators created. Updating whookie"); - webhook::Server::updateHook("/lunasa", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/lunasa", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); - webhook::Server::updateHook("/lunasa/eager_details", [this] (const map &args, stringstream &results) { - return HandleWebhookEagerDetails(args, results); + whookie::Server::updateHook("/lunasa/eager_details", [this] (const map &args, stringstream &results) { + return HandleWhookieEagerDetails(args, results); }); - webhook::Server::updateHook("/lunasa/lazy_details", [this] (const map &args, stringstream &results) { - return HandleWebhookLazyDetails(args, results); + whookie::Server::updateHook("/lunasa/lazy_details", [this] (const map &args, stringstream &results) { + return HandleWhookieLazyDetails(args, results); }); } void LunasaCoreSplit::finish() { - webhook::Server::deregisterHook("/lunasa"); - webhook::Server::deregisterHook("/lunasa/eager_details"); - webhook::Server::deregisterHook("/lunasa/lazy_details"); + whookie::Server::deregisterHook("/lunasa"); + whookie::Server::deregisterHook("/lunasa/eager_details"); + whookie::Server::deregisterHook("/lunasa/lazy_details"); } void LunasaCoreSplit::RegisterPinUnpin(net_pin_fn pin, net_unpin_fn unpin) { @@ -109,12 +109,12 @@ Lunasa LunasaCoreSplit::GetLunasaInstance() { } -void LunasaCoreSplit::HandleWebhookStatus(const map &args, stringstream &results){ +void LunasaCoreSplit::HandleWhookieStatus(const map &args, stringstream &results){ faodel::ReplyStream rs(args, "Lunasa Status", &results); rs.mkSection("Lunasa: Split Allocator"); - rs.mkText( R"(Lunasa is currently configured to use Split allocators. This means Lunasa + rs.mkText( R"(Lunasa is currently configured to use Split allocators. This means Lunasa has one allocator for tracking lazy-pinned memory (memory that is only pinned when it is about to leave the network) and eager-pinned memory (memory that is pinned when requested.)"); @@ -122,31 +122,31 @@ the network) and eager-pinned memory (memory that is pinned when requested.)"); if(lazy_allocator==eager_allocator) { - rs.mkText("Note: Lunasa is currently configured to combine lazy and eager allocators"); - lazy_allocator->webhookStatus(rs, "Lazy/Eager"); + rs.mkText(rs.createBold("Note:")+" Lunasa is currently configured to combine lazy and eager allocators"); + lazy_allocator->whookieStatus(rs, "Lazy/Eager"); } else { - eager_allocator->webhookStatus(rs, "Eager"); + eager_allocator->whookieStatus(rs, "Eager"); rs.mkText(html::mkLink("Eager Memory Details", "/lunasa/eager_details")); - lazy_allocator->webhookStatus(rs, "Lazy"); + lazy_allocator->whookieStatus(rs, "Lazy"); rs.mkText(html::mkLink("Lazy Memory Details", "/lunasa/lazy_details")); } rs.Finish(); } -void LunasaCoreSplit::HandleWebhookEagerDetails(const map &args, stringstream &results) { +void LunasaCoreSplit::HandleWhookieEagerDetails(const map &args, stringstream &results) { faodel::ReplyStream rs(args, "Lunasa Eager Allocator Details", &results); - eager_allocator->webhookStatus(rs, "Eager"); - eager_allocator->webhookMemoryAllocations(rs, "Eager"); + eager_allocator->whookieStatus(rs, "Eager"); + eager_allocator->whookieMemoryAllocations(rs, "Eager"); rs.Finish(); } -void LunasaCoreSplit::HandleWebhookLazyDetails(const map &args, stringstream &results) { +void LunasaCoreSplit::HandleWhookieLazyDetails(const map &args, stringstream &results) { faodel::ReplyStream rs(args, "Lunasa Lazy Allocator Details", &results); - lazy_allocator->webhookStatus(rs, "Lazy"); - lazy_allocator->webhookMemoryAllocations(rs, "Lazy"); + lazy_allocator->whookieStatus(rs, "Lazy"); + lazy_allocator->whookieMemoryAllocations(rs, "Lazy"); rs.Finish(); } diff --git a/src/lunasa/core/LunasaCoreSplit.hh b/src/lunasa/core/LunasaCoreSplit.hh index beb5d6f..4564f1e 100644 --- a/src/lunasa/core/LunasaCoreSplit.hh +++ b/src/lunasa/core/LunasaCoreSplit.hh @@ -18,7 +18,7 @@ public: LunasaCoreSplit(); ~LunasaCoreSplit() override; - void init(std::string lmm_name, std::string emm_name, bool use_webhook, + void init(std::string lmm_name, std::string emm_name, bool use_whookie, const faodel::Configuration &config) override; void start() override {} void finish() override; @@ -40,10 +40,10 @@ public: std::string GetType() const override { return "split"; }; - //WebHook - void HandleWebhookStatus(const std::map &args, std::stringstream &results); - void HandleWebhookEagerDetails(const std::map &args, std::stringstream &results); - void HandleWebhookLazyDetails(const std::map &args, std::stringstream &results); + //Whookie + void HandleWhookieStatus(const std::map &args, std::stringstream &results); + void HandleWhookieEagerDetails(const std::map &args, std::stringstream &results); + void HandleWhookieLazyDetails(const std::map &args, std::stringstream &results); //InfoInterface void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/lunasa/core/LunasaCoreUnconfigured.cpp b/src/lunasa/core/LunasaCoreUnconfigured.cpp index d177d15..d10a879 100644 --- a/src/lunasa/core/LunasaCoreUnconfigured.cpp +++ b/src/lunasa/core/LunasaCoreUnconfigured.cpp @@ -5,7 +5,7 @@ #include "lunasa/core/LunasaCoreUnconfigured.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; @@ -23,7 +23,7 @@ LunasaCoreUnconfigured::~LunasaCoreUnconfigured() { } -void LunasaCoreUnconfigured::init(string lmm_name, string emm_name, bool use_webhook, +void LunasaCoreUnconfigured::init(string lmm_name, string emm_name, bool use_whookie, const faodel::Configuration &config){ Panic("(LunasaCoreUnconfigured) init"); } diff --git a/src/lunasa/core/LunasaCoreUnconfigured.hh b/src/lunasa/core/LunasaCoreUnconfigured.hh index 32546a1..9a48258 100644 --- a/src/lunasa/core/LunasaCoreUnconfigured.hh +++ b/src/lunasa/core/LunasaCoreUnconfigured.hh @@ -18,7 +18,7 @@ public: LunasaCoreUnconfigured(); ~LunasaCoreUnconfigured() override; - void init(std::string lmm_name, std::string emm_name, bool use_webhook, + void init(std::string lmm_name, std::string emm_name, bool use_whookie, const faodel::Configuration &config) override; void start() override {} void finish() override {} diff --git a/src/lunasa/core/Singleton.cpp b/src/lunasa/core/Singleton.cpp index 72ec50c..58f3c72 100644 --- a/src/lunasa/core/Singleton.cpp +++ b/src/lunasa/core/Singleton.cpp @@ -9,8 +9,8 @@ #include "lunasa/core/LunasaCoreSplit.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" @@ -58,7 +58,7 @@ void SingletonImpl::GetBootstrapDependencies( name = "lunasa"; requires = {}; - optional = {"webhook", "mpisyncstart"}; + optional = {"whookie", "mpisyncstart"}; } @@ -117,7 +117,7 @@ void SingletonImpl::Init(const faodel::Configuration &config){ core->RegisterPinUnpin(registered_pin_function, registered_unpin_function); } - webhook::Server::updateHook("/lunasa/dataobject_type_registry", + whookie::Server::updateHook("/lunasa/dataobject_type_registry", [this] (const map &args, stringstream &results) { faodel::ReplyStream rs(args, "Lunasa DataObject Type Registry", &results); dataobject_type_registry.DumpRegistryStatus(rs); @@ -148,7 +148,7 @@ void SingletonImpl::Finish() { error("Attempted to finish Lunasa that is unconfigured"); } else { - webhook::Server::deregisterHook("/lunasa/dataobject_type_registry"); + whookie::Server::deregisterHook("/lunasa/dataobject_type_registry"); //Remove core and reset to unconfigured state delete core; @@ -191,8 +191,8 @@ void SingletonImpl::RegisterPinUnpin(net_pin_fn pin, net_unpin_fn unpin) { */ std::string bootstrap(){ - //register dependencies. Should only need to do webhook - webhook::bootstrap(); + //register dependencies. Should only need to do whookie + whookie::bootstrap(); //register ourselves faodel::bootstrap::RegisterComponent(&lunasa::internal::Singleton::impl, true); //lunasa diff --git a/src/nnti/CMakeLists.txt b/src/nnti/CMakeLists.txt index 716eead..7087791 100644 --- a/src/nnti/CMakeLists.txt +++ b/src/nnti/CMakeLists.txt @@ -4,8 +4,12 @@ if ( NOT DEFINED NNTI_BUILD_TYPE ) string( TOLOWER "${CMAKE_BUILD_TYPE}" lower_type ) IF ( lower_type STREQUAL "debug" ) set(NNTI_BUILD_TYPE "DEBUG") + ELSEIF ( lower_type STREQUAL "pedantic" ) + set(NNTI_BUILD_TYPE "DEBUG") ELSEIF ( lower_type STREQUAL "release" ) set(NNTI_BUILD_TYPE "RELEASE") + ELSEIF ( lower_type STREQUAL "relwithdebinfo" ) + set(NNTI_BUILD_TYPE "RELEASE") ELSE() ## fallback is RELEASE set(NNTI_BUILD_TYPE "RELEASE") @@ -16,24 +20,31 @@ string( TOLOWER "${NNTI_BUILD_TYPE}" lower_type ) if( lower_type STREQUAL "debug" ) message(STATUS "This is an NNTI debug build.") message(STATUS "NNTI is building with debug logging.") + message(STATUS "NNTI is building with args checking.") message(STATUS "NNTI is building with all stats.") set(NNTI_ENABLE_DEBUG_LOGGING TRUE) + set(NNTI_ENABLE_ARGS_CHECKING TRUE) elseif( lower_type STREQUAL "release" ) message(STATUS "This is an NNTI release build.") message(STATUS "NNTI is building without debug logging.") + message(STATUS "NNTI is building without args checking.") message(STATUS "NNTI is building with fast stats only.") set(NNTI_FAST_STATS_ONLY 1) set(NNTI_ENABLE_DEBUG_LOGGING FALSE) + set(NNTI_ENABLE_ARGS_CHECKING FALSE) elseif( lower_type STREQUAL "performance" ) message(STATUS "This is an NNTI performance build.") message(STATUS "NNTI is building without debug logging.") + message(STATUS "NNTI is building without args checking.") message(STATUS "NNTI is building with no stats.") set(NNTI_NO_STATS 1) set(NNTI_ENABLE_DEBUG_LOGGING FALSE) + set(NNTI_ENABLE_ARGS_CHECKING FALSE) else() message(FATAL_ERROR "NNTI_BUILD_TYPE must be defined as one of DEBUG or RELEASE or PERFORMANCE") endif() + if (NNTI_NO_STATS) unset(NNTI_ENABLE_STATS) unset(NNTI_ENABLE_FAST_STATS) @@ -100,13 +111,24 @@ include(nntiProbeNetwork.cmake) include(nntiProbeThreads.cmake) include(nntiProbeTimers.cmake) include(nntiProbeXDR.cmake) +include(nntiProbeCereal.cmake) +if( Faodel_ENABLE_CEREAL ) + set( NNTI_USE_XDR 0 CACHE INTERNAL "Use XDR for NNTI serialization" ) + set( NNTI_USE_CEREAL ${NNTI_HAVE_CEREAL} CACHE INTERNAL "Use Cereal for NNTI serialization" ) +else() + set( NNTI_USE_XDR ${NNTI_HAVE_XDR} CACHE INTERNAL "Use XDR for NNTI serialization" ) + if( NOT NNTI_HAVE_XDR ) + set( NNTI_USE_CEREAL ${NNTI_HAVE_CEREAL} CACHE INTERNAL "Use Cereal for NNTI serialization" ) + endif() +endif() +if( NOT NNTI_USE_XDR AND NOT NNTI_USE_CEREAL ) + message( FATAL_ERROR "Couldn't find a serailization library for NNTI." ) +endif() -## What to build ############################################################## - -include(nntiProcessXDR.cmake) +## What to build ############################################################## set(HEADERS @@ -123,14 +145,13 @@ set(HEADERS nnti_threads.h nnti_threads_types.h nnti_op.hpp - nnti_packable.h nnti_pid.hpp + nnti_serialize.hpp nnti_types.h nnti_url.hpp nnti_util.hpp nnti_wid.hpp nnti_wr.hpp - nnti_xdr.h transports/base/base_transport.hpp transports/null/null_transport.hpp ) @@ -174,28 +195,29 @@ set(HEADERS transports/mpi/mpi_transport.hpp ) endif() +if( NNTI_USE_XDR ) +set(HEADERS + ${HEADERS} + serializers/xdr/nnti_packable.h + serializers/xdr/nnti_xdr.h +) +endif() +if( NNTI_USE_CEREAL ) +set(HEADERS + ${HEADERS} + serializers/cereal/nnti_packable.hpp +) +endif() set(HEADERS_PUBLIC nnti.h - nnti_buffer.hpp - nnti_transport.hpp nnti_logger.h - nnti_logger.hpp - nnti_packable.h - nnti_types.h + nnti_transport.hpp nnti_datatype.hpp nnti_callback.hpp - nnti_connection.hpp - nnti_peer.hpp - nnti_pid.hpp - nnti_threads.h - nnti_threads_types.h - nnti_url.hpp - nnti_util.hpp - nnti_vector.hpp - nnti_wid.hpp nnti_wr.hpp - nnti_xdr.h + nnti_logger.hpp + nnti_types.h transport_factory.hpp ) @@ -203,14 +225,17 @@ set(SOURCES nnti.cpp transport_factory.cpp nnti_buffer.cpp + nnti_callback.cpp nnti_connection.cpp nnti_logger.cpp nnti_op.cpp - nnti_packable.c nnti_pid.cpp + nnti_serialize.cpp nnti_threads.cpp + nnti_transport.cpp nnti_url.cpp nnti_wid.cpp + nnti_wr.cpp transports/base/base_transport.cpp ) if( NNTI_BUILD_IBVERBS ) @@ -242,6 +267,12 @@ set(SOURCES transports/mpi/mpi_transport.cpp ) endif() +if( NNTI_USE_XDR ) +set(SOURCES + ${SOURCES} + serializers/xdr/nnti_packable.c +) +endif() # # If no transport was selected, this build of NNTI should succeed but will not @@ -255,19 +286,23 @@ if( NOT NNTI_BUILD_UGNI AND endif() -LIST( APPEND NNTI_imports webhook common sbl Boost::atomic Boost::log Boost::log_setup ) +LIST( APPEND NNTI_imports whookie common sbl Boost::atomic Boost::log Boost::log_setup ) if( Faodel_ENABLE_MPI_SUPPORT ) LIST( APPEND NNTI_imports MPI::MPI_CXX ) endif() LIST( APPEND NNTI_imports ${NNTI_TRANSPORT_TARGETS} ) - -nntiProcessXDR(${CMAKE_CURRENT_SOURCE_DIR}/nnti_packable.x ${CMAKE_CURRENT_SOURCE_DIR}) +LIST( APPEND NNTI_imports cereal ) set_source_files_properties(transport_factory.cpp PROPERTIES COMPILE_FLAGS "-O0 -g") add_library(nnti ${HEADERS} ${SOURCES}) target_link_libraries( nnti ${NNTI_imports} ) -add_dependencies(nnti generate-nnti_packable) + +if( NNTI_USE_XDR ) + include(nntiProcessXDR.cmake) + nntiProcessXDR(${CMAKE_CURRENT_SOURCE_DIR}/serializers/xdr/nnti_packable.x ${CMAKE_CURRENT_SOURCE_DIR}/serializers/xdr) + add_dependencies(nnti generate-nnti_packable) +endif() install(TARGETS nnti diff --git a/src/nnti/nnti.cpp b/src/nnti/nnti.cpp index 56531d9..b9337d0 100644 --- a/src/nnti/nnti.cpp +++ b/src/nnti/nnti.cpp @@ -23,7 +23,7 @@ #include "nnti/nnti.h" #include "nnti/nnti_datatype.hpp" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_transport.hpp" #include "nnti/transport_factory.hpp" #include "nnti/nnti_wr.hpp" diff --git a/src/nnti/nnti.h b/src/nnti/nnti.h index 108dd43..e111055 100644 --- a/src/nnti/nnti.h +++ b/src/nnti/nnti.h @@ -15,8 +15,7 @@ #define NNTI_H_ -#include -#include +#include "nnti/nnti_types.h" #ifdef __cplusplus diff --git a/src/nnti/nntiConfig.h.in b/src/nnti/nntiConfig.h.in index df5c609..bf27cb7 100644 --- a/src/nnti/nntiConfig.h.in +++ b/src/nnti/nntiConfig.h.in @@ -34,6 +34,9 @@ #cmakedefine NNTI_HAVE_IBV_EXP_QP_INIT_ATTR_ATOMICS_ARG 1 #cmakedefine NNTI_HAVE_IBV_EXP_QP_CREATE_ATOMIC_BE_REPLY 1 #cmakedefine NNTI_HAVE_IBV_EXP_ATOMIC_HCA_REPLY_BE 1 +#cmakedefine NNTI_HAVE_IBV_EXP_DEVICE_ATTR_ODP 1 +#cmakedefine NNTI_HAVE_IBV_EXP_ACCESS_ON_DEMAND 1 +#cmakedefine NNTI_HAVE_IBV_EXP_ODP_SUPPORT_IMPLICIT 1 #cmakedefine NNTI_HAVE_ENDIAN_H 1 #cmakedefine NNTI_HAVE_BE64TOH 1 @@ -101,16 +104,27 @@ * ************************************************ */ #cmakedefine NNTI_HAVE_HPCTOOLKIT 1 +/* ************************************************ + * Serialization configuration + * ************************************************ */ +#cmakedefine NNTI_USE_XDR 1 +#cmakedefine NNTI_USE_CEREAL 1 + /* ************************************************ * XDR configuration * ************************************************ */ +#cmakedefine NNTI_HAVE_XDR_U_INT8_T 1 #cmakedefine NNTI_HAVE_XDR_U_INT16_T 1 #cmakedefine NNTI_HAVE_XDR_U_INT32_T 1 #cmakedefine NNTI_HAVE_XDR_U_INT64_T 1 -#cmakedefine NNTI_HAVE_XDR_SIZEOF 1 +#cmakedefine NNTI_HAVE_XDR_SIZEOF 1 /* ************************************************ * Logging configuration * ************************************************ */ #cmakedefine NNTI_ENABLE_DEBUG_LOGGING 1 +/* ************************************************ + * Args Checking configuration + * ************************************************ */ +#cmakedefine NNTI_ENABLE_ARGS_CHECKING 1 diff --git a/src/nnti/nntiProbeCereal.cmake b/src/nnti/nntiProbeCereal.cmake new file mode 100644 index 0000000..392d8b6 --- /dev/null +++ b/src/nnti/nntiProbeCereal.cmake @@ -0,0 +1,15 @@ +INCLUDE(CheckIncludeFileCXX) + +if( TARGET cereal ) + SET( save_CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES}" ) + SET( CMAKE_REQUIRED_INCLUDES "${CEREAL_INCLUDES}" ) + + CHECK_INCLUDE_FILE_CXX("cereal/cereal.hpp" NNTI_HAVE_CEREAL_CEREAL_HPP) + + SET( CMAKE_REQUIRED_INCLUDES "${save_CMAKE_REQUIRED_INCLUDES}" ) + + SET( NNTI_HAVE_CEREAL 1 ) + if( NOT NNTI_HAVE_CEREAL_CEREAL_HPP ) + SET( NNTI_HAVE_CEREAL 0 ) + endif() +endif() diff --git a/src/nnti/nntiProbeNetwork.cmake b/src/nnti/nntiProbeNetwork.cmake index 31bd9b2..50061f4 100644 --- a/src/nnti/nntiProbeNetwork.cmake +++ b/src/nnti/nntiProbeNetwork.cmake @@ -58,10 +58,22 @@ check_c_source_compiles( NNTI_HAVE_IBV_EXP_QP_CREATE_ATOMIC_BE_REPLY; ) check_c_source_compiles( - "#include \nint main(){enum ibv_exp_atomic_cap flags = IBV_EXP_ATOMIC_HCA_REPLY_BE;return 0;}" + "#include \nint main(){enum ibv_exp_atomic_cap caps = IBV_EXP_ATOMIC_HCA_REPLY_BE;return 0;}" NNTI_HAVE_IBV_EXP_ATOMIC_HCA_REPLY_BE; ) check_c_source_compiles( "#include \nint main(){be64toh(1LL);return 0;}" NNTI_HAVE_BE64TOH; ) +check_c_source_compiles( + "#include \nint main(){enum ibv_exp_device_attr_comp_mask mask = IBV_EXP_DEVICE_ATTR_ODP;return 0;}" + NNTI_HAVE_IBV_EXP_DEVICE_ATTR_ODP; +) +check_c_source_compiles( + "#include \nint main(){enum ibv_exp_access_flags flags = IBV_EXP_ACCESS_ON_DEMAND;return 0;}" + NNTI_HAVE_IBV_EXP_ACCESS_ON_DEMAND; +) +check_c_source_compiles( + "#include \nint main(){enum ibv_odp_general_cap_bits caps = IBV_EXP_ODP_SUPPORT_IMPLICIT;return 0;}" + NNTI_HAVE_IBV_EXP_ODP_SUPPORT_IMPLICIT; +) diff --git a/src/nnti/nntiProbeXDR.cmake b/src/nnti/nntiProbeXDR.cmake index 2bbeb18..56b0447 100644 --- a/src/nnti/nntiProbeXDR.cmake +++ b/src/nnti/nntiProbeXDR.cmake @@ -1,16 +1,10 @@ - -########## CHECK FOR HEADER FILES ############ - -INCLUDE(CheckIncludeFiles) - -########## CHECK FOR FUNCTIONS ############ - -INCLUDE(CheckLibraryExists) +INCLUDE(CheckIncludeFile) INCLUDE(CheckFunctionExists) -INCLUDE(CheckCSourceCompiles) -INCLUDE(CheckCXXSourceCompiles) -CHECK_FUNCTION_EXISTS(xdr_u_int8_t NNTI_HAVE_XDR_U_INT8_T) +CHECK_INCLUDE_FILE("rpc/types.h" NNTI_HAVE_RPC_TYPES_H) +CHECK_INCLUDE_FILE("rpc/xdr.h" NNTI_HAVE_RPC_XDR_H) + +CHECK_FUNCTION_EXISTS(xdr_u_int8_t NNTI_HAVE_XDR_U_INT8_T) CHECK_FUNCTION_EXISTS(xdr_u_int16_t NNTI_HAVE_XDR_U_INT16_T) CHECK_FUNCTION_EXISTS(xdr_u_int32_t NNTI_HAVE_XDR_U_INT32_T) CHECK_FUNCTION_EXISTS(xdr_u_int64_t NNTI_HAVE_XDR_U_INT64_T) @@ -20,3 +14,9 @@ check_c_source_compiles( "#include \nint main(){xdr_sizeof(NULL,NULL);return 0;}" NNTI_HAVE_XDR_SIZEOF ) + +SET( NNTI_HAVE_XDR 1 ) + +if( NOT NNTI_HAVE_XDR_SIZEOF ) + SET( NNTI_HAVE_XDR 0 ) +endif() diff --git a/src/nnti/nntiProcessXDR.cmake b/src/nnti/nntiProcessXDR.cmake index 812bd3c..82ab364 100644 --- a/src/nnti/nntiProcessXDR.cmake +++ b/src/nnti/nntiProcessXDR.cmake @@ -7,7 +7,7 @@ function (nntiProcessXDR inpath outpath) add_custom_command( OUTPUT ${outpath}/${file}.c COMMAND rpcgen -Cc ${inpath} - | sed -e "\"s#include.*${file}.*#include #\"" + | sed -e "\"s#include.*${file}.*#include #\"" > ${outpath}/${file}.c DEPENDS ${inpath} ${outpath}/${file}.h) @@ -16,7 +16,7 @@ function (nntiProcessXDR inpath outpath) OUTPUT ${outpath}/${file}.h COMMAND rpcgen -Ch ${inpath} | sed -e "\"s#rpc/rpc.h#${file}.h#\"" - | sed -e "\"s#include <${file}.h>#include #\"" + | sed -e "\"s#include <${file}.h>#include #\"" | perl -pe \"BEGIN{undef $$/\;} s/\(enum\\s\\w+\\s\\{\\n\(\\s*.*?,\\n\)*?\\s*.*?\),\(\\n\\s*\\}\;\)/\\1\\3/smg\" > ${outpath}/${file}.h DEPENDS ${inpath}) diff --git a/src/nnti/nnti_buffer.cpp b/src/nnti/nnti_buffer.cpp index cd89cba..fd4774c 100644 --- a/src/nnti/nnti_buffer.cpp +++ b/src/nnti/nnti_buffer.cpp @@ -22,4 +22,6 @@ std::atomic nnti::datatype::nnti_buffer::next_id_ = {1}; -const uint64_t nnti::datatype::nnti_buffer::packed_size_; +#if NNTI_USE_XDR == 1 +const uint64_t nnti::datatype::nnti_buffer::max_packed_size_; +#endif diff --git a/src/nnti/nnti_buffer.hpp b/src/nnti/nnti_buffer.hpp index 714c3f8..8a527cf 100644 --- a/src/nnti/nnti_buffer.hpp +++ b/src/nnti/nnti_buffer.hpp @@ -17,7 +17,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" @@ -45,8 +45,9 @@ class nnti_buffer protected: NNTI_buffer_p_t packable_; - const static uint64_t packed_size_=256; - char packed_[packed_size_]; + const static uint64_t max_packed_size_=256; + char packed_[max_packed_size_]; + uint64_t packed_size_; uint32_t id_; bool free_in_dtor_; @@ -172,7 +173,9 @@ class nnti_buffer { unpack(packed_buf, packed_len); +#if NNTI_USE_XDR == 1 memcpy(&packed_[0], packed_buf, std::min(packed_len, packed_size_)); +#endif log_debug("nnti_buffer", "flags_=0x%04X", flags_); @@ -227,43 +230,17 @@ class nnti_buffer uint64_t packed_size(void) { - uint64_t packed_len=0; - - xdrproc_t sizeof_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; - - packed_len = xdr_sizeof(sizeof_fn, &packable_); - packed_len += sizeof(NNTI_datatype_t); - - return packed_len; + if (packed_size_ == 0) { + packed_size_ = nnti::serialize::packed_buffer_size(&packable_); + } + return packed_size_; } NNTI_result_t internal_pack(void) { - XDR encode_xdrs; - xdrproc_t encode_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; - uint64_t bytes_left = packed_size_; - char *p = packed_; - - *(NNTI_datatype_t*)packed_ = NNTI_dt_buffer; - - p += sizeof(NNTI_datatype_t); - bytes_left -= sizeof(NNTI_datatype_t); - packable_.flags = flags_; - - xdrmem_create( - &encode_xdrs, - p, - bytes_left, - XDR_ENCODE); - - if (!encode_fn(XDRPROC_ARGS(&encode_xdrs, &packable_))) { - log_fatal("nnti_buffer", "packing failed"); - return NNTI_EENCODE; - } - - return NNTI_OK; + return nnti::serialize::pack_buffer(&packable_, &packed_[0], max_packed_size_, &packed_size_); } NNTI_result_t @@ -272,49 +249,27 @@ class nnti_buffer const uint64_t packed_buflen) { memcpy(packed_buf, &packed_[0], (packed_size_ < packed_buflen) ? packed_size_ : packed_buflen); - return NNTI_OK; } NNTI_result_t unpack( char *packed_buf, - const uint64_t packed_len) + const uint64_t packed_buflen) { - XDR decode_xdrs; - xdrproc_t decode_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; - NNTI_datatype_t *dt = (NNTI_datatype_t*)packed_buf; - uint64_t dt_size = sizeof(NNTI_buffer_p_t); - uint64_t bytes_left = packed_len; - - packed_buf += sizeof(NNTI_datatype_t); - bytes_left -= sizeof(NNTI_datatype_t); - - memset(&packable_, 0, dt_size); - xdrmem_create( - &decode_xdrs, - packed_buf, - bytes_left, - XDR_DECODE); - - if (!decode_fn(XDRPROC_ARGS(&decode_xdrs, &packable_))) { - log_fatal("nnti_buffer", "unpacking failed (packed_buf=%p packed_size=%lu", packed_buf, packed_len); - return NNTI_EDECODE; - } - - flags_ = (NNTI_buffer_flags_t)packable_.flags; + NNTI_result_t rc; + packed_size_ = packed_buflen; + memcpy(&packed_[0], &packed_buf[0], packed_buflen); + rc = nnti::serialize::unpack_buffer(&packable_, &packed_buf[0], packed_buflen); + flags_ = (NNTI_buffer_flags_t)packable_.flags; return NNTI_OK; } NNTI_result_t free_packable(void) { - xdrproc_t free_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; - - xdr_free(free_fn, (char*)&packable_); - - return NNTI_OK; + return nnti::serialize::free_buffer(&packable_); } NNTI_result_t diff --git a/src/nnti/nnti_callback.cpp b/src/nnti/nnti_callback.cpp new file mode 100644 index 0000000..c86f3c9 --- /dev/null +++ b/src/nnti/nnti_callback.cpp @@ -0,0 +1,101 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/nntiConfig.h" + +#include + +#include +#include +#include + +#include "nnti/nnti_callback.hpp" +#include "nnti/nnti_serialize.hpp" + + +namespace nnti { +namespace datatype { + +class default_event_callback { +public: + NNTI_result_t operator() (NNTI_event_t *event, void *context) { + // this default callback returns !NNTI_OK so the event will pushed into the EQ + return NNTI_EIO; + } +}; + +nnti_event_callback::nnti_event_callback() +: cb_(default_event_callback()), + nnti_datatype(NNTI_dt_callback) +{ + return; +} +nnti_event_callback::nnti_event_callback( + nnti::datatype::nnti_event_callback &other) +: cb_(other.cb_), + nnti_datatype(other.transport_, + other.datatype_) +{ + return; +} +nnti_event_callback::nnti_event_callback( + nnti::transports::transport *transport) +: cb_(default_event_callback()), + nnti_datatype(transport, + NNTI_dt_callback) +{ + return; +} +nnti_event_callback::nnti_event_callback( + nnti::transports::transport *transport, + NNTI_event_callback_t cb) +: cb_(cb), + nnti_datatype(transport, + NNTI_dt_callback) +{ + if (!cb_) { + cb_ = default_event_callback(); + } + return; +} +nnti_event_callback::nnti_event_callback( + nnti::transports::transport *transport, + std::function< NNTI_result_t(NNTI_event_t*,void*) > cb) +: cb_(cb), + nnti_datatype(transport, + NNTI_dt_callback) +{ + if (!cb_) { + cb_ = default_event_callback(); + } + return; +} + +nnti_event_callback::~nnti_event_callback() +{ + return; +} + +nnti_event_callback::operator bool() const +{ + return cb_.operator bool(); +} + +NNTI_result_t +nnti_event_callback::invoke( + NNTI_event_t *event, void *context) const +{ + return cb_(event, context); +} + +std::string +nnti_event_callback::toString() { + std::stringstream out; + out << "cb_==" << &cb_; + return out.str(); +} + +} /* namespace datatype */ +} /* namespace nnti */ diff --git a/src/nnti/nnti_callback.hpp b/src/nnti/nnti_callback.hpp index 920e613..9245729 100644 --- a/src/nnti/nnti_callback.hpp +++ b/src/nnti/nnti_callback.hpp @@ -17,103 +17,42 @@ #include "nnti/nntiConfig.h" -#include - #include -#include -#include -#include "nnti/nnti_packable.h" #include "nnti/nnti_types.h" - #include "nnti/nnti_datatype.hpp" namespace nnti { namespace datatype { -class default_event_callback { -public: - NNTI_result_t operator() (NNTI_event_t *event, void *context) { - // this default callback returns !NNTI_OK so the event will pushed into the EQ - return NNTI_EIO; - } -}; - class nnti_event_callback : public nnti_datatype { private: std::function< NNTI_result_t(NNTI_event_t*,void*) > cb_; public: - nnti_event_callback() - : cb_(default_event_callback()), - nnti_datatype(NNTI_dt_callback) - { - return; - } + nnti_event_callback(); nnti_event_callback( - nnti::datatype::nnti_event_callback &other) - : cb_(other.cb_), - nnti_datatype(other.transport_, - other.datatype_) - { - return; - } + nnti::datatype::nnti_event_callback &other); nnti_event_callback( - nnti::transports::transport *transport) - : cb_(default_event_callback()), - nnti_datatype(transport, - NNTI_dt_callback) - { - return; - } + nnti::transports::transport *transport); nnti_event_callback( nnti::transports::transport *transport, - NNTI_event_callback_t cb) - : cb_(cb), - nnti_datatype(transport, - NNTI_dt_callback) - { - if (!cb_) { - cb_ = default_event_callback(); - } - return; - } + NNTI_event_callback_t cb); nnti_event_callback( nnti::transports::transport *transport, - std::function< NNTI_result_t(NNTI_event_t*,void*) > cb) - : cb_(cb), - nnti_datatype(transport, - NNTI_dt_callback) - { - if (!cb_) { - cb_ = default_event_callback(); - } - return; - } - - ~nnti_event_callback() override { - return; - } - - explicit operator bool() const - { - return cb_.operator bool(); - } + std::function< NNTI_result_t(NNTI_event_t*,void*) > cb); + + ~nnti_event_callback() override; + + explicit operator bool() const; NNTI_result_t - invoke(NNTI_event_t *event, void *context) const - { - return cb_(event, context); - } - - std::string - toString() override { - std::stringstream out; - out << "cb_==" << &cb_; - return out.str(); - } + invoke(NNTI_event_t *event, void *context) const; + + std::string + toString() override; }; diff --git a/src/nnti/nnti_connection.hpp b/src/nnti/nnti_connection.hpp index 80c7ebd..012e7e8 100644 --- a/src/nnti/nnti_connection.hpp +++ b/src/nnti/nnti_connection.hpp @@ -23,7 +23,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_peer.hpp" @@ -106,7 +106,7 @@ class nnti_connection { return ss.str(); } /* - * generate a key=value (one per line) string that can be included in a WebHook reply + * generate a key=value (one per line) string that can be included in a Whookie reply */ virtual std::string reply_string(void) diff --git a/src/nnti/nnti_datatype.hpp b/src/nnti/nnti_datatype.hpp index 0ac46d0..4b2c9e6 100644 --- a/src/nnti/nnti_datatype.hpp +++ b/src/nnti/nnti_datatype.hpp @@ -22,6 +22,7 @@ #include #include +#include "nnti/nnti_types.h" #include "nnti/nnti_transport.hpp" @@ -45,7 +46,7 @@ class nnti_datatype { } nnti_datatype( nnti::transports::transport *transport, - NNTI_datatype_t datatype) + NNTI_datatype_t datatype) : transport_(transport), datatype_(datatype) { diff --git a/src/nnti/nnti_eq.hpp b/src/nnti/nnti_eq.hpp index 01d8d3d..47c7c09 100644 --- a/src/nnti/nnti_eq.hpp +++ b/src/nnti/nnti_eq.hpp @@ -24,7 +24,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_datatype.hpp" diff --git a/src/nnti/nnti_freelist.hpp b/src/nnti/nnti_freelist.hpp index 689f350..f245bea 100644 --- a/src/nnti/nnti_freelist.hpp +++ b/src/nnti/nnti_freelist.hpp @@ -24,7 +24,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_datatype.hpp" diff --git a/src/nnti/nnti_op.hpp b/src/nnti/nnti_op.hpp index ea591db..0e47ca9 100644 --- a/src/nnti/nnti_op.hpp +++ b/src/nnti/nnti_op.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/nnti_peer.hpp b/src/nnti/nnti_peer.hpp index b7d1522..176d6d1 100644 --- a/src/nnti/nnti_peer.hpp +++ b/src/nnti/nnti_peer.hpp @@ -17,7 +17,7 @@ #include "nnti/nntiConfig.h" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_datatype.hpp" #include "nnti/nnti_url.hpp" @@ -36,8 +36,11 @@ class nnti_peer private: protected: - nnti::core::nnti_url url_; - NNTI_peer_p_t packable_; + nnti::core::nnti_url url_; + NNTI_peer_p_t packable_; + const static uint64_t max_packed_size_=256; + char packed_[max_packed_size_]; + uint64_t packed_size_; nnti::core::nnti_connection *conn_; @@ -135,14 +138,10 @@ class nnti_peer uint64_t packed_size(void) { - uint64_t packed_len=0; - - xdrproc_t sizeof_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; - - packed_len = xdr_sizeof(sizeof_fn, &packable_); - packed_len += sizeof(NNTI_datatype_t); - - return packed_len; + if (packed_size_ == 0) { + packed_size_ = nnti::serialize::packed_peer_size(&packable_); + } + return packed_size_; } NNTI_result_t @@ -150,69 +149,27 @@ class nnti_peer char *packed_buf, const uint64_t packed_buflen) { - XDR encode_xdrs; - xdrproc_t encode_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; - uint64_t bytes_left = packed_buflen; - - *(NNTI_datatype_t*)packed_buf = NNTI_dt_peer; - - packed_buf += sizeof(NNTI_datatype_t); - bytes_left -= sizeof(NNTI_datatype_t); - - xdrmem_create( - &encode_xdrs, - packed_buf, - bytes_left, - XDR_ENCODE); - - if (!encode_fn(XDRPROC_ARGS(&encode_xdrs, &packable_))) { - log_fatal("nnti_peer", "packing failed"); - return NNTI_EENCODE; - } - - return NNTI_OK; + return nnti::serialize::pack_peer(&packable_, &packed_buf[0], packed_buflen, &packed_size_); } NNTI_result_t unpack( char *packed_buf, - const uint64_t packed_len) - { - XDR decode_xdrs; - xdrproc_t decode_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; - NNTI_datatype_t *dt = (NNTI_datatype_t*)packed_buf; - uint64_t dt_size = sizeof(NNTI_peer_p_t); - uint64_t bytes_left = packed_len; - - packed_buf += sizeof(NNTI_datatype_t); - bytes_left -= sizeof(NNTI_datatype_t); - - memset(&packable_, 0, dt_size); - xdrmem_create( - &decode_xdrs, - packed_buf, - bytes_left, - XDR_DECODE); - - if (!decode_fn(XDRPROC_ARGS(&decode_xdrs, &packable_))) { - log_fatal("nnti_peer", "unpacking failed"); - return NNTI_EDECODE; - } + const uint64_t packed_buflen) + { + packed_size_ = packed_buflen; + memcpy(&packed_[0], &packed_buf[0], packed_buflen); + + return nnti::serialize::unpack_peer(&packable_, packed_buf, packed_buflen); - return NNTI_OK; } NNTI_result_t free_packable(void) { - xdrproc_t free_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; - - xdr_free(free_fn, (char*)&packable_); - - return NNTI_OK; + return nnti::serialize::free_peer(&packable_); } - static inline nnti_peer* to_obj( NNTI_peer_t peer_hdl) diff --git a/src/nnti/nnti_pid.hpp b/src/nnti/nnti_pid.hpp index e6fa0ef..ef54af7 100644 --- a/src/nnti/nnti_pid.hpp +++ b/src/nnti/nnti_pid.hpp @@ -10,7 +10,7 @@ #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" namespace nnti { diff --git a/src/nnti/nnti_serialize.cpp b/src/nnti/nnti_serialize.cpp new file mode 100644 index 0000000..1b34a15 --- /dev/null +++ b/src/nnti/nnti_serialize.cpp @@ -0,0 +1,255 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#include "nnti/nntiConfig.h" + +#include + +#include +#include + + +namespace nnti { +namespace serialize { + +NNTI_datatype_t +get_datatype( + char *packed_buf, + const uint64_t packed_buflen) +{ + NNTI_datatype_t t; + +#if NNTI_USE_XDR == 1 + t = *(NNTI_datatype_t*)packed_buf; +#elif NNTI_USE_CEREAL == 1 + { + std::istringstream iss(std::string(packed_buf, packed_buflen)); + cereal::PortableBinaryInputArchive iarchive(iss); + iarchive( t ); + } +#endif + + return t; +} + +uint64_t +packed_peer_size( + NNTI_peer_p_t *packable) +{ + uint64_t packed_len=0; + +#if NNTI_USE_XDR == 1 + xdrproc_t sizeof_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; + + packed_len = xdr_sizeof(sizeof_fn, packable); + packed_len += sizeof(NNTI_datatype_t); +#endif + + return packed_len; +} + +NNTI_result_t +pack_peer( + NNTI_peer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen, + uint64_t *packed_len) +{ +#if NNTI_USE_XDR == 1 + XDR encode_xdrs; + xdrproc_t encode_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; + uint64_t bytes_left = packed_buflen; + + *(NNTI_datatype_t*)packed_buf = NNTI_dt_peer; + + packed_buf += sizeof(NNTI_datatype_t); + bytes_left -= sizeof(NNTI_datatype_t); + + xdrmem_create( + &encode_xdrs, + packed_buf, + bytes_left, + XDR_ENCODE); + + if (!encode_fn(XDRPROC_ARGS(&encode_xdrs, packable))) { + log_fatal("nnti_peer", "packing failed"); + return NNTI_EENCODE; + } + *packed_len = packed_peer_size(packable); +#elif NNTI_USE_CEREAL == 1 + std::ostringstream oss; + { + NNTI_datatype_t t = NNTI_dt_peer; + cereal::PortableBinaryOutputArchive oarchive(oss); + oarchive( t ); + oarchive( *packable ); + } + *packed_len = oss.str().length(); + memcpy(packed_buf, oss.str().c_str(), *packed_len); +#endif + + return NNTI_OK; +} + +NNTI_result_t +unpack_peer( + NNTI_peer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen) +{ +#if NNTI_USE_XDR == 1 + XDR decode_xdrs; + xdrproc_t decode_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; + NNTI_datatype_t *dt = (NNTI_datatype_t*)packed_buf; + uint64_t dt_size = sizeof(NNTI_peer_p_t); + uint64_t bytes_left = packed_buflen; + + packed_buf += sizeof(NNTI_datatype_t); + bytes_left -= sizeof(NNTI_datatype_t); + + memset(packable, 0, dt_size); + xdrmem_create( + &decode_xdrs, + packed_buf, + bytes_left, + XDR_DECODE); + + if (!decode_fn(XDRPROC_ARGS(&decode_xdrs, packable))) { + log_fatal("nnti_peer", "unpacking failed"); + return NNTI_EDECODE; + } +#elif NNTI_USE_CEREAL == 1 + { + NNTI_datatype_t t; + std::istringstream iss(std::string(packed_buf, packed_buflen)); + cereal::PortableBinaryInputArchive iarchive(iss); + iarchive( t ); + iarchive( *packable ); + } +#endif + return NNTI_OK; +} + +NNTI_result_t +free_peer( + NNTI_peer_p_t *packable) +{ +#if NNTI_USE_XDR == 1 + xdrproc_t free_fn = (xdrproc_t)&xdr_NNTI_peer_p_t; + xdr_free(free_fn, (char*)packable); +#endif + return NNTI_OK; +} + +uint64_t +packed_buffer_size( + NNTI_buffer_p_t *packable) +{ + uint64_t packed_len=0; + +#if NNTI_USE_XDR == 1 + xdrproc_t sizeof_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; + + packed_len = xdr_sizeof(sizeof_fn, packable); + packed_len += sizeof(NNTI_datatype_t); +#endif + + return packed_len; +} + +NNTI_result_t +pack_buffer( + NNTI_buffer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen, + uint64_t *packed_len) +{ +#if NNTI_USE_XDR == 1 + XDR encode_xdrs; + xdrproc_t encode_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; + uint64_t bytes_left = packed_buflen; + + *(NNTI_datatype_t*)packed_buf = NNTI_dt_buffer; + + packed_buf += sizeof(NNTI_datatype_t); + bytes_left -= sizeof(NNTI_datatype_t); + + xdrmem_create( + &encode_xdrs, + packed_buf, + bytes_left, + XDR_ENCODE); + + if (!encode_fn(XDRPROC_ARGS(&encode_xdrs, packable))) { + log_fatal("nnti_buffer", "packing failed"); + return NNTI_EENCODE; + } + *packed_len = packed_buffer_size(packable); +#elif NNTI_USE_CEREAL == 1 + std::ostringstream oss; + { + NNTI_datatype_t t = NNTI_dt_buffer; + cereal::PortableBinaryOutputArchive oarchive(oss); + oarchive( t ); + oarchive( *packable ); + } + *packed_len = oss.str().length(); + memcpy(packed_buf, oss.str().c_str(), *packed_len); +#endif + + return NNTI_OK; +} + +NNTI_result_t +unpack_buffer( + NNTI_buffer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen) +{ +#if NNTI_USE_XDR == 1 + XDR decode_xdrs; + xdrproc_t decode_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; + NNTI_datatype_t *dt = (NNTI_datatype_t*)packed_buf; + uint64_t dt_size = sizeof(NNTI_buffer_p_t); + uint64_t bytes_left = packed_buflen; + + packed_buf += sizeof(NNTI_datatype_t); + bytes_left -= sizeof(NNTI_datatype_t); + + memset(packable, 0, dt_size); + xdrmem_create( + &decode_xdrs, + packed_buf, + bytes_left, + XDR_DECODE); + + if (!decode_fn(XDRPROC_ARGS(&decode_xdrs, packable))) { + log_fatal("nnti_buffer", "unpacking failed"); + return NNTI_EDECODE; + } +#elif NNTI_USE_CEREAL == 1 + { + NNTI_datatype_t t; + std::istringstream iss(std::string(packed_buf, packed_buflen)); + cereal::PortableBinaryInputArchive iarchive(iss); + iarchive( t ); + iarchive( *packable ); + } +#endif + return NNTI_OK; +} + +NNTI_result_t +free_buffer( + NNTI_buffer_p_t *packable) +{ +#if NNTI_USE_XDR == 1 + xdrproc_t free_fn = (xdrproc_t)&xdr_NNTI_buffer_p_t; + xdr_free(free_fn, (char*)packable); +#endif + return NNTI_OK; +} + +} // namespace serialize +} // namespace nnti diff --git a/src/nnti/nnti_serialize.hpp b/src/nnti/nnti_serialize.hpp new file mode 100644 index 0000000..2222dca --- /dev/null +++ b/src/nnti/nnti_serialize.hpp @@ -0,0 +1,72 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#ifndef NNTI_SERIALIZE_HPP +#define NNTI_SERIALIZE_HPP + +#include "nnti/nntiConfig.h" + +#include + +#if NNTI_USE_XDR == 1 +#include "nnti/serializers/xdr/nnti_xdr.hpp" +#elif NNTI_USE_CEREAL == 1 +#include "nnti/serializers/cereal/nnti_cereal.hpp" +#endif + +namespace nnti { +namespace serialize { + +NNTI_datatype_t +get_datatype( + char *packed_buf, + const uint64_t packed_len); + +uint64_t +packed_peer_size( + NNTI_peer_p_t *packable); + +NNTI_result_t +pack_peer( + NNTI_peer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen, + uint64_t *packed_len); + +NNTI_result_t +unpack_peer( + NNTI_peer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen); + +NNTI_result_t +free_peer( + NNTI_peer_p_t *packable); + +uint64_t +packed_buffer_size( + NNTI_buffer_p_t *packable); + +NNTI_result_t +pack_buffer( + NNTI_buffer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen, + uint64_t *packed_len); + +NNTI_result_t +unpack_buffer( + NNTI_buffer_p_t *packable, + char *packed_buf, + const uint64_t packed_buflen); + +NNTI_result_t +free_buffer( + NNTI_buffer_p_t *packable); + + +} // namespace serialize +} // namespace nnti + +#endif /* NNTI_SERIALIZE_HPP */ diff --git a/src/nnti/nnti_transport.cpp b/src/nnti/nnti_transport.cpp new file mode 100644 index 0000000..e48f91e --- /dev/null +++ b/src/nnti/nnti_transport.cpp @@ -0,0 +1,70 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/nnti_transport.hpp" +#include "nnti/nnti_pid.hpp" + +namespace nnti { +namespace transports { + +/** + * @brief Convert an NNTI URL to an NNTI_process_id_t. + * + * \param[in] url A string that describes this process's location on the network. + * \param[out] pid Compact binary representation of a process's location on the network. + * \return A result code (NNTI_OK or an error) + */ +NNTI_result_t +transport::dt_url_to_pid( + const char *url, + NNTI_process_id_t *pid) +{ + NNTI_result_t rc=NNTI_OK; + + *pid = nnti::datatype::nnti_pid::to_pid(std::string(url)); + + return(rc); +} + +/** + * @brief Convert an NNTI_process_id_t to an NNTI URL. + * + * \param[in] pid Compact binary representation of a process's location on the network. + * \param[out] url A string that describes this process's location on the network. + * \param[in] maxlen The maximum length of the URL that can be written into url. + * \return A result code (NNTI_OK or an error) + */ +NNTI_result_t +transport::dt_pid_to_url( + const NNTI_process_id_t pid, + char *url, + const uint64_t maxlen) +{ + NNTI_result_t rc=NNTI_OK; + + std::string s = nnti::datatype::nnti_pid::to_url(pid); + + strncpy(url, s.c_str(), maxlen); + url[maxlen-1] = '\0'; + + return(rc); +} + +transport* +transport::to_obj( + NNTI_transport_t trans_hdl) +{ + return (transport *)trans_hdl; +} + +NNTI_transport_t +transport::to_hdl( + transport *transport) +{ + return (NNTI_transport_t)transport; +} + +} /* namespace transports */ +} /* namespace nnti */ diff --git a/src/nnti/nnti_transport.hpp b/src/nnti/nnti_transport.hpp index be0670e..11cfde0 100644 --- a/src/nnti/nnti_transport.hpp +++ b/src/nnti/nnti_transport.hpp @@ -16,9 +16,7 @@ #include -#include -#include -#include +#include "nnti/nnti_types.h" namespace nnti { @@ -347,14 +345,7 @@ class transport { static NNTI_result_t dt_url_to_pid( const char *url, - NNTI_process_id_t *pid) - { - NNTI_result_t rc=NNTI_OK; - - *pid = nnti::datatype::nnti_pid::to_pid(std::string(url)); - - return(rc); - } + NNTI_process_id_t *pid); /** * @brief Convert an NNTI_process_id_t to an NNTI URL. @@ -368,17 +359,7 @@ class transport { dt_pid_to_url( const NNTI_process_id_t pid, char *url, - const uint64_t maxlen) - { - NNTI_result_t rc=NNTI_OK; - - std::string s = nnti::datatype::nnti_pid::to_url(pid); - - strncpy(url, s.c_str(), maxlen); - url[maxlen-1] = '\0'; - - return(rc); - } + const uint64_t maxlen); /** * @brief Convert an NNTI peer to an NNTI_process_id_t. @@ -545,19 +526,12 @@ class transport { const int64_t timeout, NNTI_status_t *status) = 0; - static inline transport* + static transport* to_obj( - NNTI_transport_t trans_hdl) - { - return (transport *)trans_hdl; - } - - static inline NNTI_transport_t + NNTI_transport_t trans_hdl); + static NNTI_transport_t to_hdl( - transport *transport) - { - return (NNTI_transport_t)transport; - } + transport *transport); }; } /* namespace transports */ diff --git a/src/nnti/nnti_types.h b/src/nnti/nnti_types.h index 02334d0..08533fa 100644 --- a/src/nnti/nnti_types.h +++ b/src/nnti/nnti_types.h @@ -11,6 +11,10 @@ /********** Constants **********/ +/** + * @brief Length of a URL + */ +#define NNTI_URL_LEN 128 /** * @brief Length of a hostname */ @@ -30,11 +34,52 @@ /** * @brief The number of transport mechanisms supported by NNTI. */ -#define NNTI_TRANSPORT_COUNT 2 +#define NNTI_TRANSPORT_COUNT 4 /********** Enumerations **********/ +enum NNTI_datatype_t { + NNTI_dt_peer = 1111, + NNTI_dt_buffer = 1112, + NNTI_dt_work_id = 1113, + NNTI_dt_work_request = 1114, + NNTI_dt_transport = 1115, + NNTI_dt_event_queue = 1116, + NNTI_dt_callback = 1117 +}; + +/** + * @brief Enumerator of the transport mechanisms supported by NNTI. + * + * The \ref NNTI_transport_id_t enumerator provides integer values + * to represent the supported transport mechanisms. + */ +enum NNTI_transport_id_t { + /** @brief No operations permitted. */ + NNTI_TRANSPORT_NULL, + + /** @brief Use Infiniband to transfer rpc requests. */ + NNTI_TRANSPORT_IBVERBS, + + /** @brief Use Cray ugni to transfer rpc requests. */ + NNTI_TRANSPORT_UGNI, + + /** @brief Use MPI to transfer rpc requests. */ + NNTI_TRANSPORT_MPI +}; +typedef enum NNTI_transport_id_t NNTI_transport_id_t; + +#if defined(NNTI_BUILD_IBVERBS) +#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_IBVERBS +#elif defined(NNTI_BUILD_UGNI) +#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_UGNI +#elif defined(NNTI_BUILD_MPI) +#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_MPI +#else +#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_NULL +#endif + /** * @brief Enumerator of results that NNTI functions could generate. * @@ -78,7 +123,7 @@ enum NNTI_result_t { /** @brief Not initialized. */ NNTI_ENOTINIT, - /** @brief Insufficient priveleges to perform operation. */ + /** @brief Insufficient privileges to perform operation. */ NNTI_EPERM, /** @brief Either an operation was interrupted by a signal @@ -192,7 +237,9 @@ enum NNTI_buffer_flags_t { /** @brief a remote process/NIC can write to this buffer */ NNTI_BF_REMOTE_WRITE = 8, /** @brief SENDs to this memory occur at the offset+length of the last SEND */ - NNTI_BF_QUEUING = 16 + NNTI_BF_QUEUING = 16, + NNTI_BF_LOCAL_ATOMIC = 32, + NNTI_BF_REMOTE_ATOMIC = 64 }; typedef enum NNTI_buffer_flags_t NNTI_buffer_flags_t; @@ -208,6 +255,8 @@ typedef uint64_t NNTI_event_queue_t; /** @brief Opaque handle to a process */ typedef uint64_t NNTI_peer_t; +typedef uint64_t NNTI_process_id_t; + /** @brief Opaque handle to a transport */ typedef uint64_t NNTI_transport_t; diff --git a/src/nnti/nnti_url.cpp b/src/nnti/nnti_url.cpp index b9c2f1f..018ad42 100644 --- a/src/nnti/nnti_url.cpp +++ b/src/nnti/nnti_url.cpp @@ -20,7 +20,7 @@ #include "nnti/nnti_url.hpp" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_logger.hpp" #include "nnti/nnti_pid.hpp" #include "nnti/nnti_types.h" diff --git a/src/nnti/nnti_url.hpp b/src/nnti/nnti_url.hpp index 66091d9..3fc6e08 100644 --- a/src/nnti/nnti_url.hpp +++ b/src/nnti/nnti_url.hpp @@ -21,7 +21,7 @@ #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_logger.hpp" #include "nnti/nnti_pid.hpp" #include "nnti/nnti_types.h" diff --git a/src/nnti/nnti_wid.cpp b/src/nnti/nnti_wid.cpp index 0224097..795e127 100644 --- a/src/nnti/nnti_wid.cpp +++ b/src/nnti/nnti_wid.cpp @@ -18,7 +18,253 @@ #include +#include + +#include +#include +#include +#include +#include + #include "nnti/nnti_wid.hpp" +#include "nnti/nnti_types.h" +#include "nnti/nnti_datatype.hpp" +#include "nnti/nnti_wr.hpp" +#include "nnti/nnti_threads.h" +#include "nnti/nnti_serialize.hpp" +#include "nnti/nnti_util.hpp" + + std::atomic nnti::datatype::nnti_work_id::next_id_ = {1}; + +namespace nnti { +namespace datatype { + +nnti_work_id::nnti_work_id( + nnti::transports::transport *transport) +: nnti_datatype(transport, + NNTI_dt_work_id), + wr_(transport), + complete_(false) +{ + nthread_lock_init(&lock_); + id_ = next_id_.fetch_add(1); + return; +} +nnti_work_id::nnti_work_id( + nnti::transports::transport *transport, + NNTI_work_request_t &wr) +: nnti_datatype(transport, + NNTI_dt_work_id), + wr_(transport, + wr), + complete_(false) +{ + nthread_lock_init(&lock_); + id_ = next_id_.fetch_add(1); + + return; +} +nnti_work_id::nnti_work_id( + nnti::datatype::nnti_work_request &wr) +: nnti_datatype(wr.transport(), + NNTI_dt_work_id), + wr_(wr), + complete_(false) +{ + nthread_lock_init(&lock_); + id_ = next_id_.fetch_add(1); + + return; +} + +nnti_work_id::~nnti_work_id() { + nthread_lock_fini(&lock_); + return; +} + +uint32_t +nnti_work_id::id(void) const +{ + return id_; +} +nnti_work_request& +nnti_work_id::wr(void) +{ + return wr_; +} + +int +nnti_work_id::lock() +{ + return nthread_lock(&lock_); +} +int +nnti_work_id::unlock() +{ + return nthread_unlock(&lock_); +} + +std::string +nnti_work_id::toString(void) { + std::stringstream out; + out << "id_==" << id_; + return out.str(); +} +bool +nnti_work_id::is_complete(void) const +{ + return complete_; +} + + +nnti_work_id_queue::nnti_work_id_queue() +{ + nthread_lock_init(&lock_); +} +nnti_work_id_queue::~nnti_work_id_queue() +{ + nthread_lock_fini(&lock_); +} + +void +nnti_work_id_queue::push( + nnti_work_id *wr) +{ + nthread_lock(&lock_); + queue_.push_back(wr); + nthread_unlock(&lock_); + log_debug("nnti_wr", "pushed wr=%p", wr); +} +nnti_work_id * +nnti_work_id_queue::pop() +{ + nthread_lock(&lock_); + nnti_work_id *wr=queue_.front(); + queue_.pop_front(); + nthread_unlock(&lock_); + log_debug("nnti_wr", "popped wr=%p", wr); + return(wr); +} +nnti_work_id * +nnti_work_id_queue::front() +{ + nthread_lock(&lock_); + nnti_work_id *wr=queue_.front(); + nthread_unlock(&lock_); + log_debug("nnti_wr", "fronted wr=%p", wr); + return(wr); +} + +bool +nnti_work_id_queue::empty() +{ + nthread_lock(&lock_); + bool rc=queue_.empty(); + nthread_unlock(&lock_); + return(rc); +} + +nnti_work_id * +nnti_work_id_queue::first_incomplete() +{ + nnti_work_id *wr=NULL; + + nthread_lock(&lock_); + log_debug("nnti_wr", "wr queue_.size()==%d", queue_.size()); + if (!queue_.empty()) { + std::deque::iterator i; + for (i=queue_.begin(); i != queue_.end(); i++) { + assert(*i); + if (!(*i)->is_complete()) { + wr=*i; + break; + } + } + } + nthread_unlock(&lock_); + log_debug("nnti_wr", "first incomplete wr=%p", wr); + + return(wr); +} + +nnti_work_id_queue_iter_t +nnti_work_id_queue::begin() +{ + return queue_.begin(); +} +nnti_work_id_queue_iter_t +nnti_work_id_queue::end() +{ + return queue_.end(); +} + + +nnti_work_id_map::nnti_work_id_map() +{ + nthread_lock_init(&lock_); +} +nnti_work_id_map::~nnti_work_id_map() +{ + nthread_lock_fini(&lock_); +} + +void +nnti_work_id_map::insert( + nnti_work_id *wr) +{ + nthread_lock(&lock_); + assert(map_.find(wr->id()) == map_.end()); + map_[wr->id()] = wr; + nthread_unlock(&lock_); + return; +} + +nnti_work_id * +nnti_work_id_map::get( + uint32_t id) +{ + nnti_work_id *wr=NULL; + nthread_lock(&lock_); + if (map_.find(id) != map_.end()) { + wr = map_[id]; + } + nthread_unlock(&lock_); + return(wr); +} + +nnti_work_id * +nnti_work_id_map::remove( + nnti_work_id *wr) +{ + return(remove(wr->id())); +} +nnti_work_id * +nnti_work_id_map::remove( + uint32_t id) +{ + nnti_work_id *wr=NULL; + nthread_lock(&lock_); + if (map_.find(id) != map_.end()) { + wr = map_[id]; + } + if (wr != NULL) { + map_.erase(id); + } + nthread_unlock(&lock_); + return(wr); +} + +bool +nnti_work_id_map::empty() +{ + nthread_lock(&lock_); + bool rc=map_.empty(); + nthread_unlock(&lock_); + return(rc); +} + +} /* namespace datatype */ +} /* namespace nnti */ diff --git a/src/nnti/nnti_wid.hpp b/src/nnti/nnti_wid.hpp index 718446f..c50ee34 100644 --- a/src/nnti/nnti_wid.hpp +++ b/src/nnti/nnti_wid.hpp @@ -17,21 +17,14 @@ #include "nnti/nntiConfig.h" -#include - #include #include #include -#include -#include #include "nnti/nnti_types.h" - #include "nnti/nnti_datatype.hpp" #include "nnti/nnti_wr.hpp" - #include "nnti/nnti_threads.h" -#include "nnti/nnti_util.hpp" namespace nnti { @@ -52,81 +45,29 @@ class nnti_work_id public: nnti_work_id( - nnti::transports::transport *transport) - : nnti_datatype(transport, - NNTI_dt_work_id), - wr_(transport), - complete_(false) - { - nthread_lock_init(&lock_); - id_ = next_id_.fetch_add(1); - return; - } + nnti::transports::transport *transport); nnti_work_id( nnti::transports::transport *transport, - NNTI_work_request_t &wr) - : nnti_datatype(transport, - NNTI_dt_work_id), - wr_(transport, - wr), - complete_(false) - { - nthread_lock_init(&lock_); - id_ = next_id_.fetch_add(1); - - return; - } + NNTI_work_request_t &wr); nnti_work_id( - nnti::datatype::nnti_work_request &wr) - : nnti_datatype(wr.transport(), - NNTI_dt_work_id), - wr_(wr), - complete_(false) - { - nthread_lock_init(&lock_); - id_ = next_id_.fetch_add(1); - - return; - } - - ~nnti_work_id() override { - nthread_lock_fini(&lock_); - return; - } + nnti::datatype::nnti_work_request &wr); + + ~nnti_work_id(); virtual uint32_t - id(void) const - { - return id_; - } + id(void) const; virtual nnti_work_request& - wr(void) - { - return wr_; - } + wr(void); virtual int - lock() - { - return nthread_lock(&lock_); - } + lock(); virtual int - unlock() - { - return nthread_unlock(&lock_); - } - - std::string - toString(void) override { - std::stringstream out; - out << "id_==" << id_; - return out.str(); - } + unlock(); + + std::string + toString(void) override; virtual bool - is_complete(void) const - { - return complete_; - } + is_complete(void) const; }; typedef std::deque::iterator nnti_work_id_queue_iter_t; @@ -137,86 +78,27 @@ class nnti_work_id_queue { nthread_lock_t lock_; public: - nnti_work_id_queue() - { - nthread_lock_init(&lock_); - } - virtual ~nnti_work_id_queue() - { - nthread_lock_fini(&lock_); - } + nnti_work_id_queue(); + virtual ~nnti_work_id_queue(); void push( - nnti_work_id *wr) - { - nthread_lock(&lock_); - queue_.push_back(wr); - nthread_unlock(&lock_); - log_debug("nnti_wr", "pushed wr=%p", wr); - } + nnti_work_id *wr); nnti_work_id * - pop() - { - nthread_lock(&lock_); - nnti_work_id *wr=queue_.front(); - queue_.pop_front(); - nthread_unlock(&lock_); - log_debug("nnti_wr", "popped wr=%p", wr); - return(wr); - } + pop(); nnti_work_id * - front() - { - nthread_lock(&lock_); - nnti_work_id *wr=queue_.front(); - nthread_unlock(&lock_); - log_debug("nnti_wr", "fronted wr=%p", wr); - return(wr); - } + front(); bool - empty() - { - nthread_lock(&lock_); - bool rc=queue_.empty(); - nthread_unlock(&lock_); - return(rc); - } + empty(); nnti_work_id * - first_incomplete() - { - nnti_work_id *wr=NULL; - - nthread_lock(&lock_); - log_debug("nnti_wr", "wr queue_.size()==%d", queue_.size()); - if (!queue_.empty()) { - std::deque::iterator i; - for (i=queue_.begin(); i != queue_.end(); i++) { - assert(*i); - if (!(*i)->is_complete()) { - wr=*i; - break; - } - } - } - nthread_unlock(&lock_); - log_debug("nnti_wr", "first incomplete wr=%p", wr); - - return(wr); - } + first_incomplete(); nnti_work_id_queue_iter_t - begin() - { - return queue_.begin(); - } + begin(); nnti_work_id_queue_iter_t - end() - { - return queue_.end(); - } + end(); }; class nnti_work_id_map { @@ -227,69 +109,26 @@ class nnti_work_id_map { nthread_lock_t lock_; public: - nnti_work_id_map() -{ - nthread_lock_init(&lock_); - } - virtual ~nnti_work_id_map() - { - nthread_lock_fini(&lock_); - } + nnti_work_id_map(); + virtual ~nnti_work_id_map(); void insert( - nnti_work_id *wr) - { - nthread_lock(&lock_); - assert(map_.find(wr->id()) == map_.end()); - map_[wr->id()] = wr; - nthread_unlock(&lock_); - return; - } + nnti_work_id *wr); nnti_work_id * get( - uint32_t id) - { - nnti_work_id *wr=NULL; - nthread_lock(&lock_); - if (map_.find(id) != map_.end()) { - wr = map_[id]; - } - nthread_unlock(&lock_); - return(wr); - } + uint32_t id); nnti_work_id * remove( - nnti_work_id *wr) - { - return(remove(wr->id())); - } + nnti_work_id *wr); nnti_work_id * remove( - uint32_t id) - { - nnti_work_id *wr=NULL; - nthread_lock(&lock_); - if (map_.find(id) != map_.end()) { - wr = map_[id]; - } - if (wr != NULL) { - map_.erase(id); - } - nthread_unlock(&lock_); - return(wr); - } + uint32_t id); bool - empty() - { - nthread_lock(&lock_); - bool rc=map_.empty(); - nthread_unlock(&lock_); - return(rc); - } + empty(); }; } /* namespace datatype */ diff --git a/src/nnti/nnti_wr.cpp b/src/nnti/nnti_wr.cpp new file mode 100644 index 0000000..f526919 --- /dev/null +++ b/src/nnti/nnti_wr.cpp @@ -0,0 +1,162 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/nntiConfig.h" + +#include +#include + +#include "nnti/nnti_wr.hpp" +#include "nnti/nnti_transport.hpp" +#include "nnti/nnti_datatype.hpp" +#include "nnti/nnti_callback.hpp" +#include "nnti/nnti_peer.hpp" + + +namespace nnti { +namespace datatype { + + nnti_work_request::nnti_work_request( + nnti::transports::transport *transport) + : cb_(transport), + nnti_datatype(transport, + NNTI_dt_work_request) + { + return; + } + nnti_work_request::nnti_work_request( + nnti::transports::transport *transport, + NNTI_work_request_t &wr) + : cb_(transport, + wr.callback), + nnti_datatype(transport, + NNTI_dt_work_request) + { + wr_ = wr; + + return; + } + nnti_work_request::nnti_work_request( + nnti::transports::transport *transport, + NNTI_work_request_t &wr, + nnti::datatype::nnti_event_callback cb) + : cb_(cb), + nnti_datatype(transport, + NNTI_dt_work_request) + { + wr_ = wr; + + return; + } + + nnti_work_request::~nnti_work_request() + { + return; + } + + const NNTI_work_request_t& + nnti_work_request::wr(void) const + { + return wr_; + } + + NNTI_peer_t + nnti_work_request::peer(void) const + { + return wr_.peer; + } + const NNTI_process_id_t + nnti_work_request::peer_pid(void) const + { + nnti::datatype::nnti_peer *peer=(nnti::datatype::nnti_peer*)wr_.peer; + return peer->pid(); + } + + NNTI_op_t + nnti_work_request::op(void) const + { + return wr_.op; + } + NNTI_op_flags_t + nnti_work_request::flags(void) const + { + return wr_.flags; + } + + const NNTI_buffer_t& + nnti_work_request::local_hdl(void) const + { + return wr_.local_hdl; + } + const NNTI_buffer_t& + nnti_work_request::remote_hdl(void) const + { + return wr_.remote_hdl; + } + + uint64_t + nnti_work_request::local_offset(void) const + { + return wr_.local_offset; + } + uint64_t + nnti_work_request::remote_offset(void) const + { + return wr_.remote_offset; + } + uint64_t + nnti_work_request::length(void) const + { + return wr_.length; + } + uint64_t + nnti_work_request::operand1(void) const + { + return wr_.operand1; + } + uint64_t + nnti_work_request::operand2(void) const + { + return wr_.operand2; + } + + NNTI_event_queue_t + nnti_work_request::alt_eq(void) const + { + return wr_.alt_eq; + } + + nnti::datatype::nnti_event_callback & + nnti_work_request::callback(void) + { + return cb_; + } + void * + nnti_work_request::cb_context(void) const + { + return wr_.cb_context; + } + NNTI_result_t + nnti_work_request::invoke_cb(NNTI_event_t *event) { + log_debug("nnti_event_queue", "invoking the WR callback"); + return cb_.invoke(event, wr_.cb_context); + } + + void * + nnti_work_request::event_context(void) const + { + return wr_.event_context; + } + + std::string + nnti_work_request::toString(void) const + { + std::stringstream out; + out << "cb_==" << &cb_; + return out.str(); + } + +} /* namespace datatype */ +} /* namespace nnti */ diff --git a/src/nnti/nnti_wr.hpp b/src/nnti/nnti_wr.hpp index 6cceb91..5b625ed 100644 --- a/src/nnti/nnti_wr.hpp +++ b/src/nnti/nnti_wr.hpp @@ -8,15 +8,10 @@ #include "nnti/nntiConfig.h" -#include -#include - #include "nnti/nnti_types.h" - #include "nnti/nnti_transport.hpp" #include "nnti/nnti_datatype.hpp" #include "nnti/nnti_callback.hpp" -#include "nnti/nnti_peer.hpp" namespace nnti { @@ -33,143 +28,60 @@ class nnti_work_request public: nnti_work_request( - nnti::transports::transport *transport) - : cb_(transport), - nnti_datatype(transport, - NNTI_dt_work_request) - { - return; - } + nnti::transports::transport *transport); nnti_work_request( nnti::transports::transport *transport, - NNTI_work_request_t &wr) - : cb_(transport, - wr.callback), - nnti_datatype(transport, - NNTI_dt_work_request) - { - wr_ = wr; - - return; - } + NNTI_work_request_t &wr); nnti_work_request( nnti::transports::transport *transport, NNTI_work_request_t &wr, - nnti::datatype::nnti_event_callback cb) - : cb_(cb), - nnti_datatype(transport, - NNTI_dt_work_request) - { - wr_ = wr; - - return; - } - - ~nnti_work_request() override { - return; - } + nnti::datatype::nnti_event_callback cb); + ~nnti_work_request() override; virtual const NNTI_work_request_t& - wr(void) const - { - return wr_; - } + wr(void) const; virtual NNTI_peer_t - peer(void) const - { - return wr_.peer; - } + peer(void) const; virtual const NNTI_process_id_t - peer_pid(void) const - { - nnti::datatype::nnti_peer *peer=(nnti::datatype::nnti_peer*)wr_.peer; - return peer->pid(); - } + peer_pid(void) const; NNTI_op_t - op(void) const - { - return wr_.op; - } + op(void) const; NNTI_op_flags_t - flags(void) const - { - return wr_.flags; - } + flags(void) const; const NNTI_buffer_t& - local_hdl(void) const - { - return wr_.local_hdl; - } + local_hdl(void) const; const NNTI_buffer_t& - remote_hdl(void) const - { - return wr_.remote_hdl; - } + remote_hdl(void) const; uint64_t - local_offset(void) const - { - return wr_.local_offset; - } + local_offset(void) const; uint64_t - remote_offset(void) const - { - return wr_.remote_offset; - } + remote_offset(void) const; uint64_t - length(void) const - { - return wr_.length; - } + length(void) const; uint64_t - operand1(void) const - { - return wr_.operand1; - } + operand1(void) const; uint64_t - operand2(void) const - { - return wr_.operand2; - } + operand2(void) const; NNTI_event_queue_t - alt_eq(void) const - { - return wr_.alt_eq; - } + alt_eq(void) const; nnti::datatype::nnti_event_callback & - callback(void) - { - return cb_; - } + callback(void); void * - cb_context(void) const - { - return wr_.cb_context; - } - NNTI_result_t invoke_cb(NNTI_event_t *event) { - log_debug("nnti_event_queue", "invoking the WR callback"); - return cb_.invoke(event, wr_.cb_context); - } + cb_context(void) const; + NNTI_result_t + invoke_cb(NNTI_event_t *event); void * - event_context(void) const - { - return wr_.event_context; - } + event_context(void) const; virtual std::string - toString(void) const - { - std::stringstream out; - out << "cb_==" << &cb_; - return out.str(); - } - + toString(void) const; }; } /* namespace datatype */ diff --git a/src/nnti/serializers/cereal/nnti_cereal.hpp b/src/nnti/serializers/cereal/nnti_cereal.hpp new file mode 100644 index 0000000..8951402 --- /dev/null +++ b/src/nnti/serializers/cereal/nnti_cereal.hpp @@ -0,0 +1,6 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/serializers/cereal/nnti_packable.hpp" diff --git a/src/nnti/serializers/cereal/nnti_packable.hpp b/src/nnti/serializers/cereal/nnti_packable.hpp new file mode 100644 index 0000000..cb7d620 --- /dev/null +++ b/src/nnti/serializers/cereal/nnti_packable.hpp @@ -0,0 +1,402 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#ifndef NNTI_PACKABLE_HPP +#define NNTI_PACKABLE_HPP + +#include +#include +#include + +#include "nnti/nntiConfig.h" +#include "nnti/nnti_types.h" + + +/*********** TCP/IP address types ***********/ + +/** + * @brief Binary encoding of a TCP/IP host address. + * + * The \ref NNTI_ip_addr type identifies a particular node. + */ +using NNTI_ip_addr = uint32_t; + +/** + * @brief TCP port in NBO. + * + * The \ref NNTI_tcp_addr type identifies a particular port. + */ +using NNTI_tcp_port = uint16_t; + + +/*********** NULL Process Types ***********/ + +/** + * @brief Remote process identifier for the NULL transport. + * + */ +struct NNTI_null_process_p_t { + int i; /* unused, but empty structs are not allowed */ + + template + void serialize(Archive & archive) + { + archive( i ); + } +}; + + +/*********** IB Process Types ***********/ + +/** + * @brief Remote process identifier for IB. + * + * The \ref NNTI_ib_process_t identifies a particular process + * on a particular node. If a connection has been established to the + * represented process, then that connection is identified by 'qp_num'. + */ +struct NNTI_ib_process_p_t { + /** @brief IP address encoded in Network Byte Order */ + NNTI_ip_addr addr; + /** @brief TCP port encoded in Network Byte Order */ + NNTI_tcp_port port; + + template + void serialize(Archive & archive) + { + archive( addr, port ); + } +}; + + +/*********** Gemini Process Types ***********/ + +/** + * @brief The instance ID of a Gemini process. + * + * The \ref NNTI_inst_id type identifies a particular process + * within a communication domain. + */ +using NNTI_instance_id = uint32_t; + +/** + * @brief Remote process identifier for Gemini. + * + * The \ref NNTI_ugni_process_t identifies a particular process + * on a particular node. If a connection has been established to the + * represented process, then that connection is identified by 'inst_id'. + */ +struct NNTI_ugni_process_p_t { + /** @brief IP address encoded in Network Byte Order */ + NNTI_ip_addr addr; + /** @brief TCP port encoded in Network Byte Order */ + NNTI_tcp_port port; + /** @brief Gemini process instance ID */ + NNTI_instance_id inst_id; + + template + void serialize(Archive & archive) + { + archive( addr, port, inst_id ); + } +}; + + +/*********** MPI Process Types ***********/ + +/** + * @brief Remote process identifier for MPI. + * + * The \ref NNTI_mpi_process_t identifies a particular process + * on a particular node. + */ +struct NNTI_mpi_process_p_t { + /** @brief MPI rank. */ + int rank; + + template + void serialize(Archive & archive) + { + archive( rank ); + } +}; + + +/*********** Local Process Types ***********/ + +/** + * @brief Remote process identifier for the Local transport. + * + */ +struct NNTI_local_process_p_t { + int i; /* unused, but empty structs are not allowed */ + + template + void serialize(Archive & archive) + { + archive( i ); + } +}; + + +/*********** Remote Process Union ***********/ + +/** + * @brief A structure to represent a remote processes. + * + * The NNTI_remote_process_t structure contains the + * transport specific info needed to identify a process running + * on a remote node. + */ +struct NNTI_remote_process_p_t { + NNTI_transport_id_t transport_id; + union { + /** @brief The NULL representation of a process on the network. */ + NNTI_null_process_p_t null; + /** @brief The IB representation of a process on the network. */ + NNTI_ib_process_p_t ib; + /** @brief The Cray UGNI representation of a process on the network. */ + NNTI_ugni_process_p_t ugni; + /** @brief The MPI representation of a process on the network. */ + NNTI_mpi_process_p_t mpi; + } NNTI_remote_process_p_t_u; + + template + void serialize( Archive & archive ) + { + archive( transport_id ); + switch( transport_id ) + { + case NNTI_TRANSPORT_NULL: + archive( NNTI_remote_process_p_t_u.null ); + break; + /** @brief The IB representation of a process on the network. */ + case NNTI_TRANSPORT_IBVERBS: + archive( NNTI_remote_process_p_t_u.ib ); + break; + /** @brief The Cray UGNI representation of a process on the network. */ + case NNTI_TRANSPORT_UGNI: + archive( NNTI_remote_process_p_t_u.ugni ); + break; + /** @brief The MPI representation of a process on the network. */ + case NNTI_TRANSPORT_MPI: + archive( NNTI_remote_process_p_t_u.mpi ); + break; + } + } +}; + + +/*********** Peer Type ***********/ + +typedef uint64_t NNTI_process_id_p_t; + +/** + * @brief Handle to an NNTI process. + * + * This is the datatype used by NNTI clients to reference another process. + * Use this handle to move data to/from the process. + */ +struct NNTI_peer_p_t { + /** @brief binary encoding of a process's URL */ + NNTI_process_id_p_t pid; + + /** @brief binary encoding of a process on the network */ + NNTI_remote_process_p_t peer; + + template + void serialize(Archive & archive) + { + archive( pid, peer ); + } +}; + + +/*********** NULL RDMA Address Types ***********/ + +/** + * @brief RDMA address used for the NULL transport. + */ +struct NNTI_null_rdma_addr_p_t { + int i; /* unused, but empty structs are not allowed */ + + template + void serialize(Archive & archive) + { + archive( i ); + } +}; + + +/*********** IB RDMA Address Types ***********/ + +/** + * @brief RDMA address used for the InfiniBand implementation. + */ +struct NNTI_ib_rdma_addr_p_t { + /** @brief Address of the memory buffer cast to a uint64_t. */ + uint64_t buf; + /** @brief The key that a remote processes needs to access this buffer. */ + uint32_t key; + /** @brief Size of the the memory buffer. */ + uint32_t size; + + template + void serialize(Archive & archive) + { + archive( buf, key, size ); + } +}; + + +/*********** Gemini RDMA Address Types ***********/ + +struct NNTI_ugni_mem_hdl_p_t { + uint64_t qword1; + uint64_t qword2; + + template + void serialize(Archive & archive) + { + archive( qword1, qword2 ); + } +}; + +/** + * @brief RDMA address used for the Gemini implementation. + */ +struct NNTI_ugni_rdma_addr_p_t { + /** @brief Address of the memory buffer cast to a uint64_t . */ + uint64_t buf; + /** @brief Size of the the memory buffer. */ + uint32_t size; + /** @brief The key that a remote processes needs to access this buffer. */ + NNTI_ugni_mem_hdl_p_t mem_hdl; + + template + void serialize(Archive & archive) + { + archive( buf, size, mem_hdl ); + } +}; + + +/*********** MPI RDMA Address Types ***********/ + +/** + * @brief Definition for match bits in MPI. + */ +using NNTI_match_bits = uint64_t; + +/** + * @brief RDMA address used for the MPI implementation. + */ +struct NNTI_mpi_rdma_addr_p_t { + /** @brief The MPI tag for RTR/RTS msg. */ + NNTI_match_bits cmd_tag; + /** @brief The MPI tag for GET data msg. */ + NNTI_match_bits get_data_tag; + /** @brief The MPI tag for PUT data msg. */ + NNTI_match_bits put_data_tag; + /** @brief The MPI tag for ATOMIC data msg. */ + NNTI_match_bits atomic_data_tag; + + /** @brief Address of the memory buffer cast to a uint64_t . */ + uint64_t buf; + /** @brief Size of the the memory buffer. */ + uint32_t size; + + template + void serialize(Archive & archive) + { + archive( cmd_tag, get_data_tag, put_data_tag, atomic_data_tag, buf, size ); + } +}; + + +/*********** Local RDMA Address Types ***********/ + +/** + * @brief RDMA address used for the Local transport. + */ +struct NNTI_local_rdma_addr_p_t { + int i; /* unused, but empty structs are not allowed */ + + template + void serialize(Archive & archive) + { + archive( i ); + } +}; + + +/*********** Remote Address Union ***********/ + +/** + * @brief A structure to represent a remote memory region. + * + * The NNTI_remote_addr_t structure contains the + * transport specific info needed to identify a memory region + * on a remote node. + */ +struct NNTI_remote_addr_p_t { + NNTI_transport_id_t transport_id; + union { + /** @brief The NULL representation of a memory region. */ + NNTI_null_rdma_addr_p_t null; + /** @brief The IB representation of a memory region. */ + NNTI_ib_rdma_addr_p_t ib; + /** @brief The Cray UGNI representation of a memory region. */ + NNTI_ugni_rdma_addr_p_t ugni; + /** @brief The MPI representation of a memory region. */ + NNTI_mpi_rdma_addr_p_t mpi; + } NNTI_remote_addr_p_t_u; + + template + void serialize( Archive & archive ) + { + archive( transport_id ); + switch( transport_id ) + { + case NNTI_TRANSPORT_NULL: + archive( NNTI_remote_addr_p_t_u.null ); + break; + /** @brief The IB representation of a process on the network. */ + case NNTI_TRANSPORT_IBVERBS: + archive( NNTI_remote_addr_p_t_u.ib ); + break; + /** @brief The Cray UGNI representation of a process on the network. */ + case NNTI_TRANSPORT_UGNI: + archive( NNTI_remote_addr_p_t_u.ugni ); + break; + /** @brief The MPI representation of a process on the network. */ + case NNTI_TRANSPORT_MPI: + archive( NNTI_remote_addr_p_t_u.mpi ); + break; + } + } +}; + +/*********** Buffer Type ***********/ + +/** + * @brief handle to a memory buffer prepared by NNTI_register_memory + * + * The NNTI_buffer_t structure contains the + * location of a buffer on the network. This is all the info + * a peer needs to put/get this buffer. + */ +struct NNTI_buffer_p_t { + /** @brief Segments that compose a complete buffer. */ + NNTI_remote_addr_p_t buffer; + uint8_t flags; + + template + void serialize(Archive & archive) + { + archive( buffer, flags ); + } +}; + +#endif /* NNTI_PACKABLE_HPP */ diff --git a/src/nnti/serializers/xdr/nnti_packable.x b/src/nnti/serializers/xdr/nnti_packable.x index e4880b4..fe2008f 100644 --- a/src/nnti/serializers/xdr/nnti_packable.x +++ b/src/nnti/serializers/xdr/nnti_packable.x @@ -1,65 +1,16 @@ #ifdef RPC_HDR %#include "nnti/nntiConfig.h" -%#include "nnti/nnti_xdr.h" +%#include "nnti/serializers/xdr/nnti_xdr.h" %#include #endif #ifdef RPC_XDR %#include "nnti/nntiConfig.h" -%#include "nnti/nnti_xdr.h" +%#include "nnti/serializers/xdr/nnti_xdr.h" #endif - -enum NNTI_datatype_t { - NNTI_dt_peer = 1111, - NNTI_dt_buffer = 1112, - NNTI_dt_work_id = 1113, - NNTI_dt_work_request = 1114, - NNTI_dt_transport = 1115, - NNTI_dt_event_queue = 1116, - NNTI_dt_callback = 1117 -}; - - -/** - * @brief Enumerator of the transport mechanisms supported by NNTI. - * - * The \ref NNTI_transport_id_t enumerator provides integer values - * to represent the supported transport mechanisms. - */ -enum NNTI_transport_id_t { - /** @brief No operations permitted. */ - NNTI_TRANSPORT_NULL, - - /** @brief Use Infiniband to transfer rpc requests. */ - NNTI_TRANSPORT_IBVERBS, - - /** @brief Use Cray ugni to transfer rpc requests. */ - NNTI_TRANSPORT_UGNI, - - /** @brief Use MPI to transfer rpc requests. */ - NNTI_TRANSPORT_MPI -}; - - -#ifdef RPC_HDR -%#if defined(NNTI_BUILD_IBVERBS) -%#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_IBVERBS -%#elif defined(NNTI_BUILD_UGNI) -%#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_UGNI -%#elif defined(NNTI_BUILD_MPI) -%#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_MPI -%#else -%#define NNTI_DEFAULT_TRANSPORT NNTI_TRANSPORT_NULL -%#endif -#endif - - -/** - * @brief Length of a URL - */ -const NNTI_URL_LEN = 128; +%#include "nnti/nnti_types.h" /*********** TCP/IP address types ***********/ @@ -161,6 +112,8 @@ struct NNTI_local_process_p_t { /*********** Remote Process Union ***********/ +typedef uint64_t NNTI_transport_id_p_t; + /** * @brief A structure to represent a remote processes. * @@ -169,7 +122,7 @@ struct NNTI_local_process_p_t { * on a remote node. */ #if defined(RPC_HDR) || defined(RPC_XDR) -union NNTI_remote_process_p_t switch (NNTI_transport_id_t transport_id) { +union NNTI_remote_process_p_t switch (NNTI_transport_id_p_t transport_id) { /** @brief The NULL representation of a process on the network. */ case NNTI_TRANSPORT_NULL: NNTI_null_process_p_t null; /** @brief The IB representation of a process on the network. */ @@ -195,7 +148,8 @@ union NNTI_remote_process_p_t { /*********** Peer Type ***********/ -typedef uint64_t NNTI_process_id_t; + +typedef uint64_t NNTI_process_id_p_t; /** * @brief Handle to an NNTI process. @@ -204,10 +158,8 @@ typedef uint64_t NNTI_process_id_t; * Use this handle to move data to/from the process. */ struct NNTI_peer_p_t { - NNTI_datatype_t datatype; - /** @brief binary encoding of a process's URL */ - NNTI_process_id_t pid; + NNTI_process_id_p_t pid; /** @brief binary encoding of a process on the network */ NNTI_remote_process_p_t peer; @@ -306,7 +258,7 @@ struct NNTI_local_rdma_addr_p_t { * on a remote node. */ #if defined(RPC_HDR) || defined(RPC_XDR) -union NNTI_remote_addr_p_t switch (NNTI_transport_id_t transport_id) { +union NNTI_remote_addr_p_t switch (NNTI_transport_id_p_t transport_id) { /** @brief The NULL representation of a memory region. */ case NNTI_TRANSPORT_NULL: NNTI_null_rdma_addr_p_t null; /** @brief The IB representation of a memory region. */ diff --git a/src/nnti/serializers/xdr/nnti_xdr.hpp b/src/nnti/serializers/xdr/nnti_xdr.hpp new file mode 100644 index 0000000..7e81d92 --- /dev/null +++ b/src/nnti/serializers/xdr/nnti_xdr.hpp @@ -0,0 +1,7 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/serializers/xdr/nnti_xdr.h" +#include "nnti/serializers/xdr/nnti_packable.h" diff --git a/src/nnti/transports/base/base_transport.cpp b/src/nnti/transports/base/base_transport.cpp index 68a89bd..17a0b45 100644 --- a/src/nnti/transports/base/base_transport.cpp +++ b/src/nnti/transports/base/base_transport.cpp @@ -21,7 +21,7 @@ #include "faodel-common/Configuration.hh" #include "faodel-common/NodeID.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "nnti/nnti_transport.hpp" #include "nnti/transports/base/base_transport.hpp" @@ -82,7 +82,7 @@ base_transport::base_transport( init_logger(config); - faodel::nodeid_t nodeid = webhook::Server::GetNodeID(); + faodel::nodeid_t nodeid = whookie::Server::GetNodeID(); addr = nodeid.GetIP(); port = nodeid.GetPort(); @@ -183,6 +183,7 @@ base_transport::dt_sizeof( *packed_len = ((nnti::datatype::nnti_peer*)dt)->packed_size(); break; default: + log_error("base_transport", "unknown datatype"); // unsupported datatype rc = NNTI_EINVAL; break; @@ -220,6 +221,7 @@ base_transport::dt_pack( rc = ((nnti::datatype::nnti_peer*)dt)->pack(packed_buf, packed_buflen); break; default: + log_error("base_transport", "unknown datatype"); // unsupported datatype rc = NNTI_EINVAL; break; @@ -250,6 +252,7 @@ base_transport::dt_free( rc = ((nnti::datatype::nnti_peer*)dt)->free_packable(); break; default: + log_error("base_transport", "unknown datatype"); // unsupported datatype rc = NNTI_EINVAL; break; diff --git a/src/nnti/transports/ibverbs/ibverbs_atomic_op.hpp b/src/nnti/transports/ibverbs/ibverbs_atomic_op.hpp index 3c42862..d54512d 100644 --- a/src/nnti/transports/ibverbs/ibverbs_atomic_op.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_atomic_op.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ibverbs/ibverbs_buffer.cpp b/src/nnti/transports/ibverbs/ibverbs_buffer.cpp index 42aae9f..0aa47f9 100644 --- a/src/nnti/transports/ibverbs/ibverbs_buffer.cpp +++ b/src/nnti/transports/ibverbs/ibverbs_buffer.cpp @@ -17,7 +17,7 @@ #include #include "nnti/nnti_logger.hpp" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_callback.hpp" @@ -167,27 +167,32 @@ ibverbs_buffer::register_buffer(void) access = nnti_to_ib_flags(flags_); - log_debug("ibverbs_buffer", "registering ibverbs_buffer (payload_=%x)", payload_); - mr = ibv_reg_mr(ibv_transport->pd_, payload_, payload_size_, access); - if (!mr) { - if (errno == EFAULT) { - log_debug("ibverbs_buffer", "ibv_reg_mr failed with EFAULT. trying to register with IBV_ACCESS_REMOTE_READ."); - mr = ibv_reg_mr(ibv_transport->pd_, payload_, payload_size_, IBV_ACCESS_REMOTE_READ); - if (!mr) { - log_error("ibverbs_buffer", "failed to register memory region with IBV_ACCESS_REMOTE_READ: %s", strerror(errno)); + if ((ibv_transport->use_odp_) && + (!(flags_ & NNTI_BF_REMOTE_ATOMIC))) { + mr = ibv_transport->odp_mr_; + } else { + log_debug("ibverbs_buffer", "registering ibverbs_buffer (payload_=%x)", payload_); + mr = ibv_reg_mr(ibv_transport->pd_, payload_, payload_size_, access); + if (!mr) { + if (errno == EFAULT) { + log_debug("ibverbs_buffer", "ibv_reg_mr failed with EFAULT. trying to register with IBV_ACCESS_REMOTE_READ."); + mr = ibv_reg_mr(ibv_transport->pd_, payload_, payload_size_, IBV_ACCESS_REMOTE_READ); + if (!mr) { + log_error("ibverbs_buffer", "failed to register memory region with IBV_ACCESS_REMOTE_READ: %s", strerror(errno)); + rc = NNTI_EPERM; + goto cleanup; + } + } else { + log_error("ibverbs_buffer", "failed to register memory region: %s", strerror(errno)); rc = NNTI_EPERM; goto cleanup; } - } else { - log_error("ibverbs_buffer", "failed to register memory region: %s", strerror(errno)); - rc = NNTI_EPERM; - goto cleanup; } } packable_.buffer.transport_id = NNTI_TRANSPORT_IBVERBS; - packable_.buffer.NNTI_remote_addr_p_t_u.ib.size = mr->length; - packable_.buffer.NNTI_remote_addr_p_t_u.ib.buf = (uint64_t)mr->addr; + packable_.buffer.NNTI_remote_addr_p_t_u.ib.size = payload_size_; + packable_.buffer.NNTI_remote_addr_p_t_u.ib.buf = (uint64_t)payload_; packable_.buffer.NNTI_remote_addr_p_t_u.ib.key = mr->rkey; registered_ = true; @@ -201,14 +206,20 @@ ibverbs_buffer::register_buffer(void) NNTI_result_t ibverbs_buffer::unregister_buffer(void) { - log_debug("ibverbs_buffer", "deregistering ibverbs_buffer (payload_=%x)", mr->addr); - int ibv_rc=ibv_dereg_mr(mr); - if (ibv_rc != 0) { - log_error("ibverbs_buffer", "deregistering the memory buffer failed"); - return NNTI_EINVAL; - } + transports::ibverbs_transport *ibv_transport = (transports::ibverbs_transport*)transport_; - registered_ = false; + if (ibv_transport->use_odp_) { + log_debug("ibverbs_buffer", "using ODP - unregister is a no-op"); + } else { + log_debug("ibverbs_buffer", "deregistering ibverbs_buffer (payload_=%x)", mr->addr); + int ibv_rc=ibv_dereg_mr(mr); + if (ibv_rc != 0) { + log_error("ibverbs_buffer", "deregistering the memory buffer failed"); + return NNTI_EINVAL; + } + registered_ = false; + } + mr = NULL; return NNTI_OK; } @@ -234,8 +245,8 @@ ibverbs_buffer::post_receive(void) rq_wr.sg_list = &sge; rq_wr.num_sge = 1; - sge.addr = (uint64_t)mr->addr; - sge.length = mr->length; + sge.addr = (uint64_t)payload_; + sge.length = payload_size_; sge.lkey = mr->lkey; ibv_rc=ibv_post_srq_recv(ibv_transport->rdma_srq_, &rq_wr, &bad_wr); @@ -262,10 +273,13 @@ ibverbs_buffer::nnti_to_ib_flags(NNTI_buffer_flags_t nnti_flags) ibv_flags = static_cast(ibv_flags | IBV_ACCESS_LOCAL_WRITE); } if (nnti_flags & NNTI_BF_REMOTE_READ) { - ibv_flags = static_cast(ibv_flags | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_ATOMIC); + ibv_flags = static_cast(ibv_flags | IBV_ACCESS_REMOTE_READ); } if (nnti_flags & NNTI_BF_REMOTE_WRITE) { - ibv_flags = static_cast(ibv_flags | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_ATOMIC); + ibv_flags = static_cast(ibv_flags | IBV_ACCESS_REMOTE_WRITE); + } + if (nnti_flags & NNTI_BF_REMOTE_ATOMIC) { + ibv_flags = static_cast(ibv_flags | IBV_ACCESS_REMOTE_ATOMIC); } return ibv_flags; diff --git a/src/nnti/transports/ibverbs/ibverbs_buffer.hpp b/src/nnti/transports/ibverbs/ibverbs_buffer.hpp index 1164174..63111f6 100644 --- a/src/nnti/transports/ibverbs/ibverbs_buffer.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_buffer.hpp @@ -16,7 +16,7 @@ #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_buffer.hpp" diff --git a/src/nnti/transports/ibverbs/ibverbs_cmd_buffer.cpp b/src/nnti/transports/ibverbs/ibverbs_cmd_buffer.cpp index 9902825..64fa409 100644 --- a/src/nnti/transports/ibverbs/ibverbs_cmd_buffer.cpp +++ b/src/nnti/transports/ibverbs/ibverbs_cmd_buffer.cpp @@ -60,6 +60,9 @@ ibverbs_cmd_buffer::post_recv( rq_wr.num_sge = 1; rq_wr.wr_id = (uint64_t)cmd_msg; + log_debug("ibverbs_cmd_buffer", "post_recv() - cmd_msg=%p - sge.addr=%lx ; sge.length=%lu ; sge.lkey=%x", + cmd_msg, sge.addr, sge.length, sge.lkey); + ibv_rc=ibv_post_srq_recv(transport_->cmd_srq_, &rq_wr, &bad_wr); if (ibv_rc) { log_error("ibverbs_cmd_buffer", "failed to post SRQ recv (rq_wr=%p ; bad_wr=%p): %s", @@ -75,12 +78,17 @@ ibverbs_cmd_buffer::setup_command_buffer(void) log_debug("ibverbs_cmd_buffer", "setup_command_buffer: enter"); cmd_buf_ = new char[cmd_size_ * cmd_count_]; - log_debug("ibverbs_cmd_buffer", "registering ibverbs_cmd_buffer (cmd_buf_=%x)", cmd_buf_); - cmd_mr_ = ibv_reg_mr(transport_->pd_, cmd_buf_, cmd_size_ * cmd_count_, ibv_flags); + +// if (transport_->use_odp_) { +// cmd_mr_ = transport_->odp_mr_; +// } else { + log_debug("ibverbs_cmd_buffer", "registering ibverbs_cmd_buffer (cmd_buf_=%x)", cmd_buf_); + cmd_mr_ = ibv_reg_mr(transport_->pd_, cmd_buf_, cmd_size_ * cmd_count_, ibv_flags); +// } for (int i=0;iaddr+(cmd_size_ * i); - log_debug("ibverbs_cmd_buffer", "cmd_addr = %p = %lx + (%u * %d)", cmd_addr, cmd_mr_->addr, cmd_size_, i); + char *cmd_addr = (char*)cmd_buf_+(cmd_size_ * i); + log_debug("ibverbs_cmd_buffer", "cmd_addr = %p = %lx + (%u * %d)", cmd_addr, cmd_buf_, cmd_size_, i); nnti::core::ibverbs_cmd_msg *cm = new nnti::core::ibverbs_cmd_msg(transport_, this, cmd_addr, cmd_size_); msgs_.push_back(cm); post_recv(cm); @@ -96,8 +104,12 @@ ibverbs_cmd_buffer::teardown_command_buffer(void) for (int i=0;iaddr); - int ibv_rc=ibv_dereg_mr(cmd_mr_); + if (transport_->use_odp_) { + log_debug("ibverbs_buffer", "using ODP - unregister is a no-op"); + } else { + log_debug("ibverbs_cmd_buffer", "deregistering ibverbs_cmd_buffer (cmd_buf_=%x)", cmd_buf_); + int ibv_rc=ibv_dereg_mr(cmd_mr_); + } delete[] cmd_buf_; log_debug("ibverbs_cmd_buffer", "teardown_command_buffer: exit"); diff --git a/src/nnti/transports/ibverbs/ibverbs_cmd_msg.hpp b/src/nnti/transports/ibverbs/ibverbs_cmd_msg.hpp index 35d5184..15dd536 100644 --- a/src/nnti/transports/ibverbs/ibverbs_cmd_msg.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_cmd_msg.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ibverbs/ibverbs_cmd_op.hpp b/src/nnti/transports/ibverbs/ibverbs_cmd_op.hpp index 2f84fff..b8757a5 100644 --- a/src/nnti/transports/ibverbs/ibverbs_cmd_op.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_cmd_op.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" @@ -63,7 +63,7 @@ class ibverbs_cmd_op sq_wr_.opcode = IBV_WR_SEND; sq_wr_.send_flags = IBV_SEND_SIGNALED; - sge_.addr = (uint64_t)cmd_mr_->addr; + sge_.addr = (uint64_t)cmd_msg_.buf(); sge_.length = cmd_msg_.size(); sge_.lkey = cmd_mr_->lkey; @@ -90,7 +90,7 @@ class ibverbs_cmd_op sq_wr_.opcode = IBV_WR_SEND; sq_wr_.send_flags = IBV_SEND_SIGNALED; - sge_.addr = (uint64_t)cmd_mr_->addr; + sge_.addr = (uint64_t)cmd_msg_.buf(); sge_.length = cmd_msg_.size(); sge_.lkey = cmd_mr_->lkey; @@ -119,7 +119,7 @@ class ibverbs_cmd_op } else { register_cmd_msg(); - sge_.addr = (uint64_t)cmd_mr_->addr; + sge_.addr = (uint64_t)cmd_msg_.buf(); sge_.lkey = cmd_mr_->lkey; } @@ -131,8 +131,10 @@ class ibverbs_cmd_op return; } - ~ibverbs_cmd_op() override { - if ((wid_) && !(wid_->wr().flags() & NNTI_OF_ZERO_COPY)) { + ~ibverbs_cmd_op() override { + if (transport_->use_odp_) { + log_debug("ibverbs_buffer", "using ODP - unregister is a no-op"); + } else if ((wid_) && !(wid_->wr().flags() & NNTI_OF_ZERO_COPY)) { log_debug("ibverbs_buffer", "deregistering ibverbs_cmd_op (cmd_msg_.buf()=%x)", cmd_mr_->addr); ibv_dereg_mr(cmd_mr_); } @@ -200,11 +202,15 @@ class ibverbs_cmd_op void register_cmd_msg(void) { - log_debug("ibverbs_cmd_op", "registering ibverbs_cmd_op (cmd_msg_.buf()=%x)", cmd_msg_.buf()); - cmd_mr_ = ibv_reg_mr(transport_->pd_, cmd_msg_.buf(), cmd_msg_.size(), 0); - if (!cmd_mr_) { - log_error("ibverbs_cmd_op", "failed to register memory region: %s", strerror(errno)); - return; + if (transport_->use_odp_) { + cmd_mr_ = transport_->odp_mr_; + } else { + log_debug("ibverbs_cmd_op", "registering ibverbs_cmd_op (cmd_msg_.buf()=%x)", cmd_msg_.buf()); + cmd_mr_ = ibv_reg_mr(transport_->pd_, cmd_msg_.buf(), cmd_msg_.size(), 0); + if (!cmd_mr_) { + log_error("ibverbs_cmd_op", "failed to register memory region: %s", strerror(errno)); + return; + } } return; diff --git a/src/nnti/transports/ibverbs/ibverbs_connection.hpp b/src/nnti/transports/ibverbs/ibverbs_connection.hpp index 3773e29..9082e26 100644 --- a/src/nnti/transports/ibverbs/ibverbs_connection.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_connection.hpp @@ -214,7 +214,7 @@ class ibverbs_connection return ss.str(); } /* - * generate a key=value (one per line) string that can be included in a WebHook reply + * generate a key=value (one per line) string that can be included in a Whookie reply */ std::string reply_string(void) override { diff --git a/src/nnti/transports/ibverbs/ibverbs_peer.hpp b/src/nnti/transports/ibverbs/ibverbs_peer.hpp index 62e9d87..630e320 100644 --- a/src/nnti/transports/ibverbs/ibverbs_peer.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_peer.hpp @@ -24,7 +24,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_peer.hpp" #include "nnti/nnti_url.hpp" diff --git a/src/nnti/transports/ibverbs/ibverbs_rdma_op.hpp b/src/nnti/transports/ibverbs/ibverbs_rdma_op.hpp index 9703747..4c42d06 100644 --- a/src/nnti/transports/ibverbs/ibverbs_rdma_op.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_rdma_op.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ibverbs/ibverbs_transport.cpp b/src/nnti/transports/ibverbs/ibverbs_transport.cpp index 99b58dc..be0b28c 100644 --- a/src/nnti/transports/ibverbs/ibverbs_transport.cpp +++ b/src/nnti/transports/ibverbs/ibverbs_transport.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -56,8 +57,8 @@ #include "nnti/transports/ibverbs/ibverbs_transport.hpp" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" namespace nnti { @@ -77,6 +78,7 @@ ibverbs_transport::ibverbs_transport( : base_transport(NNTI_TRANSPORT_IBVERBS, config), started_(false), + ctx_(nullptr), event_freelist_size_(128), cmd_op_freelist_size_(128), rdma_op_freelist_size_(128), @@ -89,6 +91,12 @@ ibverbs_transport::ibverbs_transport( me_ = nnti::datatype::ibverbs_peer(this, url_); + rc = config.GetString(&interface_dev_list_, "net.transport.interfaces", ""); + rc = config.GetString(&kernel_dev_list_, "net.transport.kernel_device_list", ""); + rc = config.GetString(&fs_dev_list_, "net.transport.fs_device_list", ""); + + rc = config.GetBool(&odp_enabled_, "net.transport.use_odp", "false"); + rc = config.GetUInt(&uint_value, "nnti.freelist.size", "128"); if (rc == 0) { event_freelist_size_ = uint_value; @@ -129,17 +137,16 @@ ibverbs_transport::start(void) log_debug("ibverbs_transport", "initializing InfiniBand"); - struct ibv_device *dev=get_ib_device(); - - assert(dev!=nullptr && "The IB transport couldn't find an ibverbs compatible device on this machine."); - /* open the device */ - ctx_ = ibv_open_device(dev); - if (!ctx_) { - log_error("ibverbs_transport", "ibv_open_device failed"); + struct ibv_device **dev_list; + int dev_count=0; + dev_list = ibv_get_device_list(&dev_count); + if (select_ib_device(dev_list, dev_count, &nic_port_) == false) { + log_error("ibverbs_transport", "select_ib_device failed"); return NNTI_EIO; } + ibv_free_device_list(dev_list); - nic_port_ = 1; + log_debug("ibverbs_transport", "querying IB port %d", nic_port_); /* get the lid and verify port state */ ibv_rc = ibv_query_port(ctx_, nic_port_, &dev_port_attr); @@ -151,7 +158,10 @@ ibverbs_transport::start(void) nic_lid_ = dev_port_attr.lid; if (dev_port_attr.state != IBV_PORT_ACTIVE) { - log_error("ibverbs_transport", "port is not active. cannot continue."); + log_error("ibverbs_transport", + "Could not find an active port. " + "FAODEL's net.transport.interfaces was set to %s. Cannot continue.", + interface_dev_list_.c_str()); return NNTI_EIO; } @@ -186,14 +196,24 @@ ibverbs_transport::start(void) log_debug("ibverbs_transport", "max %d queue pair work requests", dev_attr.max_qp_wr); qp_count_ = 1024; + have_odp_ = have_odp(); + have_implicit_odp_ = have_implicit_odp(); + if (odp_enabled_ && have_odp_ && have_implicit_odp_) { + use_odp_ = true; + } else { + use_odp_ = false; + } + log_debug("ibverbs_transport", "odp_enabled_=%d ; have_odp=%d ; have_implicit_odp=%d ; use_odp_=%d", + (int)odp_enabled_, (int)have_odp_, (int)have_implicit_odp_, (int)use_odp_); + attrs_.mtu = cmd_msg_size_; attrs_.max_cmd_header_size = nnti::core::ibverbs_cmd_msg::header_length(); attrs_.max_eager_size = attrs_.mtu - attrs_.max_cmd_header_size; attrs_.cmd_queue_size = cmd_msg_count_; - log_debug("mpi_transport", "attrs_.mtu =%d", attrs_.mtu); - log_debug("mpi_transport", "attrs_.max_cmd_header_size=%d", attrs_.max_cmd_header_size); - log_debug("mpi_transport", "attrs_.max_eager_size =%d", attrs_.max_eager_size); - log_debug("mpi_transport", "attrs_.cmd_queue_size =%d", attrs_.cmd_queue_size); + log_debug("ibverbs_transport", "attrs_.mtu =%d", attrs_.mtu); + log_debug("ibverbs_transport", "attrs_.max_cmd_header_size=%d", attrs_.max_cmd_header_size); + log_debug("ibverbs_transport", "attrs_.max_eager_size =%d", attrs_.max_eager_size); + log_debug("ibverbs_transport", "attrs_.cmd_queue_size =%d", attrs_.cmd_queue_size); /* Allocate a Protection Domain (global) */ pd_ = ibv_alloc_pd(ctx_); @@ -202,7 +222,15 @@ ibverbs_transport::start(void) return NNTI_EIO; } - faodel::nodeid_t nodeid = webhook::Server::GetNodeID(); + if (use_odp_) { + ibv_rc = register_odp(); + if (ibv_rc) { + log_error("ibverbs_transport", "Implicit ODP registration failed. Disabling ODP for this run."); + use_odp_ = false; + } + } + + faodel::nodeid_t nodeid = whookie::Server::GetNodeID(); std::string addr = nodeid.GetIP(); std::string port = nodeid.GetPort(); url_ = nnti::core::nnti_url(addr, port); @@ -240,12 +268,12 @@ ibverbs_transport::start(void) } NNTI_STATS_DATA( - stats_ = new webhook_stats; + stats_ = new whookie_stats; ) - assert(webhook::Server::IsRunning() && "webhook is not running. Confirm Bootstrap configuration and try again."); + assert(whookie::Server::IsRunning() && "whookie is not running. Confirm Bootstrap configuration and try again."); - register_webhook_cb(); + register_whookie_cb(); log_debug("ibverbs_transport", "url_=%s", url_.url().c_str()); @@ -280,7 +308,7 @@ ibverbs_transport::stop(void) } nthread_unlock(&new_connection_lock_); - unregister_webhook_cb(); + unregister_whookie_cb(); stop_progress_thread(); @@ -411,13 +439,13 @@ ibverbs_transport::connect( nthread_unlock(&new_connection_lock_); std::string reply; - std::string wh_path = build_webhook_connect_path(conn); + std::string wh_path = build_whookie_connect_path(conn); int wh_rc = 0; int retries = 5; - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); while (wh_rc != 0 && --retries) { sleep(1); - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); } if (wh_rc != 0) { log_debug("ibverbs_transport", "connect() timed out"); @@ -464,14 +492,14 @@ ibverbs_transport::disconnect( nthread_unlock(&new_connection_lock_); if (*peer != me_) { - std::string wh_path = build_webhook_disconnect_path(conn); - int wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + std::string wh_path = build_whookie_disconnect_path(conn); + int wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); if (wh_rc != 0) { return(NNTI_ETIMEDOUT); } } - log_debug("ibverbs_transport", "disconnect from %s (pid=%x) succeeded", peer->url(), peer->pid()); + log_debug("ibverbs_transport", "disconnect from %s (pid=%x) succeeded", peer->url().url().c_str(), peer->pid()); delete conn; delete peer; @@ -810,18 +838,20 @@ ibverbs_transport::dt_unpack( nnti::datatype::ibverbs_buffer *b = nullptr; nnti::datatype::nnti_peer *p = nullptr; - switch (*(NNTI_datatype_t*)packed_buf) { + NNTI_datatype_t t = nnti::serialize::get_datatype(packed_buf, packed_len); + switch (t) { case NNTI_dt_buffer: - log_debug("base_transport", "dt is a buffer"); + log_debug("ibverbs_transport", "dt is a buffer"); b = new nnti::datatype::ibverbs_buffer(this, packed_buf, packed_len); *(NNTI_buffer_t*)nnti_dt = nnti::datatype::nnti_buffer::to_hdl(b); break; case NNTI_dt_peer: - log_debug("base_transport", "dt is a peer"); + log_debug("ibverbs_transport", "dt is a peer"); p = new nnti::datatype::nnti_peer(this, packed_buf, packed_len); *(NNTI_peer_t*)nnti_dt = nnti::datatype::nnti_peer::to_hdl(p); break; default: + log_error("ibverbs_transport", "unknown datatype"); // unsupported datatype rc = NNTI_EINVAL; break; @@ -1008,11 +1038,20 @@ ibverbs_transport::send( log_debug("ibverbs_transport", "send - wr.local_offset=%lu", wr->local_offset()); rc = create_send_op(work_id, &cmd_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "create_send_op() failed"); + goto done; + } rc = execute_cmd_op(work_id, cmd_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "execute_cmd_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1032,12 +1071,33 @@ ibverbs_transport::put( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::ibverbs_rdma_op *put_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + const nnti::datatype::ibverbs_work_request &ibwr = (const nnti::datatype::ibverbs_work_request &)work_id->wr(); + if (ibwr.local_offset() + ibwr.length() > ibwr.local_length()) { + log_error("ibverbs_transport", "PUT length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (ibwr.remote_offset() + ibwr.length() > ibwr.remote_length()) { + log_error("ibverbs_transport", "PUT length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_put_op(work_id, &put_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "create_put_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, put_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1057,12 +1117,33 @@ ibverbs_transport::get( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::ibverbs_rdma_op *get_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + const nnti::datatype::ibverbs_work_request &ibwr = (const nnti::datatype::ibverbs_work_request &)work_id->wr(); + if (ibwr.local_offset() + ibwr.length() > ibwr.local_length()) { + log_error("ibverbs_transport", "GET length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (ibwr.remote_offset() + ibwr.length() > ibwr.remote_length()) { + log_error("ibverbs_transport", "GET length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_get_op(work_id, &get_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "create_get_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, get_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1083,11 +1164,20 @@ ibverbs_transport::atomic_fop( nnti::core::ibverbs_atomic_op *atomic_op = nullptr; rc = create_fadd_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "create_fadd_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1108,11 +1198,20 @@ ibverbs_transport::atomic_cswap( nnti::core::ibverbs_atomic_op *atomic_op = nullptr; rc = create_cswap_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "create_fadd_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("ibverbs_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1229,6 +1328,98 @@ ibverbs_transport::get_instance( } +bool +ibverbs_transport::have_odp(void) +{ +#if (NNTI_HAVE_IBV_EXP_QUERY_DEVICE && NNTI_HAVE_IBV_EXP_DEVICE_ATTR_ODP) + int ibv_rc=0; + struct ibv_exp_device_attr exp_dev_attr; + if (ctx_ == nullptr) { + // the IB device is not open + return false; + } + exp_dev_attr.comp_mask = IBV_EXP_DEVICE_ATTR_ODP | IBV_EXP_DEVICE_ATTR_EXP_CAP_FLAGS; + ibv_rc = ibv_exp_query_device(ctx_, &exp_dev_attr); + if (exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) { + return true; + } +#endif + return false; +} + +bool +ibverbs_transport::have_implicit_odp(void) +{ +#if (NNTI_HAVE_IBV_EXP_QUERY_DEVICE && NNTI_HAVE_IBV_EXP_DEVICE_ATTR_ODP && NNTI_HAVE_IBV_EXP_ODP_SUPPORT_IMPLICIT) + int ibv_rc=0; + struct ibv_exp_device_attr exp_dev_attr; + if (ctx_ == nullptr) { + // the IB device is not open + return false; + } + exp_dev_attr.comp_mask = IBV_EXP_DEVICE_ATTR_ODP | IBV_EXP_DEVICE_ATTR_EXP_CAP_FLAGS; + ibv_rc = ibv_exp_query_device(ctx_, &exp_dev_attr); + + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_SEND)) { + log_debug("ibverbs_transport", "This device supports ODP SEND"); + } + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_RECV)) { + log_debug("ibverbs_transport", "This device supports ODP RECV"); + } + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_SRQ_RECV)) { + log_debug("ibverbs_transport", "This device supports ODP SRQ RECV"); + } + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_READ)) { + log_debug("ibverbs_transport", "This device supports ODP READ"); + } + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_WRITE)) { + log_debug("ibverbs_transport", "This device supports ODP WRITE"); + } + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.per_transport_caps.rc_odp_caps & IBV_EXP_ODP_SUPPORT_ATOMIC)) { + log_debug("ibverbs_transport", "This device supports ODP ATOMIC"); + } + + if ((exp_dev_attr.exp_device_cap_flags & IBV_EXP_DEVICE_ODP) && + (exp_dev_attr.odp_caps.general_odp_caps & IBV_EXP_ODP_SUPPORT_IMPLICIT)) { + return true; + } + +#endif + return false; +} + +int +ibverbs_transport::register_odp(void) +{ +#if (NNTI_HAVE_IBV_EXP_ACCESS_ON_DEMAND) + struct ibv_exp_reg_mr_in in; + + in.pd = pd_; + in.addr = 0; + in.length = IBV_EXP_IMPLICIT_MR_SIZE; + in.exp_access = IBV_EXP_ACCESS_ON_DEMAND | IBV_EXP_ACCESS_LOCAL_WRITE | IBV_EXP_ACCESS_REMOTE_READ | IBV_EXP_ACCESS_REMOTE_WRITE | IBV_EXP_ACCESS_REMOTE_ATOMIC; + in.comp_mask = 0; + + odp_mr_ = ibv_exp_reg_mr(&in); + if (odp_mr_ == NULL) { + log_error("ibverbs_transport", "ibv_exp_reg_mr() failed: %s", strerror(errno)); + } + + log_debug("ibverbs_transport", "mr=%p", odp_mr_); + + return 0; +#else + log_error("ibverbs_transport", "attempted to register memory with ODP, but it's not available on this system"); + return -1; +#endif +} + bool ibverbs_transport::have_exp_qp(void) { @@ -1244,6 +1435,7 @@ ibverbs_transport::atomic_result_is_be(void) #if (NNTI_HAVE_IBV_EXP_QUERY_DEVICE && NNTI_HAVE_IBV_EXP_ATOMIC_HCA_REPLY_BE) int ibv_rc=0; struct ibv_exp_device_attr exp_dev_attr; + memset(&exp_dev_attr, 0, sizeof(exp_dev_attr)); exp_dev_attr.comp_mask = IBV_EXP_DEVICE_ATTR_RESERVED - 1; ibv_rc = ibv_exp_query_device(ctx_, &exp_dev_attr); if (ibv_rc) { @@ -1588,23 +1780,174 @@ ibverbs_transport::stop_progress_thread(void) } +void +ibverbs_transport::open_ib_device(struct ibv_device *dev) +{ + log_debug("ibverbs_transport", "opening device (%s|%s)", dev->name, dev->dev_name); + /* open the device */ + ctx_ = ibv_open_device(dev); +} +bool +ibverbs_transport::is_port_active(struct ibv_device *dev, int port) +{ + bool rc=false; + int ibv_rc = 0; + struct ibv_port_attr dev_port_attr; + + open_ib_device(dev); + ibv_rc = ibv_query_port(ctx_, port, &dev_port_attr); + if (ibv_rc == 0) { + if (dev_port_attr.state == IBV_PORT_ACTIVE) { + log_debug("ibverbs_transport", "port (%d) is active", port); + rc = true; + } + } else { + log_error("ibverbs_transport", "ibv_query_port failed"); + } + ibv_close_device(ctx_); + + return rc; +} struct ibv_device * -ibverbs_transport::get_ib_device(void) +ibverbs_transport::find_active_ib_device(struct ibv_device **dev_list, int dev_count, int *port) +{ + int ibv_rc = 0; + struct ibv_device *dev; + struct ibv_device_attr dev_attr; + struct ibv_port_attr dev_port_attr; + + *port=-1; + for ( int i=0 ; i < dev_count ; i++) { + dev = dev_list[i]; + open_ib_device(dev); + + /* Query the device for port count */ + ibv_rc = ibv_query_device(ctx_, &dev_attr); + if (ibv_rc == 0) { + for ( int j=0 ; j < dev_attr.phys_port_cnt ; j++) { + /* get the port state */ + ibv_rc = ibv_query_port(ctx_, j+1, &dev_port_attr); + if (ibv_rc == 0) { + if ((dev_port_attr.state == IBV_PORT_ACTIVE) && + (dev_port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND)) { + *port = j+1; + log_debug("ibverbs_transport", "found device (%s|%s) with active port (%d)", dev->name, dev->dev_name, *port); + goto done; + } + } else { + log_error("ibverbs_transport", "ibv_query_port failed"); + } + } + } else { + log_error("ibverbs_transport", "ibv_query_device failed"); + } + ibv_close_device(ctx_); + dev = nullptr; + } + return nullptr; + +done: + ibv_close_device(ctx_); + return dev; +} +bool +ibverbs_transport::select_ib_device(struct ibv_device **dev_list, int dev_count, int *port) { struct ibv_device *dev=nullptr; - struct ibv_device **dev_list; - int dev_count=0; - dev_list = ibv_get_device_list(&dev_count); - if (dev_count == 0) - return nullptr; - if (dev_count > 1) { - log_debug("ibverbs_transport", "found %d devices, defaulting the dev_list[0] (%p)", dev_count, dev_list[0]); + log_debug("ibverbs_transport", "%d devices exist", dev_count); + if (dev_count == 0) { + log_debug("ibverbs_transport", "No devices found"); + } else if (interface_dev_list_.length() == 0) { + log_debug("ibverbs_transport", "net.transport.interfaces is empty - searching for a device with an active port"); + dev = find_active_ib_device(dev_list, dev_count, port); + if (dev == nullptr) { + log_error("ibverbs_transport", + "The IB transport couldn't find an active ibverbs device on this machine. " + "FAODEL's net.transport.interfaces is not set. " + "Trying setting it to the interface (eg. ib0) of an active device."); + } + } else { + int rc; + struct stat sbuf; + std::string uverbs_dev; + uint32_t ib_port; + + std::vector interface_device_list = faodel::Split(interface_dev_list_, ',', true); + for ( std::string ifdev : interface_device_list ) { + log_debug("ibverbs_transport", "looking for interface device '%s'", ifdev.c_str()); + + // check that the interface device exists + std::string ifdev_path = std::string("/sys/class/net/" + ifdev); + log_debug("ibverbs_transport", "calling stat(%s)", ifdev_path.c_str()); + rc = stat(ifdev_path.c_str(), &sbuf); + if ((rc != 0) || !S_ISDIR(sbuf.st_mode)) { + // device doesn't exist + continue; + } + // determine the uverbs device + int uverbs_num = -1; + std::string uverbs_path; + for (int i=0;i= 0) { + uverbs_dev = std::string("uverbs" + std::to_string(uverbs_num)); + + std::ifstream in(std::string("/sys/class/net/"+ifdev+"/dev_id")); + char hex_port[64]; + in.read(hex_port, 64); + hex_port[in.gcount()]='\0'; + in.close(); + + *port = -1; + *port = strtoul(hex_port, NULL, 16); + // dev_id is 0-based but port is 1-based, so increment + (*port)++; + log_debug("ibverbs_transport", "port = %d", *port); + + for ( int i=0 ; i < dev_count ; i++) { + if (0==strcmp(dev_list[i]->dev_name,uverbs_dev.c_str())) { + log_debug("ibverbs_transport", "'%s' matches dev_list[%d] (%s|%s)", ifdev.c_str(), i, dev_list[i]->name, dev_list[i]->dev_name); + dev = dev_list[i]; + if (is_port_active(dev, *port)) { + goto done; + } else { + log_debug("ibverbs_transport", "'%s|%s' found, but port %d not active", dev_list[i]->name, dev_list[i]->dev_name, *port); + dev=nullptr; + break; + } + } else { + log_debug("ibverbs_transport", "'%s' doesn't match dev_list[%d] (%s|%s)", ifdev.c_str(), i, dev_list[i]->name, dev_list[i]->dev_name); + } + } + } + } + if (dev == nullptr) { + log_error("ibverbs_transport", + "The IB transport couldn't find an active ibverbs device on this machine. " + "FAODEL's net.transport.interfaces was set to %s. " + "Please confirm that one of these devices is active and try again.", + interface_dev_list_.c_str()); + } } - dev = dev_list[0]; - ibv_free_device_list(dev_list); - return dev; +done: + if (dev == nullptr) { + } else { + open_ib_device(dev); + } + + return (dev!=nullptr); } void @@ -1665,27 +2008,25 @@ ibverbs_transport::disconnect_cb(const std::map &args, void ibverbs_transport::stats_cb(const std::map &args, std::stringstream &results) { - html::mkHeader(results, "Transfer Statistics"); - html::mkText(results,"Transfer Statistics",1); + faodel::ReplyStream rs(args, "Transfer Statistics", &results); NNTI_STATS_DATA( - std::vector stats; - stats.push_back("pinned_bytes = " + std::to_string(stats_->pinned_bytes.load())); - stats.push_back("pinned_buffers = " + std::to_string(stats_->pinned_buffers.load())); - stats.push_back("unexpected_sends = " + std::to_string(stats_->unexpected_sends.load())); - stats.push_back("unexpected_recvs = " + std::to_string(stats_->unexpected_recvs.load())); - stats.push_back("short_sends = " + std::to_string(stats_->short_sends.load())); - stats.push_back("short_recvs = " + std::to_string(stats_->short_recvs.load())); - stats.push_back("long_sends = " + std::to_string(stats_->long_sends.load())); - stats.push_back("long_recvs = " + std::to_string(stats_->long_recvs.load())); - stats.push_back("gets = " + std::to_string(stats_->gets.load())); - stats.push_back("puts = " + std::to_string(stats_->puts.load())); - stats.push_back("fadds = " + std::to_string(stats_->fadds.load())); - stats.push_back("cswaps = " + std::to_string(stats_->cswaps.load())); - html::mkList(results, stats); + rs.tableBegin("Transport Statistics"); + rs.tableRow({"pinned_bytes", std::to_string(stats_->pinned_bytes.load())}); + rs.tableRow({"pinned_buffers", std::to_string(stats_->pinned_buffers.load())}); + rs.tableRow({"unexpected_sends", std::to_string(stats_->unexpected_sends.load())}); + rs.tableRow({"unexpected_recvs", std::to_string(stats_->unexpected_recvs.load())}); + rs.tableRow({"short_sends", std::to_string(stats_->short_sends.load())}); + rs.tableRow({"short_recvs", std::to_string(stats_->short_recvs.load())}); + rs.tableRow({"long_sends", std::to_string(stats_->long_sends.load())}); + rs.tableRow({"long_recvs", std::to_string(stats_->long_recvs.load())}); + rs.tableRow({"gets", std::to_string(stats_->gets.load())}); + rs.tableRow({"puts", std::to_string(stats_->puts.load())}); + rs.tableRow({"fadds", std::to_string(stats_->fadds.load())}); + rs.tableRow({"cswaps", std::to_string(stats_->cswaps.load())}); + rs.tableEnd(); ) - - html::mkFooter(results); + rs.Finish(); } void @@ -1706,7 +2047,7 @@ ibverbs_transport::peers_cb(const std::map &args, std:: } std::string -ibverbs_transport::build_webhook_path( +ibverbs_transport::build_whookie_path( nnti::core::nnti_connection *conn, const char *service) { @@ -1722,42 +2063,42 @@ ibverbs_transport::build_webhook_path( return wh_url.str(); } std::string -ibverbs_transport::build_webhook_connect_path( +ibverbs_transport::build_whookie_connect_path( nnti::core::nnti_connection *conn) { - return build_webhook_path(conn, "connect"); + return build_whookie_path(conn, "connect"); } std::string -ibverbs_transport::build_webhook_disconnect_path( +ibverbs_transport::build_whookie_disconnect_path( nnti::core::nnti_connection *conn) { - return build_webhook_path(conn, "disconnect"); + return build_whookie_path(conn, "disconnect"); } void -ibverbs_transport::register_webhook_cb(void) +ibverbs_transport::register_whookie_cb(void) { - webhook::Server::registerHook("/nnti/ib/connect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ib/connect", [this] (const std::map &args, std::stringstream &results){ connect_cb(args, results); }); - webhook::Server::registerHook("/nnti/ib/disconnect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ib/disconnect", [this] (const std::map &args, std::stringstream &results){ disconnect_cb(args, results); }); - webhook::Server::registerHook("/nnti/ib/stats", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ib/stats", [this] (const std::map &args, std::stringstream &results){ stats_cb(args, results); }); - webhook::Server::registerHook("/nnti/ib/peers", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ib/peers", [this] (const std::map &args, std::stringstream &results){ peers_cb(args, results); }); } void -ibverbs_transport::unregister_webhook_cb(void) +ibverbs_transport::unregister_whookie_cb(void) { - webhook::Server::deregisterHook("/nnti/ib/connect"); - webhook::Server::deregisterHook("/nnti/ib/disconnect"); - webhook::Server::deregisterHook("/nnti/ib/stats"); - webhook::Server::deregisterHook("/nnti/ib/peers"); + whookie::Server::deregisterHook("/nnti/ib/connect"); + whookie::Server::deregisterHook("/nnti/ib/disconnect"); + whookie::Server::deregisterHook("/nnti/ib/stats"); + whookie::Server::deregisterHook("/nnti/ib/peers"); } NNTI_result_t @@ -2158,7 +2499,7 @@ ibverbs_transport::poll_cq( log_error("poll_cq", "Failed status %s (%d) for wr_id %lx", ibv_wc_status_str(wc->status), wc->status, wc->wr_id); - nnti_rc = NNTI_EIO; + nnti_rc = NNTI_EPERM; } } @@ -2174,7 +2515,7 @@ ibverbs_transport::poll_cmd_cq(void) struct ibv_wc wc; nnti_rc = poll_cq(cmd_cq_, &wc); - if (nnti_rc == NNTI_OK) { + if ((nnti_rc != NNTI_EIO) && (nnti_rc != NNTI_ENOENT)) { // found a work completion if (wc.opcode & IBV_WC_RECV) { nnti::core::ibverbs_cmd_msg *cmd_msg = (nnti::core::ibverbs_cmd_msg *)wc.wr_id; @@ -2187,7 +2528,7 @@ ibverbs_transport::poll_cmd_cq(void) nnti::datatype::nnti_work_request &wr = cmd_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(cmd_op); + NNTI_event_t *e = create_event(cmd_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2248,7 +2589,7 @@ ibverbs_transport::poll_cmd_cq(void) NNTI_FAST_STAT(stats_->dropped_unexpected++;); } else { unexpected_msgs_.push_back(cmd_msg); - NNTI_event_t *e = create_event(cmd_msg); + NNTI_event_t *e = create_event(cmd_msg, nnti_rc); if (unexpected_queue_->invoke_cb(e) != NNTI_OK) { unexpected_queue_->push(e); unexpected_queue_->notify(); @@ -2280,7 +2621,7 @@ ibverbs_transport::poll_cmd_cq(void) } - e = create_event(cmd_msg, actual_offset); + e = create_event(cmd_msg, actual_offset, nnti_rc); if (tgt_buf->invoke_cb(e) != NNTI_OK) { if (q && q->invoke_cb(e) != NNTI_OK) { q->push(e); @@ -2338,7 +2679,7 @@ ibverbs_transport::poll_cmd_cq(void) nnti_rc = execute_ack_op(peer, ack_op); log_debug("poll_cmd_cqs", "ACK sent"); - e = create_event(cmd_msg); + e = create_event(cmd_msg, nnti_rc); if (tgt_buf->invoke_cb(e) != NNTI_OK) { if (q && q->invoke_cb(e) != NNTI_OK) { q->push(e); @@ -2374,7 +2715,7 @@ ibverbs_transport::poll_cmd_cq(void) nnti::datatype::nnti_work_request &wr = cmd_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(cmd_op); + NNTI_event_t *e = create_event(cmd_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2433,7 +2774,7 @@ ibverbs_transport::poll_cmd_cq(void) NNTI_event_t *e = nullptr; bool release_event = true; - e = create_event(cmd_msg, cmd_msg->target_offset()); + e = create_event(cmd_msg, cmd_msg->target_offset(), nnti_rc); if (b->invoke_cb(e) != NNTI_OK) { if (q && q->invoke_cb(e) != NNTI_OK) { q->push(e); @@ -2461,7 +2802,7 @@ ibverbs_transport::poll_rdma_cq(void) struct ibv_wc wc; nnti_rc = poll_cq(rdma_cq_, &wc); - if (nnti_rc == NNTI_OK) { + if ((nnti_rc != NNTI_EIO) && (nnti_rc != NNTI_ENOENT)) { // found a work completion if (wc.opcode == IBV_WC_RDMA_WRITE) { bool need_event = true; @@ -2471,7 +2812,7 @@ ibverbs_transport::poll_rdma_cq(void) nnti::datatype::nnti_work_request &wr = rdma_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(rdma_op); + NNTI_event_t *e = create_event(rdma_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2517,7 +2858,7 @@ ibverbs_transport::poll_rdma_cq(void) nnti::datatype::nnti_work_request &wr = rdma_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(rdma_op); + NNTI_event_t *e = create_event(rdma_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2563,7 +2904,7 @@ ibverbs_transport::poll_rdma_cq(void) nnti::datatype::nnti_work_request &wr = atomic_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(atomic_op); + NNTI_event_t *e = create_event(atomic_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2617,7 +2958,7 @@ ibverbs_transport::poll_rdma_cq(void) nnti::datatype::nnti_work_request &wr = atomic_op->wid()->wr(); nnti::datatype::nnti_event_queue *alt_q = nnti::datatype::nnti_event_queue::to_obj(wr.alt_eq()); nnti::datatype::nnti_event_queue *buf_q = nullptr; - NNTI_event_t *e = create_event(atomic_op); + NNTI_event_t *e = create_event(atomic_op, nnti_rc); bool event_complete = false; bool release_event = true; @@ -2671,7 +3012,8 @@ ibverbs_transport::poll_rdma_cq(void) NNTI_event_t * ibverbs_transport::create_event( nnti::core::ibverbs_cmd_msg *cmd_msg, - uint64_t offset) + uint64_t offset, + NNTI_result_t result) { NNTI_event_t *e = nullptr; @@ -2682,7 +3024,7 @@ ibverbs_transport::create_event( } e->trans_hdl = nnti::transports::transport::to_hdl(this); - e->result = NNTI_OK; + e->result = result; e->op = NNTI_OP_SEND; e->peer = nnti::datatype::nnti_peer::to_hdl(cmd_msg->initiator_peer()); log_debug("ibverbs_transport", "e->peer = %p", e->peer); @@ -2709,13 +3051,14 @@ ibverbs_transport::create_event( NNTI_event_t * ibverbs_transport::create_event( - nnti::core::ibverbs_cmd_msg *cmd_msg) + nnti::core::ibverbs_cmd_msg *cmd_msg, + NNTI_result_t result) { NNTI_event_t *e = nullptr; log_debug("ibverbs_transport", "create_event(cmd_msg) - enter"); - e = create_event(cmd_msg, cmd_msg->target_offset()); + e = create_event(cmd_msg, cmd_msg->target_offset(), result); log_debug("ibverbs_transport", "create_event(cmd_msg) - exit"); @@ -2724,7 +3067,8 @@ ibverbs_transport::create_event( NNTI_event_t * ibverbs_transport::create_event( - nnti::core::ibverbs_cmd_op *cmd_op) + nnti::core::ibverbs_cmd_op *cmd_op, + NNTI_result_t result) { nnti::datatype::nnti_work_id *wid = cmd_op->wid(); const nnti::datatype::nnti_work_request &wr = wid->wr(); @@ -2737,7 +3081,7 @@ ibverbs_transport::create_event( } e->trans_hdl = nnti::transports::transport::to_hdl(this); - e->result = NNTI_OK; + e->result = result; e->op = wr.op(); e->peer = wr.peer(); e->length = wr.length(); @@ -2754,7 +3098,8 @@ ibverbs_transport::create_event( NNTI_event_t * ibverbs_transport::create_event( - nnti::core::ibverbs_rdma_op *rdma_op) + nnti::core::ibverbs_rdma_op *rdma_op, + NNTI_result_t result) { nnti::datatype::nnti_work_id *wid = rdma_op->wid(); const nnti::datatype::nnti_work_request &wr = wid->wr(); @@ -2768,7 +3113,7 @@ ibverbs_transport::create_event( } e->trans_hdl = nnti::transports::transport::to_hdl(this); - e->result = NNTI_OK; + e->result = result; e->op = wr.op(); e->peer = wr.peer(); e->length = wr.length(); @@ -2790,7 +3135,8 @@ ibverbs_transport::create_event( NNTI_event_t * ibverbs_transport::create_event( - nnti::core::ibverbs_atomic_op *atomic_op) + nnti::core::ibverbs_atomic_op *atomic_op, + NNTI_result_t result) { nnti::datatype::nnti_work_id *wid = atomic_op->wid(); const nnti::datatype::nnti_work_request &wr = wid->wr(); @@ -2803,7 +3149,7 @@ ibverbs_transport::create_event( } e->trans_hdl = nnti::transports::transport::to_hdl(this); - e->result = NNTI_OK; + e->result = result; e->op = wr.op(); e->peer = wr.peer(); e->length = wr.length(); diff --git a/src/nnti/transports/ibverbs/ibverbs_transport.hpp b/src/nnti/transports/ibverbs/ibverbs_transport.hpp index 526e8ff..accab6f 100644 --- a/src/nnti/transports/ibverbs/ibverbs_transport.hpp +++ b/src/nnti/transports/ibverbs/ibverbs_transport.hpp @@ -79,7 +79,7 @@ class ibverbs_transport private: NNTI_STATS_DATA( - struct webhook_stats { + struct whookie_stats { std::atomic pinned_bytes; std::atomic pinned_buffers; std::atomic unexpected_sends; @@ -95,7 +95,7 @@ class ibverbs_transport std::atomic fadds; std::atomic cswaps; - webhook_stats() + whookie_stats() { pinned_bytes.store(0); pinned_buffers.store(0); @@ -114,13 +114,17 @@ class ibverbs_transport } }; - struct webhook_stats *stats_; + struct whookie_stats *stats_; ) NNTI_attrs_t attrs_; bool started_; + std::string interface_dev_list_; + std::string kernel_dev_list_; + std::string fs_dev_list_; + struct ibv_device *dev_; uint16_t nic_lid_; int nic_port_; @@ -142,6 +146,12 @@ class ibverbs_transport struct ibv_cq *long_get_cq_; struct ibv_srq *long_get_srq_; + bool have_odp_; + bool have_implicit_odp_; + bool odp_enabled_; + bool use_odp_; + struct ibv_mr *odp_mr_; + bool have_exp_qp_; bool byte_swap_atomic_result_; @@ -613,6 +623,13 @@ class ibverbs_transport faodel::Configuration &config); private: + bool + have_odp(void); + bool + have_implicit_odp(void); + int + register_odp(); + bool have_exp_qp(void); bool @@ -638,8 +655,14 @@ class ibverbs_transport void stop_progress_thread(void); + void + open_ib_device(struct ibv_device *dev); + bool + is_port_active(struct ibv_device *dev, int port); struct ibv_device * - get_ib_device(void); + find_active_ib_device(struct ibv_device **dev_list, int dev_count, int *port); + bool + select_ib_device(struct ibv_device **dev_list, int dev_count, int *port); void connect_cb( @@ -658,19 +681,19 @@ class ibverbs_transport const std::map &args, std::stringstream &results); std::string - build_webhook_path( + build_whookie_path( nnti::core::nnti_connection *conn, const char *service); std::string - build_webhook_connect_path( + build_whookie_connect_path( nnti::core::nnti_connection *conn); std::string - build_webhook_disconnect_path( + build_whookie_disconnect_path( nnti::core::nnti_connection *conn); void - register_webhook_cb(void); + register_whookie_cb(void); void - unregister_webhook_cb(void); + unregister_whookie_cb(void); NNTI_result_t create_send_op( @@ -733,19 +756,24 @@ class ibverbs_transport NNTI_event_t * create_event( nnti::core::ibverbs_cmd_msg *cmd_msg, - uint64_t offset); + uint64_t offset, + NNTI_result_t result); NNTI_event_t * create_event( - nnti::core::ibverbs_cmd_msg *cmd_msg); + nnti::core::ibverbs_cmd_msg *cmd_msg, + NNTI_result_t result); NNTI_event_t * create_event( - nnti::core::ibverbs_cmd_op *cmd_op); + nnti::core::ibverbs_cmd_op *cmd_op, + NNTI_result_t result); NNTI_event_t * create_event( - nnti::core::ibverbs_rdma_op *rdma_op); + nnti::core::ibverbs_rdma_op *rdma_op, + NNTI_result_t result); NNTI_event_t * create_event( - nnti::core::ibverbs_atomic_op *atomic_op); + nnti::core::ibverbs_atomic_op *atomic_op, + NNTI_result_t result); nnti::datatype::nnti_buffer * unpack_buffer( diff --git a/src/nnti/transports/mpi/mpi_buffer.cpp b/src/nnti/transports/mpi/mpi_buffer.cpp index 4c6a954..7cd19fb 100644 --- a/src/nnti/transports/mpi/mpi_buffer.cpp +++ b/src/nnti/transports/mpi/mpi_buffer.cpp @@ -17,7 +17,7 @@ #include #include "nnti/nnti_logger.hpp" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_callback.hpp" diff --git a/src/nnti/transports/mpi/mpi_buffer.hpp b/src/nnti/transports/mpi/mpi_buffer.hpp index b5dd341..73bfa19 100644 --- a/src/nnti/transports/mpi/mpi_buffer.hpp +++ b/src/nnti/transports/mpi/mpi_buffer.hpp @@ -18,7 +18,7 @@ #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_buffer.hpp" diff --git a/src/nnti/transports/mpi/mpi_cmd_msg.hpp b/src/nnti/transports/mpi/mpi_cmd_msg.hpp index 46e6f66..4a9c324 100644 --- a/src/nnti/transports/mpi/mpi_cmd_msg.hpp +++ b/src/nnti/transports/mpi/mpi_cmd_msg.hpp @@ -18,7 +18,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/mpi/mpi_cmd_op.hpp b/src/nnti/transports/mpi/mpi_cmd_op.hpp index 886454d..ed9ca08 100644 --- a/src/nnti/transports/mpi/mpi_cmd_op.hpp +++ b/src/nnti/transports/mpi/mpi_cmd_op.hpp @@ -18,7 +18,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/mpi/mpi_peer.hpp b/src/nnti/transports/mpi/mpi_peer.hpp index afc723c..405458c 100644 --- a/src/nnti/transports/mpi/mpi_peer.hpp +++ b/src/nnti/transports/mpi/mpi_peer.hpp @@ -15,7 +15,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_peer.hpp" #include "nnti/nnti_url.hpp" diff --git a/src/nnti/transports/mpi/mpi_transport.cpp b/src/nnti/transports/mpi/mpi_transport.cpp index 164fb0b..1abc1ee 100644 --- a/src/nnti/transports/mpi/mpi_transport.cpp +++ b/src/nnti/transports/mpi/mpi_transport.cpp @@ -43,8 +43,8 @@ #include "nnti/transports/mpi/mpi_transport.hpp" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" namespace nnti { @@ -118,7 +118,7 @@ mpi_transport::start(void) MPI_Comm_size(nnti_comm_, &nnti_comm_size_); MPI_Comm_rank(nnti_comm_, &nnti_comm_rank_); - faodel::nodeid_t nodeid = webhook::Server::GetNodeID(); + faodel::nodeid_t nodeid = whookie::Server::GetNodeID(); std::string addr = nodeid.GetIP(); std::string port = nodeid.GetPort(); url_ = nnti::core::nnti_url(addr, port); @@ -149,11 +149,11 @@ mpi_transport::start(void) return NNTI_EIO; } - stats_ = new struct webhook_stats; + stats_ = new struct whookie_stats; - assert(webhook::Server::IsRunning() && "webhook is not running. Confirm Bootstrap configuration and try again."); + assert(whookie::Server::IsRunning() && "whookie is not running. Confirm Bootstrap configuration and try again."); - register_webhook_cb(); + register_whookie_cb(); log_debug("mpi_transport", "url_=%s", url_.url().c_str()); @@ -193,7 +193,7 @@ mpi_transport::stop(void) } nthread_unlock(&new_connection_lock_); - unregister_webhook_cb(); + unregister_whookie_cb(); stop_progress_thread(); @@ -324,13 +324,13 @@ mpi_transport::connect( nthread_unlock(&new_connection_lock_); std::string reply; - std::string wh_path = build_webhook_connect_path(); + std::string wh_path = build_whookie_connect_path(); int wh_rc = 0; int retries = 5; - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); while (wh_rc != 0 && --retries) { sleep(1); - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); log_debug("mpi_transport", "retrieveData() rc=%d", wh_rc); } if (wh_rc != 0) { @@ -382,14 +382,14 @@ mpi_transport::disconnect( nthread_unlock(&new_connection_lock_); if (*peer != me_) { - std::string wh_path = build_webhook_disconnect_path(); - int wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + std::string wh_path = build_whookie_disconnect_path(); + int wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); if (wh_rc != 0) { return(NNTI_ETIMEDOUT); } } - log_debug("mpi_transport", "disconnect from %s (pid=%x) succeeded", peer->url(), peer->pid()); + log_debug("mpi_transport", "disconnect from %s (pid=%x) succeeded", peer->url().url().c_str(), peer->pid()); delete conn; delete peer; @@ -711,7 +711,8 @@ mpi_transport::dt_unpack( nnti::datatype::mpi_buffer *b = nullptr; nnti::datatype::nnti_peer *p = nullptr; - switch (*(NNTI_datatype_t*)packed_buf) { + NNTI_datatype_t t = nnti::serialize::get_datatype(packed_buf, packed_len); + switch (t) { case NNTI_dt_buffer: log_debug("base_transport", "dt is a buffer"); b = new nnti::datatype::mpi_buffer(this, packed_buf, packed_len); @@ -909,11 +910,20 @@ mpi_transport::send( log_debug("mpi_transport", "send - wr.local_offset=%lu", wr->local_offset()); rc = create_send_op(work_id, &cmd_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "create_send_op() failed"); + goto done; + } rc = execute_cmd_op(work_id, cmd_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "execute_cmd_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -933,12 +943,35 @@ mpi_transport::put( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::mpi_cmd_op *put_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); + if (work_id->wr().local_offset() + work_id->wr().length() > local_buffer->length()) { + log_error("mpi_transport", "PUT length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + + nnti::datatype::mpi_buffer *remote_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().remote_hdl(); + if (work_id->wr().remote_offset() + work_id->wr().length() > remote_buffer->length()) { + log_error("mpi_transport", "PUT length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_put_op(work_id, &put_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "create_put_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, put_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -958,12 +991,35 @@ mpi_transport::get( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::mpi_cmd_op *get_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); + if (work_id->wr().local_offset() + work_id->wr().length() > local_buffer->length()) { + log_error("mpi_transport", "GET length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + + nnti::datatype::mpi_buffer *remote_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().remote_hdl(); + if (work_id->wr().remote_offset() + work_id->wr().length() > remote_buffer->length()) { + log_error("mpi_transport", "GET length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_get_op(work_id, &get_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "create_get_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, get_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -984,11 +1040,20 @@ mpi_transport::atomic_fop( nnti::core::mpi_cmd_op *atomic_op = nullptr; rc = create_fadd_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "create_fadd_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1009,11 +1074,20 @@ mpi_transport::atomic_cswap( nnti::core::mpi_cmd_op *atomic_op = nullptr; rc = create_cswap_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "create_cswap_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("mpi_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1350,23 +1424,21 @@ mpi_transport::stats_cb( const std::map &args, std::stringstream &results) { - html::mkHeader(results, "Transfer Statistics"); - html::mkText(results,"Transfer Statistics",1); - - std::vector stats; - stats.push_back("pinned_bytes = " + std::to_string(stats_->pinned_bytes.load())); - stats.push_back("pinned_buffers = " + std::to_string(stats_->pinned_buffers.load())); - stats.push_back("unexpected_sends = " + std::to_string(stats_->unexpected_sends.load())); - stats.push_back("unexpected_recvs = " + std::to_string(stats_->unexpected_recvs.load())); - stats.push_back("short_sends = " + std::to_string(stats_->short_sends.load())); - stats.push_back("short_recvs = " + std::to_string(stats_->short_recvs.load())); - stats.push_back("long_sends = " + std::to_string(stats_->long_sends.load())); - stats.push_back("long_recvs = " + std::to_string(stats_->long_recvs.load())); - stats.push_back("gets = " + std::to_string(stats_->gets.load())); - stats.push_back("puts = " + std::to_string(stats_->puts.load())); - html::mkList(results, stats); + faodel::ReplyStream rs(args, "Transfer Statistics", &results); - html::mkFooter(results); + rs.tableBegin("Transport Statistics"); + rs.tableRow({"pinned_bytes", std::to_string(stats_->pinned_bytes.load())}); + rs.tableRow({"pinned_buffers", std::to_string(stats_->pinned_buffers.load())}); + rs.tableRow({"unexpected_sends", std::to_string(stats_->unexpected_sends.load())}); + rs.tableRow({"unexpected_recvs", std::to_string(stats_->unexpected_recvs.load())}); + rs.tableRow({"short_sends", std::to_string(stats_->short_sends.load())}); + rs.tableRow({"short_recvs", std::to_string(stats_->short_recvs.load())}); + rs.tableRow({"long_sends", std::to_string(stats_->long_sends.load())}); + rs.tableRow({"long_recvs", std::to_string(stats_->long_recvs.load())}); + rs.tableRow({"gets", std::to_string(stats_->gets.load())}); + rs.tableRow({"puts", std::to_string(stats_->puts.load())}); + rs.tableEnd(); + rs.Finish(); } void @@ -1389,7 +1461,7 @@ mpi_transport::peers_cb( } std::string -mpi_transport::build_webhook_path( +mpi_transport::build_whookie_path( const char *service) { std::stringstream wh_url; @@ -1403,40 +1475,40 @@ mpi_transport::build_webhook_path( return wh_url.str(); } std::string -mpi_transport::build_webhook_connect_path(void) +mpi_transport::build_whookie_connect_path(void) { - return build_webhook_path("connect"); + return build_whookie_path("connect"); } std::string -mpi_transport::build_webhook_disconnect_path(void) +mpi_transport::build_whookie_disconnect_path(void) { - return build_webhook_path("disconnect"); + return build_whookie_path("disconnect"); } void -mpi_transport::register_webhook_cb(void) +mpi_transport::register_whookie_cb(void) { - webhook::Server::registerHook("/nnti/mpi/connect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/mpi/connect", [this] (const std::map &args, std::stringstream &results){ connect_cb(args, results); }); - webhook::Server::registerHook("/nnti/mpi/disconnect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/mpi/disconnect", [this] (const std::map &args, std::stringstream &results){ disconnect_cb(args, results); }); - webhook::Server::registerHook("/nnti/mpi/stats", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/mpi/stats", [this] (const std::map &args, std::stringstream &results){ stats_cb(args, results); }); - webhook::Server::registerHook("/nnti/mpi/peers", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/mpi/peers", [this] (const std::map &args, std::stringstream &results){ peers_cb(args, results); }); } void -mpi_transport::unregister_webhook_cb(void) +mpi_transport::unregister_whookie_cb(void) { - webhook::Server::deregisterHook("/nnti/mpi/connect"); - webhook::Server::deregisterHook("/nnti/mpi/disconnect"); - webhook::Server::deregisterHook("/nnti/mpi/stats"); - webhook::Server::deregisterHook("/nnti/mpi/peers"); + whookie::Server::deregisterHook("/nnti/mpi/connect"); + whookie::Server::deregisterHook("/nnti/mpi/disconnect"); + whookie::Server::deregisterHook("/nnti/mpi/stats"); + whookie::Server::deregisterHook("/nnti/mpi/peers"); } void @@ -1631,17 +1703,40 @@ mpi_transport::execute_cmd_op( nnti::datatype::mpi_peer *peer = (nnti::datatype::mpi_peer *)work_id->wr().peer(); nnti::core::mpi_connection *conn = (nnti::core::mpi_connection *)peer->conn(); - nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); - uint64_t local_offset = work_id->wr().local_offset(); + nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); + uint64_t local_offset = work_id->wr().local_offset(); + uint64_t local_length = local_buffer->length(); + + nnti::datatype::mpi_buffer *remote_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().remote_hdl(); + + uint64_t op_length = work_id->wr().length(); + + // bounds checking - MPI won't help us do bounds checking because we + // are telling it to send/receive based on the app's WR which means + // the lengths will always match. We might get a segfault or other + // memory error if we try to send/receive beyond the allocated buffer + // limits, but that's not a very nice thing to do to the app. + if (local_offset + op_length > local_length) { + log_error("mpi_transport", "SEND length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (remote_buffer) { + uint64_t remote_offset = work_id->wr().remote_offset(); + uint64_t remote_length = remote_buffer->length(); + if (remote_offset + op_length > remote_length) { + log_error("mpi_transport", "SEND length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } + } if (!cmd_op->eager()) { log_debug("mpi_transport", "posting long send Issend(%s) (payload=%p, local_offset=%lu, length=%lu, peer=%d, cmd_tag=%d", - cmd_op->toString().c_str(), local_buffer->payload(), local_offset, work_id->wr().length(), peer->rank(), local_buffer->cmd_tag()); + cmd_op->toString().c_str(), local_buffer->payload(), local_offset, op_length, peer->rank(), local_buffer->cmd_tag()); std::unique_lock mpi_lock(mpi_mutex_); mpi_rc = MPI_Issend((char*)local_buffer->payload() + local_offset, - work_id->wr().length(), + op_length, MPI_BYTE, peer->rank(), local_buffer->cmd_tag(), @@ -1728,33 +1823,58 @@ mpi_transport::execute_rdma_op( nnti::datatype::mpi_peer *peer = (nnti::datatype::mpi_peer *)work_id->wr().peer(); nnti::core::mpi_connection *conn = (nnti::core::mpi_connection *)peer->conn(); - nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); - uint64_t local_offset = work_id->wr().local_offset(); + nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); + uint64_t local_offset = work_id->wr().local_offset(); + uint64_t local_length = local_buffer->length(); + nnti::datatype::mpi_buffer *remote_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().remote_hdl(); uint64_t remote_offset = work_id->wr().remote_offset(); + uint64_t remote_length = remote_buffer->length(); + + uint64_t op_length = work_id->wr().length(); + + // bounds checking - MPI won't help us do bounds checking because we + // are telling it to send/receive based on the app's WR which means + // the lengths will always match. We might get a segfault or other + // memory error if we try to send/receive beyond the allocated buffer + // limits, but that's not a very nice thing to do to the app. + if (local_offset + op_length > local_length) { + log_error("mpi_transport", "RDMA length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (remote_offset + op_length > remote_length) { + log_error("mpi_transport", "RDMA length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } switch (work_id->wr().op()) { case NNTI_OP_GET: mpi_lock.lock(); mpi_rc = MPI_Irecv((char*)local_buffer->payload() + local_offset, - work_id->wr().length(), + op_length, MPI_BYTE, peer->rank(), local_buffer->get_tag(), MPI_COMM_WORLD, &rdma_op->rdma_request()); mpi_lock.unlock(); + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Irecv() failed - rc=%d", mpi_rc); + } break; case NNTI_OP_PUT: mpi_lock.lock(); mpi_rc = MPI_Issend((char*)local_buffer->payload() + local_offset, - work_id->wr().length(), + op_length, MPI_BYTE, peer->rank(), remote_buffer->put_tag(), MPI_COMM_WORLD, &rdma_op->rdma_request()); mpi_lock.unlock(); + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Issend() failed - rc=%d", mpi_rc); + } break; case NNTI_OP_NOOP: case NNTI_OP_SEND: @@ -1775,6 +1895,9 @@ mpi_transport::execute_rdma_op( MPI_COMM_WORLD, &rdma_op->cmd_request()); mpi_lock.unlock(); + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Issend() failed - rc=%d", mpi_rc); + } mpi_transport::add_outstanding_cmd_op( rdma_op->cmd_request(), rdma_op); @@ -1837,10 +1960,29 @@ mpi_transport::execute_atomic_op( nnti::datatype::mpi_peer *peer = (nnti::datatype::mpi_peer *)work_id->wr().peer(); nnti::core::mpi_connection *conn = (nnti::core::mpi_connection *)peer->conn(); - nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); - uint64_t local_offset = work_id->wr().local_offset(); + nnti::datatype::mpi_buffer *local_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().local_hdl(); + uint64_t local_offset = work_id->wr().local_offset(); + uint64_t local_length = local_buffer->length(); + nnti::datatype::mpi_buffer *remote_buffer = (nnti::datatype::mpi_buffer *)work_id->wr().remote_hdl(); uint64_t remote_offset = work_id->wr().remote_offset(); + uint64_t remote_length = remote_buffer->length(); + + uint64_t op_length = work_id->wr().length(); + + // bounds checking - MPI won't help us do bounds checking because we + // are telling it to send/receive based on the app's WR which means + // the lengths will always match. We might get a segfault or other + // memory error if we try to send/receive beyond the allocated buffer + // limits, but that's not a very nice thing to do to the app. + if (local_offset + op_length > local_length) { + log_error("mpi_transport", "RDMA length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (remote_offset + op_length > remote_length) { + log_error("mpi_transport", "RDMA length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } std::unique_lock mpi_lock(mpi_mutex_); mpi_rc = MPI_Irecv((char*)local_buffer->payload() + local_offset, @@ -1998,14 +2140,15 @@ mpi_transport::complete_get_command(nnti::core::mpi_cmd_msg *cmd_msg) MPI_Status status; std::unique_lock mpi_lock(mpi_mutex_); - mpi_rc = MPI_Issend((char*)target_buffer->payload() + cmd_msg->target_offset(), + mpi_rc = MPI_Ssend((char*)target_buffer->payload() + cmd_msg->target_offset(), cmd_msg->payload_length(), MPI_BYTE, peer->rank(), initiator_buffer->get_tag(), - MPI_COMM_WORLD, - &req); - mpi_rc = MPI_Wait(&req, &status); + MPI_COMM_WORLD); + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Ssend failed. rc=%d", mpi_rc); + } mpi_lock.unlock(); cmd_msg->post_recv(); @@ -2159,7 +2302,7 @@ mpi_transport::poll_msg_requests(std::vector &reqs, int &index, int std::unique_lock mpi_lock(mpi_mutex_, std::defer_lock); std::lock(req_lock, mpi_lock); int mpi_rc = MPI_Testany(outstanding_msg_requests_.size(), &outstanding_msg_requests_[0], &index, &done, &event); - if ((mpi_rc == MPI_SUCCESS) && (index >= 0) && (done == TRUE)) { + if ((mpi_rc == MPI_SUCCESS) && (index >= 0) && (done == 1)) { // get the cmd_msg now while we hold the lock cmd_msg = outstanding_msgs_[index]; remove_outstanding_cmd_msg(req_lock, cmd_msg->index()); @@ -2184,14 +2327,14 @@ mpi_transport::progress_msg_requests(void) mpi_rc = poll_msg_requests(outstanding_msg_requests_, index, done, event, cmd_msg); /* MPI_Testany() says no active requests. this is not fatal. */ - if ((mpi_rc == MPI_SUCCESS) && (index == MPI_UNDEFINED) && (done == TRUE)) { + if ((mpi_rc == MPI_SUCCESS) && (index == MPI_UNDEFINED) && (done == 1)) { log_debug("mpi_transport", "MPI_Testany() says there a no active requests (mpi_rc=%d, index=%d, done=%d)", mpi_rc, index, done); nnti_rc = NNTI_ENOENT; } /* MPI_Testany() says requests have completed */ else if (mpi_rc == MPI_SUCCESS) { /* case 1: success */ - if (done == FALSE) { + if (done == 0) { nnti_rc = NNTI_EWOULDBLOCK; } else { nnti_rc = NNTI_OK; @@ -2251,7 +2394,7 @@ mpi_transport::poll_op_requests(std::vector &reqs, int &index, int std::unique_lock mpi_lock(mpi_mutex_, std::defer_lock); std::lock(req_lock, mpi_lock); int mpi_rc = MPI_Testany(outstanding_op_requests_.size(), &outstanding_op_requests_[0], &index, &done, &event); - if ((mpi_rc == MPI_SUCCESS) && (index >= 0) && (done == TRUE)) { + if ((mpi_rc == MPI_SUCCESS) && (index >= 0) && (done == 1)) { // get the cmd_op now while we hold the lock cmd_op = outstanding_ops_[index]; remove_outstanding_cmd_op(req_lock, cmd_op->index()); @@ -2276,14 +2419,14 @@ mpi_transport::progress_op_requests(void) mpi_rc = poll_op_requests(outstanding_op_requests_, index, done, event, cmd_op); /* MPI_Testany() says no active requests. this is not fatal. */ - if ((mpi_rc == MPI_SUCCESS) && (index == MPI_UNDEFINED) && (done == TRUE)) { + if ((mpi_rc == MPI_SUCCESS) && (index == MPI_UNDEFINED) && (done == 1)) { log_debug("mpi_transport", "MPI_Testany() says there a no active requests (mpi_rc=%d, index=%d, done=%d)", mpi_rc, index, done); nnti_rc = NNTI_ENOENT; } /* MPI_Testany() says requests have completed */ else if (mpi_rc == MPI_SUCCESS) { /* case 1: success */ - if (done == FALSE) { + if (done == 0) { nnti_rc = NNTI_EWOULDBLOCK; } else { nnti_rc = NNTI_OK; @@ -2305,10 +2448,13 @@ mpi_transport::progress_op_requests(void) break; case NNTI_OP_SEND: if (!cmd_op->eager()) { - std::unique_lock mpi_lock(mpi_mutex_); - MPI_Wait(&cmd_op->long_send_request(), &event); + mpi_lock.lock(); + mpi_rc = MPI_Wait(&cmd_op->long_send_request(), &event); mpi_lock.unlock(); + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Wait(long_send_request) (mpi_rc=%d)", mpi_rc); + } log_debug("mpi_transport", "Long Send Event= {"); log_debug("mpi_transport", "\tsource = %d", event.MPI_SOURCE); log_debug("mpi_transport", "\ttag = %d", event.MPI_TAG); @@ -2321,8 +2467,12 @@ mpi_transport::progress_op_requests(void) case NNTI_OP_ATOMIC_FADD: case NNTI_OP_ATOMIC_CSWAP: mpi_lock.lock(); - MPI_Wait(&cmd_op->rdma_request(), &event); + mpi_rc = MPI_Wait(&cmd_op->rdma_request(), &event); mpi_lock.unlock(); + + if (mpi_rc != MPI_SUCCESS) { + log_error("mpi_transport", "MPI_Wait(rdma_request) (mpi_rc=%d)", mpi_rc); + } log_debug("mpi_transport", "RDMA Event= {"); log_debug("mpi_transport", "\tsource = %d", event.MPI_SOURCE); log_debug("mpi_transport", "\ttag = %d", event.MPI_TAG); diff --git a/src/nnti/transports/mpi/mpi_transport.hpp b/src/nnti/transports/mpi/mpi_transport.hpp index 14d7383..7d42231 100644 --- a/src/nnti/transports/mpi/mpi_transport.hpp +++ b/src/nnti/transports/mpi/mpi_transport.hpp @@ -66,7 +66,7 @@ class mpi_transport friend class nnti::datatype::mpi_buffer; private: - struct webhook_stats { + struct whookie_stats { std::atomic pinned_bytes; std::atomic pinned_buffers; std::atomic unexpected_sends; @@ -79,7 +79,7 @@ class mpi_transport std::atomic gets; std::atomic puts; - webhook_stats() + whookie_stats() { pinned_bytes.store(0); pinned_buffers.store(0); @@ -138,7 +138,7 @@ class mpi_transport uint64_t cmd_op_freelist_size_; nnti::core::nnti_freelist *cmd_op_freelist_; - struct webhook_stats *stats_; + struct whookie_stats *stats_; NNTI_attrs_t attrs_; @@ -658,16 +658,16 @@ class mpi_transport const std::map &args, std::stringstream &results); std::string - build_webhook_path( + build_whookie_path( const char *service); std::string - build_webhook_connect_path(void); + build_whookie_connect_path(void); std::string - build_webhook_disconnect_path(void); + build_whookie_disconnect_path(void); void - register_webhook_cb(void); + register_whookie_cb(void); void - unregister_webhook_cb(void); + unregister_whookie_cb(void); NNTI_result_t create_send_op( diff --git a/src/nnti/transports/ugni/ugni_atomic_op.hpp b/src/nnti/transports/ugni/ugni_atomic_op.hpp index d72d1c4..226872a 100644 --- a/src/nnti/transports/ugni/ugni_atomic_op.hpp +++ b/src/nnti/transports/ugni/ugni_atomic_op.hpp @@ -18,7 +18,7 @@ #include "faodel-common/MutexWrapper.hh" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ugni/ugni_buffer.cpp b/src/nnti/transports/ugni/ugni_buffer.cpp index f44a423..7401037 100644 --- a/src/nnti/transports/ugni/ugni_buffer.cpp +++ b/src/nnti/transports/ugni/ugni_buffer.cpp @@ -18,7 +18,7 @@ #include #include "nnti/nnti_logger.hpp" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_callback.hpp" diff --git a/src/nnti/transports/ugni/ugni_buffer.hpp b/src/nnti/transports/ugni/ugni_buffer.hpp index 356ac46..b082123 100644 --- a/src/nnti/transports/ugni/ugni_buffer.hpp +++ b/src/nnti/transports/ugni/ugni_buffer.hpp @@ -17,7 +17,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_buffer.hpp" diff --git a/src/nnti/transports/ugni/ugni_cmd_msg.hpp b/src/nnti/transports/ugni/ugni_cmd_msg.hpp index 47c67ee..b705915 100644 --- a/src/nnti/transports/ugni/ugni_cmd_msg.hpp +++ b/src/nnti/transports/ugni/ugni_cmd_msg.hpp @@ -16,7 +16,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ugni/ugni_cmd_op.cpp b/src/nnti/transports/ugni/ugni_cmd_op.cpp index 8a4b89f..ed60af9 100644 --- a/src/nnti/transports/ugni/ugni_cmd_op.cpp +++ b/src/nnti/transports/ugni/ugni_cmd_op.cpp @@ -15,7 +15,7 @@ #include "faodel-common/MutexWrapper.hh" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ugni/ugni_cmd_op.hpp b/src/nnti/transports/ugni/ugni_cmd_op.hpp index e99372a..26b5ecc 100644 --- a/src/nnti/transports/ugni/ugni_cmd_op.hpp +++ b/src/nnti/transports/ugni/ugni_cmd_op.hpp @@ -18,7 +18,7 @@ #include "faodel-common/MutexWrapper.hh" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ugni/ugni_cmd_tgt.hpp b/src/nnti/transports/ugni/ugni_cmd_tgt.hpp index 36528ba..e438cc8 100644 --- a/src/nnti/transports/ugni/ugni_cmd_tgt.hpp +++ b/src/nnti/transports/ugni/ugni_cmd_tgt.hpp @@ -18,7 +18,7 @@ #include "faodel-common/MutexWrapper.hh" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" diff --git a/src/nnti/transports/ugni/ugni_connection.cpp b/src/nnti/transports/ugni/ugni_connection.cpp index 29d79fe..2a263d7 100644 --- a/src/nnti/transports/ugni/ugni_connection.cpp +++ b/src/nnti/transports/ugni/ugni_connection.cpp @@ -146,7 +146,7 @@ namespace core { return ss.str(); } /* - * generate a key=value (one per line) string that can be included in a WebHook reply + * generate a key=value (one per line) string that can be included in a Whookie reply */ std::string ugni_connection::reply_string(void) diff --git a/src/nnti/transports/ugni/ugni_connection.hpp b/src/nnti/transports/ugni/ugni_connection.hpp index b1fd011..b39a425 100644 --- a/src/nnti/transports/ugni/ugni_connection.hpp +++ b/src/nnti/transports/ugni/ugni_connection.hpp @@ -112,7 +112,7 @@ class ugni_connection query_string(void); /* - * generate a key=value (one per line) string that can be included in a WebHook reply + * generate a key=value (one per line) string that can be included in a Whookie reply */ std::string reply_string(void); diff --git a/src/nnti/transports/ugni/ugni_peer.hpp b/src/nnti/transports/ugni/ugni_peer.hpp index 612d4e7..1b6a50e 100644 --- a/src/nnti/transports/ugni/ugni_peer.hpp +++ b/src/nnti/transports/ugni/ugni_peer.hpp @@ -15,7 +15,7 @@ #include #include -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_peer.hpp" #include "nnti/nnti_url.hpp" diff --git a/src/nnti/transports/ugni/ugni_rdma_op.hpp b/src/nnti/transports/ugni/ugni_rdma_op.hpp index b5cc2a4..8db8a23 100644 --- a/src/nnti/transports/ugni/ugni_rdma_op.hpp +++ b/src/nnti/transports/ugni/ugni_rdma_op.hpp @@ -18,7 +18,7 @@ #include "faodel-common/MutexWrapper.hh" -#include "nnti/nnti_packable.h" +#include "nnti/nnti_serialize.hpp" #include "nnti/nnti_types.h" #include "nnti/nnti_threads.h" #include "nnti/nnti_util.hpp" @@ -50,7 +50,8 @@ class ugni_rdma_op nnti::transports::ugni_transport *transport) : nnti_op(), transport_(transport), - state_(op_state::INIT) + state_(op_state::INIT), + result_(NNTI_OK) { sm_lock_ = faodel::GenerateMutex("pthreads"); @@ -61,7 +62,8 @@ class ugni_rdma_op nnti::datatype::nnti_work_id *wid) : nnti_op(wid), transport_(transport), - state_(op_state::INIT) + state_(op_state::INIT), + result_(NNTI_OK) { sm_lock_ = faodel::GenerateMutex("pthreads"); @@ -77,14 +79,20 @@ class ugni_rdma_op void set(nnti::datatype::nnti_work_id *wid) { - id_ = next_id_.fetch_add(1); - wid_ = wid; - state_ = op_state::INIT; + id_ = next_id_.fetch_add(1); + wid_ = wid; + state_ = op_state::INIT; + result_ = NNTI_OK; populate_post_desc(wid); return; } + NNTI_result_t + result() + { + return result_; + } virtual std::string toString() @@ -151,12 +159,15 @@ class ugni_rdma_op WAIT_RDMA_COMPLETE, ISSUE_RDMA_EVENT, + ERROR, + CLEANUP, DONE }; - op_state state_; + op_state state_; faodel::MutexWrapper *sm_lock_; + NNTI_result_t result_; op_state execute_rdma(void) @@ -170,7 +181,7 @@ class ugni_rdma_op nnti::datatype::nnti_peer *peer = (nnti::datatype::nnti_peer *)wid_->wr().peer(); nnti::core::ugni_connection *conn = (nnti::core::ugni_connection *)peer->conn(); - log_debug("ugni_rdma_op", "calling PostFma(fma get ; ep_hdl(%llu) transport_global_data.ep_cq_hdl(%llu) local_mem_hdl(%llu, %llu) remote_mem_hdl(%llu, %llu))", + log_debug("ugni_rdma_op", "calling PostRdma(ep_hdl(%llu) transport_global_data.ep_cq_hdl(%llu) local_mem_hdl(%llu, %llu) remote_mem_hdl(%llu, %llu))", conn->unexpected_ep_hdl(), conn->unexpected_cq_hdl(), post_desc_.local_mem_hndl.qword1, post_desc_.local_mem_hndl.qword2, post_desc_.remote_mem_hndl.qword1, post_desc_.remote_mem_hndl.qword2); @@ -182,21 +193,26 @@ class ugni_rdma_op 0); if (gni_rc != GNI_RC_SUCCESS) { log_error("ugni_rdma_op", "EpSetEventData(rdma_ep_hdl_) failed: %d", gni_rc); - abort(); + goto error; } gni_rc=GNI_PostRdma( conn->rdma_ep_hdl(), &post_desc_); - nthread_unlock(&transport_->ugni_lock_); if (gni_rc != GNI_RC_SUCCESS) { - log_error("ugni_cmd_tgt", "failed to post BTE (gni_rc=%d): %s", gni_rc, strerror(errno)); - abort(); + log_error("ugni_rdma_op", "failed to post BTE (gni_rc=%d): %s", gni_rc, strerror(errno)); + result_ = NNTI_EMSGSIZE; + goto error; } - log_debug("ugni_cmd_tgt", "called PostRdma(rdma get)"); - - log_debug("ugni_rdma_op", "exit"); + nthread_unlock(&transport_->ugni_lock_); + log_debug("ugni_rdma_op", "called PostRdma()"); + log_debug("ugni_rdma_op", "exit - success"); return op_state::WAIT_RDMA_COMPLETE; + +error: + nthread_unlock(&transport_->ugni_lock_); + log_debug("ugni_rdma_op", "exit - failure"); + return op_state::ERROR; } NNTI_event_t * create_event(void) @@ -211,7 +227,7 @@ class ugni_rdma_op } e->trans_hdl = nnti::transports::transport::to_hdl(transport_); - e->result = NNTI_OK; + e->result = result_; e->op = wr.op(); e->peer = wr.peer(); e->length = wr.length(); @@ -305,6 +321,9 @@ class ugni_rdma_op case op_state::ISSUE_RDMA_EVENT: state_ = issue_rdma_event(); break; + case op_state::ERROR: + state_ = op_state::CLEANUP; + break; case op_state::CLEANUP: update_stats(); state_ = op_state::DONE; diff --git a/src/nnti/transports/ugni/ugni_transport.cpp b/src/nnti/transports/ugni/ugni_transport.cpp index 171c2d2..2e8ac70 100644 --- a/src/nnti/transports/ugni/ugni_transport.cpp +++ b/src/nnti/transports/ugni/ugni_transport.cpp @@ -45,8 +45,8 @@ #include "nnti/transports/ugni/ugni_transport.hpp" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" namespace nnti { @@ -260,7 +260,7 @@ ugni_transport::start(void) goto cleanup; } - nodeid = webhook::Server::GetNodeID(); + nodeid = whookie::Server::GetNodeID(); addr = nodeid.GetIP(); port = nodeid.GetPort(); url_ = nnti::core::nnti_url(addr, port); @@ -286,12 +286,12 @@ ugni_transport::start(void) } NNTI_STATS_DATA( - stats_ = new webhook_stats; + stats_ = new whookie_stats; ) - assert(webhook::Server::IsRunning() && "webhook is not running. Confirm Bootstrap configuration and try again."); + assert(whookie::Server::IsRunning() && "whookie is not running. Confirm Bootstrap configuration and try again."); - register_webhook_cb(); + register_whookie_cb(); log_debug("ugni_transport", "url_=%s", url_.url().c_str()); @@ -327,7 +327,7 @@ ugni_transport::stop(void) } nthread_unlock(&new_connection_lock_); - unregister_webhook_cb(); + unregister_whookie_cb(); stop_progress_thread(); @@ -460,13 +460,13 @@ ugni_transport::connect( nthread_unlock(&new_connection_lock_); std::string reply; - std::string wh_path = build_webhook_connect_path(conn); + std::string wh_path = build_whookie_connect_path(conn); int wh_rc = 0; int retries = 5; - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); while (wh_rc != 0 && --retries) { sleep(1); - wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); } if (wh_rc != 0) { log_debug("ugni_transport", "connect() timed out"); @@ -515,14 +515,14 @@ ugni_transport::disconnect( nthread_unlock(&new_connection_lock_); if (*peer != me_) { - std::string wh_path = build_webhook_disconnect_path(conn); - int wh_rc = webhook::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); + std::string wh_path = build_whookie_disconnect_path(conn); + int wh_rc = whookie::retrieveData(peer_url.hostname(), peer_url.port(), wh_path, &reply); if (wh_rc != 0) { return(NNTI_ETIMEDOUT); } } - log_debug("ugni_transport", "disconnect from %s (pid=%x) succeeded", peer->url(), peer->pid()); + log_debug("ugni_transport", "disconnect from %s (pid=%x) succeeded", peer->url().url().c_str(), peer->pid()); delete conn; delete peer; @@ -817,7 +817,8 @@ ugni_transport::dt_unpack( nnti::datatype::ugni_buffer *b = nullptr; nnti::datatype::nnti_peer *p = nullptr; - switch (*(NNTI_datatype_t*)packed_buf) { + NNTI_datatype_t t = nnti::serialize::get_datatype(packed_buf, packed_len); + switch (t) { case NNTI_dt_buffer: log_debug("ugni_transport", "dt is a buffer"); b = new nnti::datatype::ugni_buffer(this, packed_buf, packed_len); @@ -1017,7 +1018,7 @@ ugni_transport::send( rc = create_send_op(work_id, &cmd_op); if (rc != NNTI_OK) { log_error("ugni_transport", "create_send_op() failed"); - goto cleanup; + goto done; } log_debug("ugni_transport", "cmd_op(%p) id(%u)", cmd_op, cmd_op->id()); @@ -1025,12 +1026,12 @@ ugni_transport::send( rc = execute_cmd_op(work_id, cmd_op); if (rc != NNTI_OK) { log_error("ugni_transport", "execute_cmd_op() failed"); - goto cleanup; + goto done; } *wid = (NNTI_work_id_t)work_id; -cleanup: +done: return rc; } @@ -1051,12 +1052,33 @@ ugni_transport::put( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::ugni_rdma_op *put_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + const nnti::datatype::ugni_work_request &gni_wr = (const nnti::datatype::ugni_work_request &)work_id->wr(); + if (gni_wr.local_offset() + gni_wr.length() > gni_wr.local_length()) { + log_error("ugni_transport", "PUT length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (gni_wr.remote_offset() + gni_wr.length() > gni_wr.remote_length()) { + log_error("ugni_transport", "PUT length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_put_op(work_id, &put_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "create_put_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, put_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1076,12 +1098,33 @@ ugni_transport::get( nnti::datatype::nnti_work_id *work_id = new nnti::datatype::nnti_work_id(*wr); nnti::core::ugni_rdma_op *get_op = nullptr; +#ifdef NNTI_ENABLE_ARGS_CHECKING + const nnti::datatype::ugni_work_request &gni_wr = (const nnti::datatype::ugni_work_request &)work_id->wr(); + if (gni_wr.local_offset() + gni_wr.length() > gni_wr.local_length()) { + log_error("ugni_transport", "GET length extends beyond the end of local buffer"); + return NNTI_EMSGSIZE; + } + if (gni_wr.remote_offset() + gni_wr.length() > gni_wr.remote_length()) { + log_error("ugni_transport", "GET length extends beyond the end of remote buffer"); + return NNTI_EMSGSIZE; + } +#endif + rc = create_get_op(work_id, &get_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "create_get_op() failed"); + goto done; + } rc = execute_rdma_op(work_id, get_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "execute_rdma_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1102,11 +1145,20 @@ ugni_transport::atomic_fop( nnti::core::ugni_atomic_op *atomic_op = nullptr; rc = create_fadd_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "create_fadd_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1127,11 +1179,20 @@ ugni_transport::atomic_cswap( nnti::core::ugni_atomic_op *atomic_op = nullptr; rc = create_cswap_op(work_id, &atomic_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "create_cswap_op() failed"); + goto done; + } rc = execute_atomic_op(work_id, atomic_op); + if (rc != NNTI_OK) { + log_error("ugni_transport", "execute_atomic_op() failed"); + goto done; + } *wid = (NNTI_work_id_t)work_id; - return NNTI_OK; +done: + return rc; } /** @@ -1903,25 +1964,23 @@ ugni_transport::disconnect_cb(const std::map &args, std void ugni_transport::stats_cb(const std::map &args, std::stringstream &results) { - html::mkHeader(results, "Transfer Statistics"); - html::mkText(results,"Transfer Statistics",1); + faodel::ReplyStream rs(args, "Transfer Statistics", &results); NNTI_STATS_DATA( - std::vector stats; - stats.push_back("pinned_bytes = " + std::to_string(stats_->pinned_bytes.load())); - stats.push_back("pinned_buffers = " + std::to_string(stats_->pinned_buffers.load())); - stats.push_back("unexpected_sends = " + std::to_string(stats_->unexpected_sends.load())); - stats.push_back("unexpected_recvs = " + std::to_string(stats_->unexpected_recvs.load())); - stats.push_back("short_sends = " + std::to_string(stats_->short_sends.load())); - stats.push_back("short_recvs = " + std::to_string(stats_->short_recvs.load())); - stats.push_back("long_sends = " + std::to_string(stats_->long_sends.load())); - stats.push_back("long_recvs = " + std::to_string(stats_->long_recvs.load())); - stats.push_back("gets = " + std::to_string(stats_->gets.load())); - stats.push_back("puts = " + std::to_string(stats_->puts.load())); - html::mkList(results, stats); + rs.tableBegin("Transport Statistics"); + rs.tableRow({"pinned_bytes", std::to_string(stats_->pinned_bytes.load())}); + rs.tableRow({"pinned_buffers", std::to_string(stats_->pinned_buffers.load())}); + rs.tableRow({"unexpected_sends", std::to_string(stats_->unexpected_sends.load())}); + rs.tableRow({"unexpected_recvs", std::to_string(stats_->unexpected_recvs.load())}); + rs.tableRow({"short_sends", std::to_string(stats_->short_sends.load())}); + rs.tableRow({"short_recvs", std::to_string(stats_->short_recvs.load())}); + rs.tableRow({"long_sends", std::to_string(stats_->long_sends.load())}); + rs.tableRow({"long_recvs", std::to_string(stats_->long_recvs.load())}); + rs.tableRow({"gets", std::to_string(stats_->gets.load())}); + rs.tableRow({"puts", std::to_string(stats_->puts.load())}); + rs.tableEnd(); ) - - html::mkFooter(results); + rs.Finish(); } void @@ -1942,7 +2001,7 @@ ugni_transport::peers_cb(const std::map &args, std::str } std::string -ugni_transport::build_webhook_path( +ugni_transport::build_whookie_path( const char *service) { std::stringstream wh_url; @@ -1957,66 +2016,66 @@ ugni_transport::build_webhook_path( return wh_url.str(); } std::string -ugni_transport::build_webhook_path( +ugni_transport::build_whookie_path( nnti::core::nnti_connection *conn, const char *service) { std::stringstream wh_url; - wh_url << build_webhook_path(service); + wh_url << build_whookie_path(service); wh_url << conn->query_string(); return wh_url.str(); } std::string -ugni_transport::build_webhook_connect_path(void) +ugni_transport::build_whookie_connect_path(void) { - return build_webhook_path("connect"); + return build_whookie_path("connect"); } std::string -ugni_transport::build_webhook_connect_path( +ugni_transport::build_whookie_connect_path( nnti::core::nnti_connection *conn) { - return build_webhook_path(conn, "connect"); + return build_whookie_path(conn, "connect"); } std::string -ugni_transport::build_webhook_disconnect_path(void) +ugni_transport::build_whookie_disconnect_path(void) { - return build_webhook_path("disconnect"); + return build_whookie_path("disconnect"); } std::string -ugni_transport::build_webhook_disconnect_path( +ugni_transport::build_whookie_disconnect_path( nnti::core::nnti_connection *conn) { - return build_webhook_path(conn, "disconnect"); + return build_whookie_path(conn, "disconnect"); } void -ugni_transport::register_webhook_cb(void) +ugni_transport::register_whookie_cb(void) { - webhook::Server::registerHook("/nnti/ugni/connect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ugni/connect", [this] (const std::map &args, std::stringstream &results){ connect_cb(args, results); }); - webhook::Server::registerHook("/nnti/ugni/disconnect", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ugni/disconnect", [this] (const std::map &args, std::stringstream &results){ disconnect_cb(args, results); }); - webhook::Server::registerHook("/nnti/ugni/stats", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ugni/stats", [this] (const std::map &args, std::stringstream &results){ stats_cb(args, results); }); - webhook::Server::registerHook("/nnti/ugni/peers", [this] (const std::map &args, std::stringstream &results){ + whookie::Server::registerHook("/nnti/ugni/peers", [this] (const std::map &args, std::stringstream &results){ peers_cb(args, results); }); } void -ugni_transport::unregister_webhook_cb(void) +ugni_transport::unregister_whookie_cb(void) { - log_debug("ugni_transport", "unregister_webhook_cb() - enter"); - webhook::Server::deregisterHook("/nnti/ugni/connect"); - webhook::Server::deregisterHook("/nnti/ugni/disconnect"); - webhook::Server::deregisterHook("/nnti/ugni/stats"); - webhook::Server::deregisterHook("/nnti/ugni/peers"); - log_debug("ugni_transport", "unregister_webhook_cb() - exit"); + log_debug("ugni_transport", "unregister_whookie_cb() - enter"); + whookie::Server::deregisterHook("/nnti/ugni/connect"); + whookie::Server::deregisterHook("/nnti/ugni/disconnect"); + whookie::Server::deregisterHook("/nnti/ugni/stats"); + whookie::Server::deregisterHook("/nnti/ugni/peers"); + log_debug("ugni_transport", "unregister_whookie_cb() - exit"); } NNTI_result_t @@ -2127,8 +2186,9 @@ ugni_transport::execute_rdma_op( log_debug("ugni_transport", "execute_rdma_op() - enter"); rdma_op->update(nullptr); + rc = rdma_op->result(); - log_debug("ugni_transport", "execute_rdma_op() - exit"); + log_debug("ugni_transport", "execute_rdma_op(rc=%d) - exit", rc); return rc; } diff --git a/src/nnti/transports/ugni/ugni_transport.hpp b/src/nnti/transports/ugni/ugni_transport.hpp index 0c08715..445905e 100644 --- a/src/nnti/transports/ugni/ugni_transport.hpp +++ b/src/nnti/transports/ugni/ugni_transport.hpp @@ -85,7 +85,7 @@ class ugni_transport private: NNTI_STATS_DATA( - struct webhook_stats { + struct whookie_stats { std::atomic pinned_bytes; std::atomic pinned_buffers; std::atomic unexpected_sends; @@ -100,7 +100,7 @@ class ugni_transport std::atomic fadds; std::atomic cswaps; - webhook_stats() + whookie_stats() { pinned_bytes.store(0); pinned_buffers.store(0); @@ -118,7 +118,7 @@ class ugni_transport } }; - struct webhook_stats *stats_; + struct whookie_stats *stats_; ) struct drc_info_t { @@ -667,26 +667,26 @@ class ugni_transport const std::map &args, std::stringstream &results); std::string - build_webhook_path( + build_whookie_path( const char *service); std::string - build_webhook_path( + build_whookie_path( nnti::core::nnti_connection *conn, const char *service); std::string - build_webhook_connect_path(void); + build_whookie_connect_path(void); std::string - build_webhook_connect_path( + build_whookie_connect_path( nnti::core::nnti_connection *conn); std::string - build_webhook_disconnect_path(void); + build_whookie_disconnect_path(void); std::string - build_webhook_disconnect_path( + build_whookie_disconnect_path( nnti::core::nnti_connection *conn); void - register_webhook_cb(void); + register_whookie_cb(void); void - unregister_webhook_cb(void); + unregister_whookie_cb(void); NNTI_result_t create_send_op( diff --git a/src/opbox/CMakeLists.txt b/src/opbox/CMakeLists.txt index 576130d..e04fc38 100644 --- a/src/opbox/CMakeLists.txt +++ b/src/opbox/CMakeLists.txt @@ -51,7 +51,7 @@ set(SOURCES add_subdirectory( net ) -LIST( APPEND Opbox_imports lunasa ${FaodelNetlib_TARGETS} services webhook common sbl Boost::serialization ) +LIST( APPEND Opbox_imports lunasa ${FaodelNetlib_TARGETS} services whookie common sbl Boost::serialization ) add_library( opbox ${HEADERS} ${SOURCES} ) target_link_libraries( opbox PUBLIC ${Opbox_imports} ) diff --git a/src/opbox/README_OpBox.md b/src/opbox/README_OpBox.md index adda8ab..d3a6c11 100644 --- a/src/opbox/README_OpBox.md +++ b/src/opbox/README_OpBox.md @@ -195,7 +195,7 @@ OpBox has the following build dependencies: | -------------- | ----------------------------------- | | FAODEL:SBL | Uses logging capabilities for boost | | FAODEL:Common | Uses bootstrap and `nodeid_t` | -| FAODEL:WebHook | For status info and new connections | +| FAODEL:Whookie | For status info and new connections | | FAODEL:Lunasa | For network memory management | | Boost | Uses boost and systems components | diff --git a/src/opbox/common/Message.hh b/src/opbox/common/Message.hh index 3b9b2de..8ec8bd1 100644 --- a/src/opbox/common/Message.hh +++ b/src/opbox/common/Message.hh @@ -25,6 +25,9 @@ namespace net { } // end namespace opbox::net +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * @brief Provides a basic header for all OpBox messages. User may place addition info in body * @@ -74,7 +77,7 @@ struct message_t { std::string str(int depth=0, int indent=0) const; }; - +#pragma GCC diagnostic pop const uint32_t MESSAGE_MTU = 2048; //!< Maximum Transfer Unit for underlying network const uint32_t MESSAGE_BODY_MTU = (MESSAGE_MTU - sizeof(message_t)); //!< Maximum size the body can be for an MTU diff --git a/src/opbox/common/OpRegistry.cpp b/src/opbox/common/OpRegistry.cpp index 1e13467..483d64a 100644 --- a/src/opbox/common/OpRegistry.cpp +++ b/src/opbox/common/OpRegistry.cpp @@ -162,26 +162,26 @@ Op * OpRegistry::CreateOp(unsigned int op_id){ /** - * @brief Process a request from Webhook to get status information + * @brief Process a request from Whookie to get status information * * @param[in] args The map of k/v parameters the user sent in this request * @param[in] results The stringstream to write results to */ -void OpRegistry::HandleWebhookStatus( +void OpRegistry::HandleWhookieStatus( const std::map &args, std::stringstream &results) { faodel::ReplyStream rs(args, "OpBox OpRegistry Status", &results); - webhookInfo(rs); + whookieInfo(rs); rs.Finish(); } /** - * @brief Append information about the reqistry to a webhook replystream + * @brief Append information about the reqistry to a whookie replystream * * @param[in] rs The replystream that output should be written to */ -void OpRegistry::webhookInfo(faodel::ReplyStream &rs) { +void OpRegistry::whookieInfo(faodel::ReplyStream &rs) { rs.tableBegin("OpRegistry"); rs.tableTop({"Parameter","Setting"}); diff --git a/src/opbox/common/OpRegistry.hh b/src/opbox/common/OpRegistry.hh index ff4506f..fc866f9 100644 --- a/src/opbox/common/OpRegistry.hh +++ b/src/opbox/common/OpRegistry.hh @@ -7,8 +7,8 @@ #include "faodel-common/Common.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "opbox/ops/Op.hh" #include "opbox/common/Types.hh" @@ -45,8 +45,8 @@ public: Op * CreateOp(unsigned int op_id); - void HandleWebhookStatus(const std::map &args, std::stringstream &results); - void webhookInfo(faodel::ReplyStream &rs); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); + void whookieInfo(faodel::ReplyStream &rs); //InfoInterface void sstr(std::stringstream &ss, int depth=0, int indent=0) const override; diff --git a/src/opbox/core/OpBoxCoreStandard.cpp b/src/opbox/core/OpBoxCoreStandard.cpp index 6418887..3e2a992 100644 --- a/src/opbox/core/OpBoxCoreStandard.cpp +++ b/src/opbox/core/OpBoxCoreStandard.cpp @@ -9,14 +9,14 @@ #include "faodel-common/Debug.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "opbox/OpBox.hh" #include "opbox/core/OpBoxCoreStandard.hh" #include "opbox/core/OpBoxCoreUnconfigured.hh" -#include "opbox/core/Singleton.hh" //for registry webhook +#include "opbox/core/Singleton.hh" //for registry whookie using namespace std; @@ -76,8 +76,8 @@ void OpBoxCoreStandard::init(const faodel::Configuration &config) { dbg("Done with opbox::net::Init()"); opbox::net::RegisterRecvCallback(opbox::internal::HandleIncomingMessage); - webhook::Server::updateHook("/opbox", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/opbox", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); //Start thread @@ -106,7 +106,7 @@ void OpBoxCoreStandard::finish() { dbg("private finish"); kassert(initialized && running, "Attempted to finish OpBoxCoreStandard that is not started"); - webhook::Server::deregisterHook("/opbox"); + whookie::Server::deregisterHook("/opbox"); opbox::net::Finish(); @@ -354,12 +354,12 @@ void OpBoxCoreStandard::endActiveOp(mailbox_t mailbox){ /** - * @brief HandleWebhookStatus Process a request from Webhook to get status information + * @brief HandleWhookieStatus Process a request from Whookie to get status information * * @param[in] args The map of k/v parameters the user sent in this request * @param[in] results The stringstream to write results to */ -void OpBoxCoreStandard::HandleWebhookStatus( +void OpBoxCoreStandard::HandleWhookieStatus( const std::map &args, std::stringstream &results) { @@ -368,7 +368,7 @@ void OpBoxCoreStandard::HandleWebhookStatus( vector> stats; stats.push_back(pair("Core Type", GetType())); rs.mkTable(stats, "OpBox Status"); - opbox::internal::Singleton::impl.webhookInfoRegistry(rs); + opbox::internal::Singleton::impl.whookieInfoRegistry(rs); rs.Finish(); } diff --git a/src/opbox/core/OpBoxCoreStandard.hh b/src/opbox/core/OpBoxCoreStandard.hh index c61f948..0aacb95 100644 --- a/src/opbox/core/OpBoxCoreStandard.hh +++ b/src/opbox/core/OpBoxCoreStandard.hh @@ -48,7 +48,7 @@ public: int HandleIncomingMessage(opbox::net::peer_ptr_t peer, message_t *incoming_message) override; int UpdateOp(Op *op, OpArgs *args) override; - void HandleWebhookStatus(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); std::string GetType() const override { return "standard"; } diff --git a/src/opbox/core/OpBoxCoreThreaded.cpp b/src/opbox/core/OpBoxCoreThreaded.cpp index dc77868..368edef 100644 --- a/src/opbox/core/OpBoxCoreThreaded.cpp +++ b/src/opbox/core/OpBoxCoreThreaded.cpp @@ -10,14 +10,14 @@ #include "faodel-common/Debug.hh" #include "faodel-services/BackBurner.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "opbox/OpBox.hh" #include "opbox/core/OpBoxCoreThreaded.hh" //#include "opbox/core/OpBoxCoreUnconfigured.hh" -#include "opbox/core/Singleton.hh" //for registry webhook +#include "opbox/core/Singleton.hh" //for registry whookie using namespace std; @@ -77,11 +77,11 @@ void OpBoxCoreThreaded::init(const faodel::Configuration &config) { dbg("Done with opbox::net::Init()"); opbox::net::RegisterRecvCallback(opbox::internal::HandleIncomingMessage); - webhook::Server::updateHook("/opbox", [this] (const map &args, stringstream &results) { - return HandleWebhookStatus(args, results); + whookie::Server::updateHook("/opbox", [this] (const map &args, stringstream &results) { + return HandleWhookieStatus(args, results); }); - webhook::Server::updateHook("/opbox/ops", [this] (const map &args, stringstream &results) { - return HandleWebhookActiveOps(args, results); + whookie::Server::updateHook("/opbox/ops", [this] (const map &args, stringstream &results) { + return HandleWhookieActiveOps(args, results); }); @@ -111,8 +111,8 @@ void OpBoxCoreThreaded::finish() { dbg("private finish"); kassert(initialized && running, "Attempted to finish OpBoxCoreThreaded that is not started"); - webhook::Server::deregisterHook("/opbox"); - webhook::Server::deregisterHook("/opbox/ops"); + whookie::Server::deregisterHook("/opbox"); + whookie::Server::deregisterHook("/opbox/ops"); opbox::net::Finish(); @@ -204,10 +204,17 @@ int OpBoxCoreThreaded::HandleIncomingMessage(opbox::net::peer_ptr_t peer, messag //See if this is an unexpected message if(my_mailbox == 0){ - dbg("Creating new TargetOp"); + dbg("Creating new TargetOp. OpID is "+std::to_string(incoming_message->op_id)); + //New communication. Spin up a new op to andle it. op = opbox::internal::CreateNewTargetOp(incoming_message->op_id); + + if(op==nullptr) { + KHALT("Incoming message asked for an opid that was not known"); + } + + addActiveOp(op); //Sets the mailbox if needed my_mailbox = op->GetAssignedMailbox(); } else { @@ -446,12 +453,12 @@ void OpBoxCoreThreaded::endActiveOp(mailbox_t mailbox){ /** - * @brief HandleWebhookStatus Process a request from Webhook to get status information + * @brief HandleWhookieStatus Process a request from Whookie to get status information * * @param[in] args The map of k/v parameters the user sent in this request * @param[in] results The stringstream to write results to */ -void OpBoxCoreThreaded::HandleWebhookStatus( +void OpBoxCoreThreaded::HandleWhookieStatus( const std::map &args, std::stringstream &results) { @@ -467,17 +474,17 @@ void OpBoxCoreThreaded::HandleWebhookStatus( rs.mkText(html::mkLink("Current Active Ops", "/opbox/ops")); - opbox::internal::Singleton::impl.webhookInfoRegistry(rs); + opbox::internal::Singleton::impl.whookieInfoRegistry(rs); rs.Finish(); } /** - * @brief HandleWebhookActiveOps Process a request from Webhook to get Active Op information + * @brief HandleWhookieActiveOps Process a request from Whookie to get Active Op information * * @param[in] args The map of k/v parameters the user sent in this request * @param[in] results The stringstream to write results to */ -void OpBoxCoreThreaded::HandleWebhookActiveOps( +void OpBoxCoreThreaded::HandleWhookieActiveOps( const std::map &args, std::stringstream &results) { diff --git a/src/opbox/core/OpBoxCoreThreaded.hh b/src/opbox/core/OpBoxCoreThreaded.hh index 629575b..032034d 100644 --- a/src/opbox/core/OpBoxCoreThreaded.hh +++ b/src/opbox/core/OpBoxCoreThreaded.hh @@ -49,8 +49,8 @@ public: - void HandleWebhookStatus(const std::map &args, std::stringstream &results); - void HandleWebhookActiveOps(const std::map &args, std::stringstream &results); + void HandleWhookieStatus(const std::map &args, std::stringstream &results); + void HandleWhookieActiveOps(const std::map &args, std::stringstream &results); std::string GetType() const override { return "threaded"; } //InfoInterface diff --git a/src/opbox/core/OpBoxCoreUnconfigured.cpp b/src/opbox/core/OpBoxCoreUnconfigured.cpp index 0da1b0d..6e8a342 100644 --- a/src/opbox/core/OpBoxCoreUnconfigured.cpp +++ b/src/opbox/core/OpBoxCoreUnconfigured.cpp @@ -9,8 +9,8 @@ #include "faodel-common/NodeID.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" using namespace std; diff --git a/src/opbox/core/Singleton.cpp b/src/opbox/core/Singleton.cpp index db406bc..9607d13 100644 --- a/src/opbox/core/Singleton.cpp +++ b/src/opbox/core/Singleton.cpp @@ -11,8 +11,8 @@ #include "opbox/core/OpBoxCoreThreaded.hh" -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" @@ -56,7 +56,7 @@ void SingletonImpl::GetBootstrapDependencies( vector &optional) const { name = "opbox"; requires = {"backburner", "lunasa"}; - optional = {"webhook"}; + optional = {"whookie"}; } @@ -89,9 +89,9 @@ void SingletonImpl::Init(const faodel::Configuration &config){ //Unconfigured can exist before bootstrap, so we need to make sure we don't - //register any hooks in unconfigured before webhook has been Init'd. - webhook::Server::updateHook("/opbox/opregistry", [this] (const map &args, stringstream &results) { - return registry.HandleWebhookStatus(args, results); + //register any hooks in unconfigured before whookie has been Init'd. + whookie::Server::updateHook("/opbox/opregistry", [this] (const map &args, stringstream &results) { + return registry.HandleWhookieStatus(args, results); }); core->init(config); diff --git a/src/opbox/core/Singleton.hh b/src/opbox/core/Singleton.hh index a9f8183..f9dc66c 100644 --- a/src/opbox/core/Singleton.hh +++ b/src/opbox/core/Singleton.hh @@ -45,7 +45,7 @@ public: std::vector &requires, std::vector &optional) const override; - void webhookInfoRegistry(faodel::ReplyStream &rs) { return registry.webhookInfo(rs); } + void whookieInfoRegistry(faodel::ReplyStream &rs) { return registry.whookieInfo(rs); } OpRegistry registry; OpBoxCoreBase *core; diff --git a/src/opbox/net/libfabric_wrapper/fab_transport.cpp b/src/opbox/net/libfabric_wrapper/fab_transport.cpp index 226fcfc..fedce0a 100644 --- a/src/opbox/net/libfabric_wrapper/fab_transport.cpp +++ b/src/opbox/net/libfabric_wrapper/fab_transport.cpp @@ -11,8 +11,8 @@ #include "opbox/net/net.hh" #include "opbox/net/peer.hh" -#include "webhook/Server.hh" -#include "webhook/client/Client.hh" +#include "whookie/Server.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" #include @@ -581,11 +581,11 @@ fab_transport::create_rdm_connection_server( struct fi_info *my_fi; string hostname, port, rem_name, rem_port; - auto new_val = args.find("rem_webhook_hostname"); + auto new_val = args.find("rem_whookie_hostname"); if(new_val != args.end()){ hostname = new_val->second; } - new_val = args.find("rem_webhook_port"); + new_val = args.find("rem_whookie_port"); if(new_val != args.end()){ port = new_val->second; } @@ -668,17 +668,17 @@ fab_transport::fab_init_rdm(const char *provider_name) hints->fabric_attr->prov_name = strdup(provider_name); hints->domain_attr->mr_mode = FI_MR_BASIC; - //src_port = webhook::port(); - // have a global port relative to webhook port , which is unique to start libfabric as server connection - // There is really no connection but this is to indetify the src_addr, dest_addr etc for webhook exchange + //src_port = whookie::port(); + // have a global port relative to whookie port , which is unique to start libfabric as server connection + // There is really no connection but this is to indetify the src_addr, dest_addr etc for whookie exchange // - faodel::nodeid_t nid = webhook::Server::GetNodeID();; + faodel::nodeid_t nid = whookie::Server::GetNodeID();; string s_host, s_port; uint32_t b_host; uint16_t b_port; nid.GetIPPort(&s_host, &s_port); nid.GetIPPort(&b_host, &b_port); -// cout << "webhook " << s_host<<" / "< &args, stringstream &results) { + whookie::Server::registerHook("/fab/rdmlookup", [this] (const map &args, stringstream &results) { create_rdm_connection_server(args, results); }); - webhook::Server::registerHook("/fab/disconnect", [this] (const map &args, stringstream &results) { + whookie::Server::registerHook("/fab/disconnect", [this] (const map &args, stringstream &results) { destroy_rdm_connection_server(args, results); }); //cout << "Fab init RDM done " << std::endl; @@ -760,8 +760,8 @@ fab_transport::create_rdm_connection_client( // cout << "outbound connection to " << peer_nodeid.GetHex() << endl; - ss_path << "/fab/rdmlookup&rem_webhook_hostname="<fabric_attr->prov_name = strdup(provider_name); hints->domain_attr->mr_mode = FI_MR_BASIC; - faodel::nodeid_t nid = webhook::Server::GetNodeID();; + faodel::nodeid_t nid = whookie::Server::GetNodeID();; string s_host, s_port; uint32_t b_host; uint16_t b_port; nid.GetIPPort(&s_host, &s_port); nid.GetIPPort(&b_host, &b_port); - //cout << "webhook " << s_host<<" / "< &args, stringstream &results) { + // Register for whookies to lookup my IB Ip address and port, where fabric connection mgmt running + whookie::Server::registerHook("/fab/iblookup", [this] (const map &args, stringstream &results) { struct fab_connection *conn=create_ib_pending_connection(args); std::string my_port = to_string(my_fab_port); string s_host, s_port; @@ -1063,11 +1063,11 @@ fab_transport::create_ib_pending_connection ( char* addr; uint16_t lport; struct fab_connection* conn = new fab_connection; - auto new_val = args.find("rem_webhook_hostname"); + auto new_val = args.find("rem_whookie_hostname"); if(new_val != args.end()){ hostname = new_val->second; } - new_val = args.find("rem_webhook_port"); + new_val = args.find("rem_whookie_port"); if(new_val != args.end()){ port = new_val->second; } @@ -1183,17 +1183,17 @@ fab_transport::client_connect_ib ( (uint16_t) ntohs (((struct sockaddr_in *) fi->src_addr)->sin_port); // cout << "client_ib_connect() - My addr:" << src_addr << " conn port " << src_port << " addrlen " << fi->src_addrlen <second; } - new_val = args.find("rem_webhook_port"); + new_val = args.find("rem_whookie_port"); if(new_val != args.end()){ port = new_val->second; } diff --git a/src/opbox/net/libfabric_wrapper/libfabric_wrapper.cpp b/src/opbox/net/libfabric_wrapper/libfabric_wrapper.cpp index 0ca9de2..3cb28a9 100644 --- a/src/opbox/net/libfabric_wrapper/libfabric_wrapper.cpp +++ b/src/opbox/net/libfabric_wrapper/libfabric_wrapper.cpp @@ -11,8 +11,8 @@ #include "opbox/net/net.hh" #include "opbox/net/peer.hh" -#include "webhook/Server.hh" -#include "webhook/client/Client.hh" +#include "whookie/Server.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" #include "opbox/net/libfabric_wrapper/fab_transport.hh" @@ -76,7 +76,7 @@ using namespace std; using namespace opbox; using namespace faodel; using namespace lunasa; -using namespace webhook; +using namespace whookie; namespace opbox { namespace net { @@ -256,8 +256,8 @@ Start() std::cerr << "NetLibfabric -> Provider name (verbs/sockets/gni) not specified. Defaulting to 'sockets'." << std::endl;; trans_name = "sockets"; } - assert(webhook::Server::IsRunning() && "Webhook not started before fabric started"); - fabtrns->mynodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before fabric started"); + fabtrns->mynodeid = whookie::Server::GetNodeID(); // cout << "mynodeid = " << fabtrns->mynodeid.GetHex() << endl; if((trans_name == "gni") || (trans_name == "ugni")) { fabtrns->my_transport_id = 2; diff --git a/src/opbox/net/nnti/NetNnti.cpp b/src/opbox/net/nnti/NetNnti.cpp index d77d145..2080aec 100644 --- a/src/opbox/net/nnti/NetNnti.cpp +++ b/src/opbox/net/nnti/NetNnti.cpp @@ -27,7 +27,7 @@ #include "faodel-common/Configuration.hh" #include "faodel-common/NodeID.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" #include "lunasa/DataObject.hh" @@ -288,7 +288,7 @@ namespace NetNnti } } } -}; +} namespace NetNnti { @@ -334,7 +334,7 @@ namespace NetNnti t_->register_memory( (char*)base_addr, length, - static_cast(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), + static_cast(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE|NNTI_BF_REMOTE_ATOMIC), NNTI_INVALID_HANDLE, *cb, nullptr, @@ -431,7 +431,7 @@ namespace NetNnti return NNTI_OK; } }; -}; +} using namespace NetNnti; @@ -464,9 +464,9 @@ void Start() { NNTI_result_t rc; - //Webhook should be started by this point. We can get its info now. - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - myid_ = webhook::Server::GetNodeID(); + //Whookie should be started by this point. We can get its info now. + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + myid_ = whookie::Server::GetNodeID(); t_ = nnti::transports::factory::get_instance(config_); diff --git a/src/sbl/README_SBL.md b/src/sbl/README_SBL.md index 4eec0ee..35aa489 100644 --- a/src/sbl/README_SBL.md +++ b/src/sbl/README_SBL.md @@ -213,7 +213,7 @@ Example: sbl::stream file_stream("debug.log", sbl::severity_level::debug); // all messages go to this file std::stringstream ss; sbl::stream buffer_stream(ss, sbl::severity_level::warning); // warning, error and fatal messages go to a stringstream - // that webhook can put in replies + // that whookie can put in replies sbl::source debug_source(sbl::severity_level::debug); // these will only go to the file_stream sbl::source fatal_source(sbl::severity_level::fatal); // these will go to all streams diff --git a/src/sbl/sbl_boost_headers.hh b/src/sbl/sbl_boost_headers.hh index 018dacc..8d0a8dc 100644 --- a/src/sbl/sbl_boost_headers.hh +++ b/src/sbl/sbl_boost_headers.hh @@ -3,8 +3,8 @@ // the U.S. Government retains certain rights in this software. -#ifndef SBL_BOOST_HEADERS_HPP_ -#define SBL_BOOST_HEADERS_HPP_ +#ifndef SBL_BOOST_HEADERS_HH_ +#define SBL_BOOST_HEADERS_HH_ #include #include @@ -18,4 +18,4 @@ #include -#endif /* SBL_BOOST_HEADERS_HPP_ */ +#endif /* SBL_BOOST_HEADERS_HH_ */ diff --git a/src/sbl/sbl_logger.hh b/src/sbl/sbl_logger.hh index b4bc9e2..a0951a7 100644 --- a/src/sbl/sbl_logger.hh +++ b/src/sbl/sbl_logger.hh @@ -3,14 +3,14 @@ // the U.S. Government retains certain rights in this software. /* - * sbl_logger.hpp + * sbl_logger.hh * * Created on: Jul 13, 2015 * Author: thkorde */ -#ifndef SBL_LOGGER_HPP_ -#define SBL_LOGGER_HPP_ +#ifndef SBL_LOGGER_HH_ +#define SBL_LOGGER_HH_ #include "faodelConfig.h" #include "sbl/sbl_boost_headers.hh" @@ -156,4 +156,4 @@ private: } /* namespace sbl */ -#endif /* SBL_LOGGER_HPP_ */ +#endif /* SBL_LOGGER_HH_ */ diff --git a/src/sbl/sbl_source.cpp b/src/sbl/sbl_source.cpp index e5150c0..41c20ea 100644 --- a/src/sbl/sbl_source.cpp +++ b/src/sbl/sbl_source.cpp @@ -21,7 +21,8 @@ namespace sbl { severity_level severity) : logger_id_(0), logger_id_attr_(logger_id_), - severity_(severity) + severity_(severity), + disabled(false) { boostlogger_.add_attribute("LoggerID", logger_id_attr_); } @@ -30,7 +31,8 @@ namespace sbl { severity_level severity) : logger_id_(logger_id), logger_id_attr_(logger_id_), - severity_(severity) + severity_(severity), + disabled(false) { boostlogger_.add_attribute("LoggerID", logger_id_attr_); } @@ -160,11 +162,29 @@ namespace sbl { } /* private methods */ + void source::disable(void) + { + disabled = true; + std::cerr << \ + "Boost.Log threw a fatal exception - SBL logging disabled to avoid a segfault.\n" + "This usually happens when an app exits without shutting down Faodel.\n" + "Try adding a call to 'faodel::bootstrap::Finish()' at the end of main() and \n" + "before MPI_Finalize() in an MPI app.\n"; + } + void source::output( const char *channel, const char *msg) { - BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << msg; + if (disabled) { + return; + } + try { + BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << msg; + } + catch (std::exception &e) { + disable(); + } return; } @@ -174,7 +194,15 @@ namespace sbl { const char *prefix, const char *msg) { - BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << prefix << msg; + if (disabled) { + return; + } + try { + BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << prefix << msg; + } + catch (std::exception &e) { + disable(); + } return; } @@ -186,9 +214,18 @@ namespace sbl { { char buf[256]; + if (disabled) { + return; + } + vsprintf(buf, msg, params); - BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << buf; + try { + BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << buf; + } + catch (std::exception &e) { + disable(); + } return; } @@ -201,11 +238,19 @@ namespace sbl { { char buf[256]; + if (disabled) { + return; + } + vsprintf(buf, msg, params); - BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << prefix << buf; + try { + BOOST_LOG_CHANNEL_SEV(boostlogger_, channel, severity_) << prefix << buf; + } + catch (std::exception &e) { + disable(); + } return; } - } diff --git a/src/sbl/sbl_source.hh b/src/sbl/sbl_source.hh index 9c4f685..c11ae5e 100644 --- a/src/sbl/sbl_source.hh +++ b/src/sbl/sbl_source.hh @@ -3,14 +3,14 @@ // the U.S. Government retains certain rights in this software. /* - * sbl_logger.hpp + * sbl_source.hh * * Created on: Jul 13, 2015 * Author: thkorde */ -#ifndef SBL_SOURCE_HPP_ -#define SBL_SOURCE_HPP_ +#ifndef SBL_SOURCE_HH_ +#define SBL_SOURCE_HH_ #include "faodelConfig.h" #include "sbl/sbl_boost_headers.hh" @@ -99,6 +99,7 @@ public: va_list params); private: + void disable(void); void output( const char *channel, const char *msg); @@ -122,8 +123,10 @@ private: severity_level severity_; sbl_logger boostlogger_; + + bool disabled; }; } /* namespace sbl */ -#endif /* SBL_SOURCE_HPP_ */ +#endif /* SBL_SOURCE_HH_ */ diff --git a/src/sbl/sbl_stream.hh b/src/sbl/sbl_stream.hh index ec2819b..4e06caa 100644 --- a/src/sbl/sbl_stream.hh +++ b/src/sbl/sbl_stream.hh @@ -3,14 +3,14 @@ // the U.S. Government retains certain rights in this software. /* - * sbl_logger.hpp + * sbl_stream.hh * * Created on: Jul 13, 2015 * Author: thkorde */ -#ifndef SBL_STREAM_HPP_ -#define SBL_STREAM_HPP_ +#ifndef SBL_STREAM_HH_ +#define SBL_STREAM_HH_ #include "faodelConfig.h" #include "sbl/sbl_boost_headers.hh" @@ -91,4 +91,4 @@ private: } /* namespace sbl */ -#endif /* SBL_STREAM_HPP_ */ +#endif /* SBL_STREAM_HH_ */ diff --git a/src/sbl/sbl_types.hh b/src/sbl/sbl_types.hh index 986ddd9..4ca2e5f 100644 --- a/src/sbl/sbl_types.hh +++ b/src/sbl/sbl_types.hh @@ -3,14 +3,14 @@ // the U.S. Government retains certain rights in this software. /* - * sbl_logger.hpp + * sbl_types.hh * * Created on: Jul 13, 2015 * Author: thkorde */ -#ifndef SBL_TYPES_HPP_ -#define SBL_TYPES_HPP_ +#ifndef SBL_TYPES_HH_ +#define SBL_TYPES_HH_ namespace sbl { @@ -25,4 +25,4 @@ enum severity_level } /* namespace sbl */ -#endif /* SBL_TYPES_HPP_ */ +#endif /* SBL_TYPES_HH_ */ diff --git a/src/whookie/CMakeLists.txt b/src/whookie/CMakeLists.txt index 3ed61ab..956e82a 100644 --- a/src/whookie/CMakeLists.txt +++ b/src/whookie/CMakeLists.txt @@ -1,6 +1,6 @@ set(HEADERS - WebHook.hh + Whookie.hh Server.hh client/Client.hh @@ -15,7 +15,7 @@ set(HEADERS server/boost/server.hpp ) set(HEADERS_PUBLIC - WebHook.hh + Whookie.hh Server.hh client/Client.hh ) @@ -32,12 +32,12 @@ set(SOURCES server/ServerBoost.cpp ) -# Everything now goes in webhook -add_library( webhook ${HEADERS} ${SOURCES} ) -target_link_libraries( webhook Boost::boost ) +# Everything now goes in whookie +add_library( whookie ${HEADERS} ${SOURCES} ) +target_link_libraries( whookie Boost::boost ) -install(TARGETS webhook +install(TARGETS whookie EXPORT FaodelTargets RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin ARCHIVE DESTINATION "${LIBRARY_INSTALL_DIR}" COMPONENT lib diff --git a/src/whookie/README_Whookie.md b/src/whookie/README_Whookie.md index dc8e477..57502c0 100644 --- a/src/whookie/README_Whookie.md +++ b/src/whookie/README_Whookie.md @@ -1,52 +1,52 @@ -WebHook: A Library for Remote Debug and Control +Whookie: A Library for Remote Debug and Control =============================================== -WebHook is a library that provides a simple, web-based API for +Whookie is a library that provides a simple, web-based API for interacting with a running applications. Developers register _hooks_ -with WebHook that respond to web requests. Whenever the application +with Whookie that respond to web requests. Whenever the application receives an http request for a particular hook, it passes the hook a list of URL arguments that were supplied in the request and expects the hook to generate an html response that is shipped back to the -requester. WebHook is used in FAODEL for debugging purposes +requester. Whookie is used in FAODEL for debugging purposes (eg getting status information about the rank) as well as simple communication (eg, passing RDMA keys between a pair of nodes during connect in OpBox). -**Note:** The bulk of the networking code for WebHook comes from an +**Note:** The bulk of the networking code for Whookie comes from an example provided with Boost. Please see TPL License Information below for more details. Bootstrap and Initialization ---------------------------- -WebHook has code that will automatically register the service with +Whookie has code that will automatically register the service with Bootstrap to simplify configuring and starting it. In order to start -the WebHook service a user must issue a boostrap::Init() with a proper +the Whookie service a user must issue a boostrap::Init() with a proper Configuration. Networks, Ports, and nodeid_t ----------------------------- A nodeid for an application is built from the node's IP address and -the TCP socket that WebHook uses for incoming connections. Since most +the TCP socket that Whookie uses for incoming connections. Since most HPC systems have multiple NICs, it is often necessary to specify which NIC should be used for communication. A user may pass address -information in via Configuration by either setting the webhook.address +information in via Configuration by either setting the whookie.address to a specific value (generally tedious), or by supplying a comma-separated list of nic names (eg "ib0,eth0,lo") to query for an address. -A user may specify the network port WebHook should listen to by -setting the webhook.port value in Configuration. If another +A user may specify the network port Whookie should listen to by +setting the whookie.port value in Configuration. If another application is currently using the port at the node (eg, when two -webhook applications start on the same node and use the default port -number), WebHook will increment the port number until it finds an +whookie applications start on the same node and use the default port +number), Whookie will increment the port number until it finds an unused value. As such, it should be expected that a service will not always have the port number that was originally supplied in the Configuration. The FAODEL nodeid_t is a simple data type that contains both the IP -address and port value for the WebHook server. Once WebHook is +address and port value for the Whookie server. Once Whookie is initialized, a user may query it to determine what the nodeid_t is for this instance. Often, nodes need to exchange nodeid_t values through out-of-band means (eg mpi broadcast or the opbox dirman service) in @@ -55,17 +55,17 @@ order to connect with each other. Server: Registering a Hook -------------------------- A developer can create a new hook simply by registering a lambda for -the hook with WebHook. The simplest example is: +the hook with Whookie. The simplest example is: ``` -webhook::Server::registerHook("/bob", [] (const map &args, stringstream &results) { +whookie::Server::registerHook("/bob", [] (const map &args, stringstream &results) { html::mkHeader(results, "Bob's Page"); html::mkTable(results, args, "Bobs args"); html::mkFooter(results); }); ``` -WebHook passes a map of arguments that were embedded in +Whookie passes a map of arguments that were embedded in the http request (eg /bob&item1=apple&item2=orange would have the k/v pairs item1/apple and item2/orange). Results are passed back through a stringstream that a user appends with data. @@ -82,12 +82,12 @@ Server: HTML Helpers Hooks that are intended to provide information back to users need html formatting in order to render properly in a browser. While users are free to generate responses themselves or with TPL HTML libraries, -WebHook provides a few simple classes for generating html-marked up +Whookie provides a few simple classes for generating html-marked up data. These commands append formatting to a stingstream the hook is going to pass back as a result (eg, namespace html contains mkHeader, mkTable, and mkFooter). -WebHook also provides a class named **ReplyStream** that is useful for +Whookie also provides a class named **ReplyStream** that is useful for formatting tables of information or standard blocks of text. Client: General Use @@ -97,15 +97,15 @@ server. A client can request data as follows: ``` string data; -webhook::retrieveData(host, port, path, &data); +whookie::retrieveData(host, port, path, &data); ``` Issues ====== While generally stable, we have observed the following issues with -WebHook in some instances: +Whookie in some instances: -- **Multi-Rank on XC40:** When running multiple WebHook-equipped +- **Multi-Rank on XC40:** When running multiple Whookie-equipped applications on a single node in the Cray XC40, there are asynchronous i/o problems that cause crashes. Until this is fixed we recommend running one rank per node. @@ -118,7 +118,7 @@ WebHook in some instances: - **Single Implementation:** The current version only provides a single backend for the server (Boost). Our intention is to implement alternate backends to provide other users with other options. -- **Name:** "WebHook" is a name that is widely used for other things. We +- **Name:** "Whookie" is a name that is widely used for other things. We will rename this component in the next release. @@ -128,7 +128,7 @@ Build and Configuration Settings Build Dependencies ------------------ -WebHook has the following build dependencies: +Whookie has the following build dependencies: | Dependency | Information | | -------------- | ----------------------------------- | @@ -140,20 +140,21 @@ WebHook has the following build dependencies: Compile-Time Options -------------------- -WebHook does not currently implement any flags at compile time. +Whookie does not currently implement any flags at compile time. Run-Time Options ---------------- -WebHook examines Configuration for the following flags when it is +Whookie examines Configuration for the following flags when it is initialized: -| Property | Type | Default | Description | -| ------------------ | ----------- | ------- | -------------------------------------------- | -| webhook.debug | boolean | false | Display debug messages | -| webhook.interfaces | string list | eth0,lo | Order of interfaces to get IP from | -| webhook.address | string | 0.0.0.0 | The IP address the app would like to bind to | -| webhook.port | integer | 1990 | The desired port to use | +| Property | Type | Default | Description | +| ------------------ | ----------- | ------------------- | -------------------------------------------- | +| whookie.debug | boolean | false | Display debug messages | +| whookie.interfaces | string list | eth0,lo | Order of interfaces to get IP from | +| whookie.app_name | string | Whookie Application | Name of the application to display up front | +| whookie.address | string | 0.0.0.0 | The IP address the app would like to bind to | +| whookie.port | integer | 1990 | The desired port to use | TPL License Information diff --git a/src/whookie/Server.hh b/src/whookie/Server.hh index ffa57dc..37d35ae 100644 --- a/src/whookie/Server.hh +++ b/src/whookie/Server.hh @@ -2,8 +2,8 @@ // LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. -#ifndef WEBHOOK_SERVER_HH -#define WEBHOOK_SERVER_HH +#ifndef WHOOKIE_SERVER_HH +#define WHOOKIE_SERVER_HH #include #include @@ -12,7 +12,7 @@ #include "faodel-common/Common.hh" -namespace webhook { +namespace whookie { std::string bootstrap(); //Function handed to bootstrap for dependency injection @@ -25,7 +25,7 @@ class ServerImpl; //Forward reference for server implementation /** - * @brief A Webhook server that maintains hooks. + * @brief A Whookie server that maintains hooks. */ class Server { @@ -33,6 +33,7 @@ public: Server(); ~Server(); + static int updateAppName(std::string app_name); static int registerHook(std::string name, cb_web_handler_t func); static int updateHook(std::string name, cb_web_handler_t func); static int deregisterHook(std::string name); @@ -49,6 +50,6 @@ public: -} // namespace webhook +} // namespace whookie -#endif // WEBHOOK_SERVER_HH +#endif // WHOOKIE_SERVER_HH diff --git a/src/whookie/Whookie.hh b/src/whookie/Whookie.hh index 9924357..fcd0e68 100644 --- a/src/whookie/Whookie.hh +++ b/src/whookie/Whookie.hh @@ -2,12 +2,12 @@ // LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. -#ifndef WEBHOOK_WEBHOOK_HH -#define WEBHOOK_WEBHOOK_HH +#ifndef WHOOKIE_WHOOKIE_HH +#define WHOOKIE_WHOOKIE_HH -#include "webhook/client/Client.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" #include "faodel-common/ReplyStream.hh" -#endif // WEBHOOK_WEBHOOK_HH +#endif // WHOOKIE_WHOOKIE_HH diff --git a/src/whookie/client/Client.cpp b/src/whookie/client/Client.cpp index 26903d4..324f9d1 100644 --- a/src/whookie/client/Client.cpp +++ b/src/whookie/client/Client.cpp @@ -9,12 +9,12 @@ #include #include -#include "webhook/client/Client.hh" +#include "whookie/client/Client.hh" using namespace std; using boost::asio::ip::tcp; -namespace webhook { +namespace whookie { /** * @brief Connect to a destination and request data @@ -141,4 +141,4 @@ int retrieveData(const std::string &server, unsigned int port, const string &pat -} // namespace webhook +} // namespace whookie diff --git a/src/whookie/client/Client.hh b/src/whookie/client/Client.hh index b2ce6dd..3c4d085 100644 --- a/src/whookie/client/Client.hh +++ b/src/whookie/client/Client.hh @@ -2,14 +2,14 @@ // LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. -#ifndef WEBHOOK_CLIENT_HH -#define WEBHOOK_CLIENT_HH +#ifndef WHOOKIE_CLIENT_HH +#define WHOOKIE_CLIENT_HH #include #include "faodel-common/Common.hh" -namespace webhook { +namespace whookie { int retrieveData(faodel::nodeid_t nid, const std::string &path, std::string *data=nullptr); int retrieveData(const std::string &server, const std::string &port, const std::string &path, std::string *data=nullptr); @@ -18,7 +18,7 @@ int retrieveData(const std::string &server, unsigned int port, const std::string -} // namespace webhook +} // namespace whookie -#endif // WEBHOOK_CLIENT_HH +#endif // WHOOKIE_CLIENT_HH diff --git a/src/whookie/server/ServerBoost.cpp b/src/whookie/server/ServerBoost.cpp index 1f443a6..36e46b5 100644 --- a/src/whookie/server/ServerBoost.cpp +++ b/src/whookie/server/ServerBoost.cpp @@ -2,18 +2,18 @@ // LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. -#include "webhook/Server.hh" -#include "webhook/server/boost/server.hpp" +#include "whookie/Server.hh" +#include "whookie/server/boost/server.hpp" using namespace std; -//Alias the boost server class to be webhook's internal server +//Alias the boost server class to be whookie's internal server //Initialize the static server implementation -//static webhook::internal::server webhook::Server::server_impl(); +//static whookie::internal::server whookie::Server::server_impl(); -namespace webhook { +namespace whookie { //The boost implementation has everything built into its server, //so just plug it in. @@ -24,14 +24,14 @@ class ServerImpl { //Initialize the static server implementation -ServerImpl webhook::Server::server_impl; +ServerImpl whookie::Server::server_impl; /** - * @brief Bootstrap function used to manually register webhook (and + * @brief Bootstrap function used to manually register whookie (and * dependencies) with bootstrap * - * @retval "webhook" + * @retval "whookie" * * @note Users pass this to bootstrap's Start/Init. Only the last * bootstap dependenciy needs to be supplied. @@ -42,9 +42,12 @@ std::string bootstrap() { //TODO: server_impl was private, couldn't get at it from here faodel::bootstrap::RegisterComponent(&Server::server_impl.boost_server, true); - return "webhook"; + return "whookie"; } +int Server::updateAppName(std::string app_name) { + return server_impl.boost_server.updateAppName(app_name); +} int Server::registerHook(string name, cb_web_handler_t func) { return server_impl.boost_server.registerHook(name, func); @@ -64,5 +67,5 @@ faodel::nodeid_t Server::GetNodeID(){ return server_impl.boost_server.GetNodeID(); } -} // namespace webhook +} // namespace whookie diff --git a/src/whookie/server/boost/connection.cpp b/src/whookie/server/boost/connection.cpp index 52f0e1e..0a675e2 100644 --- a/src/whookie/server/boost/connection.cpp +++ b/src/whookie/server/boost/connection.cpp @@ -8,11 +8,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/connection.hpp" +#include "whookie/server/boost/connection.hpp" #include #include -#include "webhook/server/boost/connection_manager.hpp" -#include "webhook/server/boost/request_handler.hpp" +#include "whookie/server/boost/connection_manager.hpp" +#include "whookie/server/boost/request_handler.hpp" namespace http { diff --git a/src/whookie/server/boost/connection.hpp b/src/whookie/server/boost/connection.hpp index e64825f..7a44936 100644 --- a/src/whookie/server/boost/connection.hpp +++ b/src/whookie/server/boost/connection.hpp @@ -8,16 +8,16 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_CONNECTION_HPP -#define WEBHOOK_CONNECTION_HPP +#ifndef WHOOKIE_CONNECTION_HPP +#define WHOOKIE_CONNECTION_HPP #include #include #include -#include "webhook/server/boost/reply.hpp" -#include "webhook/server/boost/request.hpp" -#include "webhook/server/boost/request_handler.hpp" -#include "webhook/server/boost/request_parser.hpp" +#include "whookie/server/boost/reply.hpp" +#include "whookie/server/boost/request.hpp" +#include "whookie/server/boost/request_handler.hpp" +#include "whookie/server/boost/request_parser.hpp" namespace http { namespace server { @@ -76,4 +76,4 @@ typedef std::shared_ptr connection_ptr; } // namespace server } // namespace http -#endif // WEBHOOK_CONNECTION_HPP +#endif // WHOOKIE_CONNECTION_HPP diff --git a/src/whookie/server/boost/connection_manager.cpp b/src/whookie/server/boost/connection_manager.cpp index 9802c72..2671b1c 100644 --- a/src/whookie/server/boost/connection_manager.cpp +++ b/src/whookie/server/boost/connection_manager.cpp @@ -8,7 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/connection_manager.hpp" +#include "whookie/server/boost/connection_manager.hpp" #include using namespace std; diff --git a/src/whookie/server/boost/connection_manager.hpp b/src/whookie/server/boost/connection_manager.hpp index 3989420..6169082 100644 --- a/src/whookie/server/boost/connection_manager.hpp +++ b/src/whookie/server/boost/connection_manager.hpp @@ -8,11 +8,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_CONNECTION_MANAGER_HPP -#define WEBHOOK_CONNECTION_MANAGER_HPP +#ifndef WHOOKIE_CONNECTION_MANAGER_HPP +#define WHOOKIE_CONNECTION_MANAGER_HPP #include -#include "webhook/server/boost/connection.hpp" +#include "whookie/server/boost/connection.hpp" namespace http { namespace server { @@ -45,4 +45,4 @@ class connection_manager } // namespace server } // namespace http -#endif // WEBHOOK_CONNECTION_MANAGER_HPP +#endif // WHOOKIE_CONNECTION_MANAGER_HPP diff --git a/src/whookie/server/boost/header.hpp b/src/whookie/server/boost/header.hpp index 4936a11..2081c0f 100644 --- a/src/whookie/server/boost/header.hpp +++ b/src/whookie/server/boost/header.hpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_HEADER_HPP -#define WEBHOOK_HEADER_HPP +#ifndef WHOOKIE_HEADER_HPP +#define WHOOKIE_HEADER_HPP #include @@ -25,4 +25,4 @@ struct header } // namespace server } // namespace http -#endif // WEBHOOK_HEADER_HPP +#endif // WHOOKIE_HEADER_HPP diff --git a/src/whookie/server/boost/mime_types.cpp b/src/whookie/server/boost/mime_types.cpp index a141983..91d8650 100644 --- a/src/whookie/server/boost/mime_types.cpp +++ b/src/whookie/server/boost/mime_types.cpp @@ -8,7 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/mime_types.hpp" +#include "whookie/server/boost/mime_types.hpp" namespace http { namespace server { diff --git a/src/whookie/server/boost/mime_types.hpp b/src/whookie/server/boost/mime_types.hpp index 53f230b..817c287 100644 --- a/src/whookie/server/boost/mime_types.hpp +++ b/src/whookie/server/boost/mime_types.hpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_MIME_TYPES_HPP -#define WEBHOOK_MIME_TYPES_HPP +#ifndef WHOOKIE_MIME_TYPES_HPP +#define WHOOKIE_MIME_TYPES_HPP #include @@ -24,4 +24,4 @@ std::string extension_to_type(const std::string& extension); } // namespace server } // namespace http -#endif // WEBHOOK_MIME_TYPES_HPP +#endif // WHOOKIE_MIME_TYPES_HPP diff --git a/src/whookie/server/boost/reply.cpp b/src/whookie/server/boost/reply.cpp index 31e1572..96de0c6 100644 --- a/src/whookie/server/boost/reply.cpp +++ b/src/whookie/server/boost/reply.cpp @@ -8,7 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/reply.hpp" +#include "whookie/server/boost/reply.hpp" #include namespace http { diff --git a/src/whookie/server/boost/reply.hpp b/src/whookie/server/boost/reply.hpp index 9901cec..0255427 100644 --- a/src/whookie/server/boost/reply.hpp +++ b/src/whookie/server/boost/reply.hpp @@ -8,13 +8,13 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_REPLY_HPP -#define WEBHOOK_REPLY_HPP +#ifndef WHOOKIE_REPLY_HPP +#define WHOOKIE_REPLY_HPP #include #include #include -#include "webhook/server/boost/header.hpp" +#include "whookie/server/boost/header.hpp" namespace http { namespace server { @@ -61,4 +61,4 @@ struct reply } // namespace server } // namespace http -#endif // WEBHOOK_REPLY_HPP +#endif // WHOOKIE_REPLY_HPP diff --git a/src/whookie/server/boost/request.hpp b/src/whookie/server/boost/request.hpp index e853d1b..e4a3227 100644 --- a/src/whookie/server/boost/request.hpp +++ b/src/whookie/server/boost/request.hpp @@ -8,12 +8,12 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_REQUEST_HPP -#define WEBHOOK_REQUEST_HPP +#ifndef WHOOKIE_REQUEST_HPP +#define WHOOKIE_REQUEST_HPP #include #include -#include "webhook/server/boost/header.hpp" +#include "whookie/server/boost/header.hpp" namespace http { namespace server { @@ -31,4 +31,4 @@ struct request } // namespace server } // namespace http -#endif // WEBHOOK_REQUEST_HPP +#endif // WHOOKIE_REQUEST_HPP diff --git a/src/whookie/server/boost/request_handler.cpp b/src/whookie/server/boost/request_handler.cpp index 8dc2edf..433cf8b 100644 --- a/src/whookie/server/boost/request_handler.cpp +++ b/src/whookie/server/boost/request_handler.cpp @@ -8,7 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/request_handler.hpp" +#include "whookie/server/boost/request_handler.hpp" #include #include #include @@ -17,9 +17,9 @@ #include "faodel-common/Common.hh" -#include "webhook/server/boost/mime_types.hpp" -#include "webhook/server/boost/reply.hpp" -#include "webhook/server/boost/request.hpp" +#include "whookie/server/boost/mime_types.hpp" +#include "whookie/server/boost/reply.hpp" +#include "whookie/server/boost/request.hpp" #include "faodel-common/QuickHTML.hh" @@ -35,11 +35,11 @@ request_handler::request_handler() { //Default "/" handler dumps out info about what handles are registered registerHook("/", [this] (const map &args, stringstream &results) { - dumpRegisteredHandles(results); + dumpRegisteredHandles(args, results); }); registerHook("/about", [this] (const map &args, stringstream &results) { - dumpAbout(results); + dumpAbout(args, results); }); } @@ -134,7 +134,7 @@ bool request_handler::url_decode(const string& in, string& out) { } -int request_handler::registerHook(const string &name, webhook::cb_web_handler_t func){ +int request_handler::registerHook(const string &name, whookie::cb_web_handler_t func){ int rc=0; cbs_mutex.lock(); @@ -142,14 +142,14 @@ int request_handler::registerHook(const string &name, webhook::cb_web_handler_t if(fptr == cbs.end()){ cbs[name] = func; } else { - cout <<"WebHook: RegisterHandler detected double register of function '"< &args, stringstream &results) { + + faodel::ReplyStream rs(args, app_name+" Whookie", &results); + vector links; - for(auto &name_hdlr : cbs) - links.push_back(html::mkLink(name_hdlr.first, name_hdlr.first)); - - html::mkHeader(results, "WebHooks"); - html::mkText(results,"WebHooks",1); - html::mkText(results,"The following hooks are known to this application:"); - html::mkList(results, links); - html::mkFooter(results); + for(auto &name_hdlr : cbs) { + links.push_back( rs.createLink(name_hdlr.first, name_hdlr.first)); + } + rs.mkSection(app_name,1); + rs.mkText("The following hooks are known to this application:"); + rs.mkList(links); + rs.Finish(); + } -void request_handler::dumpAbout(stringstream &results){ - html::mkHeader(results,"About WebHook"); - html::mkText(results,"About WebHook",1); - html::mkText(results,R"(WebHook is a simple add-on that allows multiple -software components in an application to share a simple network interface -for debugging and basic restful api operations.)"); - html::mkText(results, html::mkLink("See available services", "/")); - html::mkFooter(results); - +void request_handler::dumpAbout(const map &args, stringstream &results){ + + faodel::ReplyStream rs(args, "About Whookie", &results); + + rs.mkSection("About Whookie",1); + rs.mkText(R"( +Whookie is a simple service that allows multiple software components in an +application to share a network interface for debugging and basic RESTful +API kinds of operations. It is included in the FAODEL collection of +libraries.)"); + rs.Finish(); + } // note: not safe to insert hooks here? random crashes observed diff --git a/src/whookie/server/boost/request_handler.hpp b/src/whookie/server/boost/request_handler.hpp index d08d0ae..2d19960 100644 --- a/src/whookie/server/boost/request_handler.hpp +++ b/src/whookie/server/boost/request_handler.hpp @@ -8,15 +8,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_REQUEST_HANDLER_HPP -#define WEBHOOK_REQUEST_HANDLER_HPP +#ifndef WHOOKIE_REQUEST_HANDLER_HPP +#define WHOOKIE_REQUEST_HANDLER_HPP #include #include #include #include -#include "webhook/Server.hh" +#include "whookie/Server.hh" namespace http { namespace server { @@ -40,30 +40,33 @@ class request_handler void handle_request(const request& req, reply& rep); //CDU - int registerHook(const std::string &name, webhook::cb_web_handler_t func); - int updateHook(const std::string &name, webhook::cb_web_handler_t func); + int updateAppName(std::string name) { app_name=name; return 0; } + int registerHook(const std::string &name, whookie::cb_web_handler_t func); + int updateHook(const std::string &name, whookie::cb_web_handler_t func); int deregisterHook(const std::string &name); std::string makeHTMLHeader(const std::string &name); private: + std::string app_name; + /// Perform URL-decoding on a string. Returns false if the encoding was /// invalid. static bool url_decode(const std::string& in, std::string& out); - std::map cbs; + std::map cbs; std::mutex cbs_mutex; std::pair splitString(const std::string &item, char delim); std::map parseArgString(const std::string &args); - void dumpRegisteredHandles(std::stringstream &results); - void dumpAbout(std::stringstream &results); + void dumpRegisteredHandles(const std::map &args, std::stringstream &results); + void dumpAbout(const std::map &args, std::stringstream &results); }; } // namespace server } // namespace http -#endif // WEBHOOK_REQUEST_HANDLER_HPP +#endif // WHOOKIE_REQUEST_HANDLER_HPP diff --git a/src/whookie/server/boost/request_parser.cpp b/src/whookie/server/boost/request_parser.cpp index 8607e6c..a90e602 100644 --- a/src/whookie/server/boost/request_parser.cpp +++ b/src/whookie/server/boost/request_parser.cpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "webhook/server/boost/request_parser.hpp" -#include "webhook/server/boost/request.hpp" +#include "whookie/server/boost/request_parser.hpp" +#include "whookie/server/boost/request.hpp" namespace http { namespace server { diff --git a/src/whookie/server/boost/request_parser.hpp b/src/whookie/server/boost/request_parser.hpp index 4a12773..f98f8df 100644 --- a/src/whookie/server/boost/request_parser.hpp +++ b/src/whookie/server/boost/request_parser.hpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_REQUEST_PARSER_HPP -#define WEBHOOK_REQUEST_PARSER_HPP +#ifndef WHOOKIE_REQUEST_PARSER_HPP +#define WHOOKIE_REQUEST_PARSER_HPP #include @@ -93,4 +93,4 @@ class request_parser } // namespace server } // namespace http -#endif // WEBHOOK_REQUEST_PARSER_HPP +#endif // WHOOKIE_REQUEST_PARSER_HPP diff --git a/src/whookie/server/boost/server.cpp b/src/whookie/server/boost/server.cpp index b20b359..acddc85 100644 --- a/src/whookie/server/boost/server.cpp +++ b/src/whookie/server/boost/server.cpp @@ -11,8 +11,8 @@ #include "faodel-common/NodeID.hh" #include "faodel-common/Bootstrap.hh" -#include "webhook/Server.hh" -#include "webhook/server/boost/server.hpp" +#include "whookie/Server.hh" +#include "whookie/server/boost/server.hpp" #include #include @@ -28,7 +28,7 @@ namespace http { namespace server { server::server() - : LoggingInterface("webhook"), + : LoggingInterface("whookie"), configured_(false), port_(0), num_starters_(0) { @@ -53,18 +53,19 @@ void server::Init(const faodel::Configuration &config){ string address; string interface_address(""); - //Extract config info for webhook + //Extract config info for whookie ConfigureLogging(config); //Grab logging - config.GetInt(&port, "webhook.port", "1990"); - config.GetLowercaseString(&address, "webhook.address", "0.0.0.0"); - config.GetLowercaseString(&interfaces, "webhook.interfaces", "eth,lo"); + config.GetString(&app_name, "whookie.app_name", "Whookie Application"); + config.GetInt(&port, "whookie.port", "1990"); + config.GetLowercaseString(&address, "whookie.address", "0.0.0.0"); + config.GetLowercaseString(&interfaces, "whookie.interfaces", "eth,lo"); /* - * How webhook finds an address to bind to: - * 1) If the application asked for a specific address with webhook.address, then use it. + * How whookie finds an address to bind to: + * 1) If the application asked for a specific address with whookie.address, then use it. * 2) Otherwise, query for all net interfaces and search for an "up" interface that - * matches one of the prefixes in webhook.interfaces - * 3) If no match is found, use the webhook.address fallback address. + * matches one of the prefixes in whookie.interfaces + * 3) If no match is found, use the whookie.address fallback address. */ if (address == "0.0.0.0") { // search for an interface to use @@ -81,14 +82,16 @@ void server::Init(const faodel::Configuration &config){ asio_ = new asio_resources(); do_await_stop(); - webhook::Server::updateHook("/config", [this] (const map &args, stringstream &results) { - return HandleWebhookConfig(args, results); + whookie::Server::updateHook("/config", [this] (const map &args, stringstream &results) { + return HandleWhookieConfig(args, results); }); - webhook::Server::updateHook("/bootstraps", [this] (const map &args, stringstream &results) { - return HandleWebhookBootstrap(args, results); + whookie::Server::updateHook("/bootstraps", [this] (const map &args, stringstream &results) { + return HandleWhookieBootstrap(args, results); }); + whookie::Server::updateAppName(app_name); + //Start the server. This is the only service that should fire up in Init, //because we need nodeid to be valid asap. dbg("Requesting "+requested_address+":"+to_string(requested_port)); @@ -101,18 +104,19 @@ void server::Init(const faodel::Configuration &config){ -void server::HandleWebhookConfig(const map &args, stringstream &results){ - faodel::ReplyStream rs(args, "Webhook Configuration Settings", &results); +void server::HandleWhookieConfig(const map &args, stringstream &results){ + faodel::ReplyStream rs(args, "Whookie Configuration Settings", &results); - rs.tableBegin("Webhook Node Info",2); + rs.tableBegin("Whookie Node Info",2); rs.tableTop({"Parameter", "Value"}); - rs.tableRow({"Webhook Link", my_nodeid.GetHtmlLink("",my_nodeid.GetHttpLink())}); - rs.tableRow({"NodeID",my_nodeid.GetHtmlLink()}); + //rs.tableRow({"Whookie Link", my_nodeid.GetHtmlLink("",my_nodeid.GetHttpLink())}); + rs.tableRow({"Whookie Link", rs.createLink( my_nodeid.GetHttpLink(), my_nodeid.GetHttpLink(), false) }); + rs.tableRow({"NodeID", rs.createLink( my_nodeid.GetHex(), my_nodeid.GetHttpLink(), false) }); // my_nodeid.GetHtmlLink()}); rs.tableEnd(); rs.mkTable(config_entries, "User-Supplied Configuration"); - rs.mkText("Note: These are the parameters provided to bootstrap. Some values " - "(eg webhook.port) may have been adjusted due to conflicts\n"); + rs.mkText(rs.createBold("Note:")+"These are the parameters provided to bootstrap. Some values " + "(eg whookie.port) may have been adjusted due to conflicts\n"); rs.mkSection("All Application Options"); rs.mkText("Each component in this application has its own configuration settings." @@ -131,7 +135,7 @@ void server::HandleWebhookConfig(const map &args, stringstream &r rs.Finish(); } -void server::HandleWebhookBootstrap(const map &args, stringstream &results) { +void server::HandleWhookieBootstrap(const map &args, stringstream &results) { faodel::ReplyStream rs(args, "Bootstrap", &results); faodel::bootstrap::dumpInfo(rs); @@ -148,7 +152,7 @@ void server::GetBootstrapDependencies( std::string &name, std::vector &requires, std::vector &optional) const { - name = "webhook"; + name = "whookie"; requires = {}; optional = {}; } @@ -303,10 +307,10 @@ int server::stop(){ num_starters_--; if(num_starters_==0){ - //cout << "Stopping webhook on port " << port_ << endl; + //cout << "Stopping whookie on port " << port_ << endl; do_await_stop(); //Kill all the connections asio_->io_service_.stop(); //Must manually stop - //cout << "Joining webhook thread " << th_http_server_.get_id() << endl; + //cout << "Joining whookie thread " << th_http_server_.get_id() << endl; th_http_server_.join(); delete asio_; configured_=false; @@ -321,7 +325,7 @@ bool server::IsRunning(){ bool is_running; configured_mutex_.lock(); is_running = configured_; - //cout << "webhook running? " << configured_ << endl; + //cout << "whookie running? " << configured_ << endl; configured_mutex_.unlock(); return is_running; } diff --git a/src/whookie/server/boost/server.hpp b/src/whookie/server/boost/server.hpp index ea0dedb..f56da2a 100644 --- a/src/whookie/server/boost/server.hpp +++ b/src/whookie/server/boost/server.hpp @@ -8,22 +8,22 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef WEBHOOK_SERVER_HPP -#define WEBHOOK_SERVER_HPP +#ifndef WHOOKIE_SERVER_HPP +#define WHOOKIE_SERVER_HPP #include #include #include #include -#include "webhook/server/boost/connection.hpp" -#include "webhook/server/boost/connection_manager.hpp" -#include "webhook/server/boost/request_handler.hpp" +#include "whookie/server/boost/connection.hpp" +#include "whookie/server/boost/connection_manager.hpp" +#include "whookie/server/boost/request_handler.hpp" #include "faodel-common/Common.hh" #include "faodel-common/LoggingInterface.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" @@ -98,11 +98,16 @@ class server : bool IsRunning(); faodel::nodeid_t GetNodeID() { return my_nodeid; } - - int registerHook(const std::string &name, webhook::cb_web_handler_t func) { + + int updateAppName(std::string app_name_) { + app_name=app_name_; + return asio_->request_handler_.updateAppName(app_name); + } + + int registerHook(const std::string &name, whookie::cb_web_handler_t func) { return asio_->request_handler_.registerHook(name, func); } - int updateHook(const std::string &name, webhook::cb_web_handler_t func) { + int updateHook(const std::string &name, whookie::cb_web_handler_t func) { return asio_->request_handler_.updateHook(name, func); } int deregisterHook(const std::string &name) { @@ -119,13 +124,14 @@ class server : unsigned int port_; std::mutex configured_mutex_; int num_starters_; - + + std::string app_name; std::string requested_address; unsigned int requested_port; - //Provide the configuration webhook was given - void HandleWebhookConfig(const std::map &args, std::stringstream &results); - void HandleWebhookBootstrap(const std::map &args, std::stringstream &results); + //Provide the configuration whookie was given + void HandleWhookieConfig(const std::map &args, std::stringstream &results); + void HandleWhookieBootstrap(const std::map &args, std::stringstream &results); /// interate over a list of network interfaces looking for an address to use std::string search_interfaces(std::string interfaces); @@ -145,4 +151,4 @@ class server : } // namespace server } // namespace http -#endif // WEBHOOK_SERVER_HPP +#endif // WHOOKIE_SERVER_HPP diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index 2032e71..4f393dd 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -10,7 +10,7 @@ set( SERIAL_TEST_LIBS GTest::Main ) -set(webhook +set(whookie ${SERIAL_TEST_LIBS} ) # Wire in all the source directories so testing can run as if diff --git a/tests/common/unit/tb_common_resourceurl.cpp b/tests/common/unit/tb_common_resourceurl.cpp index e84dce2..cda0268 100644 --- a/tests/common/unit/tb_common_resourceurl.cpp +++ b/tests/common/unit/tb_common_resourceurl.cpp @@ -34,6 +34,7 @@ TEST_F(UrlTest, SimpleByHand) { ResourceURL l7("local:[bucket]&myoption=foo"); ResourceURL l8("/local/thing"); ResourceURL l9("/local"); + ResourceURL l10("/"); //Root reference //All ok EXPECT_TRUE(true); } catch(std::invalid_argument e) { @@ -49,7 +50,7 @@ TEST_F(UrlTest, SimpleByHand) { EXPECT_TRUE(x2.IsEmpty()); bucket_t b("this_is_my_bucket"); - EXPECT_EQ("dht", x.resource_type); + EXPECT_EQ("dht", x.Type()); EXPECT_EQ(0x2, x.reference_node.nid); EXPECT_EQ(b, x.bucket); EXPECT_EQ("/a/b", x.path); @@ -60,7 +61,7 @@ TEST_F(UrlTest, SimpleByHand) { //Make sure a copy copies all fields, and the fields aren't aliased x2=x; - EXPECT_EQ("dht", x2.resource_type); + EXPECT_EQ("dht", x2.Type()); EXPECT_EQ(0x2, x2.reference_node.nid); EXPECT_EQ(b, x2.bucket); EXPECT_EQ("/a/b", x2.path); @@ -110,21 +111,21 @@ TEST_F(UrlTest, SimpleByHand) { TEST_F(UrlTest, LocalReference) { //Make sure references are preserved - ResourceURL r1("<0x0>[bucket]/my/thing&op1=yes"); EXPECT_EQ("", r1.resource_type); - ResourceURL r2("/my/thing"); EXPECT_EQ("", r2.resource_type); - ResourceURL r3(":/bob"); EXPECT_EQ("", r3.resource_type); - ResourceURL r4("/localstuff"); EXPECT_EQ("", r4.resource_type); - ResourceURL r5("/localstuff/bob"); EXPECT_EQ("", r5.resource_type); + ResourceURL r1("<0x0>[bucket]/my/thing&op1=yes"); EXPECT_EQ("ref", r1.Type()); + ResourceURL r2("/my/thing"); EXPECT_EQ("ref", r2.Type()); + ResourceURL r3(":/bob"); EXPECT_EQ("ref", r3.Type()); + ResourceURL r4("/localstuff"); EXPECT_EQ("ref", r4.Type()); + ResourceURL r5("/localstuff/bob"); EXPECT_EQ("ref", r5.Type()); //User-provided type overrides everything - ResourceURL nl1("dht:/local/item"); EXPECT_EQ("dht", nl1.resource_type); + ResourceURL nl1("dht:/local/item"); EXPECT_EQ("dht", nl1.Type()); //Legit locals. These should all get assigned a local resource type - ResourceURL l1("local:<0x0>[bucket]/my/thing&op1=yes"); EXPECT_EQ("local",l1.resource_type); - ResourceURL l2("/local/iom1"); EXPECT_EQ("local",l2.resource_type); - ResourceURL l3("/local"); EXPECT_EQ("local",l3.resource_type); - ResourceURL l4("/local/stuff/bob"); EXPECT_EQ("local",l4.resource_type); + ResourceURL l1("local:<0x0>[bucket]/my/thing&op1=yes"); EXPECT_EQ("local",l1.Type()); + ResourceURL l2("/local/iom1"); EXPECT_EQ("local",l2.Type()); + ResourceURL l3("/local"); EXPECT_EQ("local",l3.Type()); + ResourceURL l4("/local/stuff/bob"); EXPECT_EQ("local",l4.Type()); } @@ -180,7 +181,7 @@ TEST_F(UrlTest, LocalOptions) { EXPECT_EQ("option1=foo&option2=bar", sorted_s2); ResourceURL dash1("local:/empire/mesh-thing"); - EXPECT_EQ("local", dash1.resource_type); + EXPECT_EQ("local", dash1.Type()); EXPECT_EQ("/empire", dash1.path); EXPECT_EQ("mesh-thing", dash1.name); @@ -207,23 +208,14 @@ TEST_F(UrlTest, SimpleAutomated) { check_t items[] = { { "xyz", nodeid_t(12,iuo), bucket_t(36,iuo), "/a/b/c", "thing","op1=1&op2=2", "[0x24]/a/b/c/thing", "xyz:<0xc>[0x24]/a/b/c/thing&op1=1&op2=2" }, { "dht", nodeid_t(128,iuo), bucket_t(10,iuo), "/x/y", "bob", "", "[0xa]/x/y/bob", "dht:<0x80>[0xa]/x/y/bob" }, + { "ref", nodeid_t(128,iuo), bucket_t(10,iuo), "/", "", "", "[0xa]/", "ref:<0x80>[0xa]/"}, { "end", nodeid_t(), bucket_t(),"","","","","" } }; vector< pair > entries; for(int i=0; items[i].rtype != "end"; i++) { - ResourceURL u; - u.resource_type = items[i].rtype; - u.reference_node = items[i].node; - u.bucket = items[i].bucket; - u.path = items[i].path; - u.name = items[i].name; - u.options = items[i].options; + ResourceURL u(items[i].rtype, items[i].node, items[i].bucket, items[i].path, items[i].name, items[i].options); entries.push_back( pair(i, u) ); - - - ResourceURL u2(items[i].rtype, items[i].node, items[i].bucket, items[i].path, items[i].name, items[i].options); - entries.push_back( pair(i, u2) ); } for( vector< pair >::iterator it=entries.begin(); it!=entries.end(); ++it) { @@ -321,27 +313,49 @@ TEST_F(UrlTest, Parent) { xp=x.GetLineageReference(4); EXPECT_EQ("[0x1]/this", xp.GetBucketPathName()); EXPECT_TRUE(xp.IsRootLevel()); } TEST_F(UrlTest, IsRoot) { - ResourceURL x("[0x1]/myroot"); EXPECT_TRUE(x.IsRootLevel()); - ResourceURL x2("ref", NODE_LOCALHOST, bucket_t(90210,iuo), "/", "myroot", ""); EXPECT_TRUE(x.IsRootLevel()); + ResourceURL x("[0x1]/myroot"); + EXPECT_TRUE( x.IsRootLevel()); + EXPECT_FALSE(x.IsRoot()); + EXPECT_TRUE(x.Valid()); + + ResourceURL x2("ref", NODE_LOCALHOST, bucket_t(90210,iuo), "/", "myroot", ""); + EXPECT_TRUE( x2.IsRootLevel()); + EXPECT_FALSE(x2.IsRoot()); + EXPECT_TRUE(x2.Valid()); + + ResourceURL x3("/"); + EXPECT_TRUE(x3.IsRootLevel()); + EXPECT_TRUE(x3.IsRoot()); + EXPECT_TRUE(x3.Valid()); + + ResourceURL x4("ref:/"); + EXPECT_TRUE(x4.IsRootLevel()); + EXPECT_TRUE(x4.IsRoot()); + EXPECT_TRUE(x4.Valid()); + } TEST_F(UrlTest, Local) { ResourceURL x("local:[0x1]"); - EXPECT_EQ("local",x.resource_type); + EXPECT_EQ("local",x.Type()); EXPECT_EQ(0x1, x.bucket.bid); } TEST_F(UrlTest, NoRef) { - //Sometimes we want to ask for a resource, but we don't know exactly what the - //thing is yet. Thus, we want to make sure the reference is prepended with - //an empty ref (ie just ":") + //previous: Sometimes we want to ask for a resource, but we don't know exactly what the + //previous: thing is yet. Thus, we want to make sure the reference is prepended with + //previous: an empty ref (ie just ":") + //In Feb 2019, this changed. If no type is defined, GetFullURL will return ref. ResourceURL x("/a/b/c&op1=100&op2=200"); string s = x.GetFullURL(); - EXPECT_EQ(":<0x0>[0x0]/a/b/c&op1=100&op2=200", s); + EXPECT_EQ("ref:<0x0>[0x0]/a/b/c&op1=100&op2=200", s); + + s = x.GetBucketPathName(); + EXPECT_EQ("[0x0]/a/b/c", s); } diff --git a/tests/common/unit/tb_common_structs.cpp b/tests/common/unit/tb_common_structs.cpp index 1d9396d..e3abb4c 100644 --- a/tests/common/unit/tb_common_structs.cpp +++ b/tests/common/unit/tb_common_structs.cpp @@ -5,7 +5,7 @@ // This aggregates tests for basic structures including: // // Bucket : A hash of a string -// NodeID : Holds host/port for webhook +// NodeID : Holds host/port for whookie // NameAndNode : A string label and a node id // DirectoryInfo : Holds a list of name/node resource @@ -286,12 +286,12 @@ TEST_F(DirectoryInfoTest, SimpleByHand) { DirectoryInfo di1("ref:/my/thing&num=2&ag0=0x19900001&ag1=0x19900002&info=silly%20stuff"); EXPECT_FALSE(di1.IsEmpty()); - EXPECT_EQ(2, di1.children.size()); - if(di1.children.size()==2){ - EXPECT_EQ("ag0", di1.children[0].name); - EXPECT_EQ("ag1", di1.children[1].name); - EXPECT_EQ("0x19900001", di1.children[0].node.GetHex()); - EXPECT_EQ("0x19900002", di1.children[1].node.GetHex()); + EXPECT_EQ(2, di1.members.size()); + if(di1.members.size()==2){ + EXPECT_EQ("ag0", di1.members[0].name); + EXPECT_EQ("ag1", di1.members[1].name); + EXPECT_EQ("0x19900001", di1.members[0].node.GetHex()); + EXPECT_EQ("0x19900002", di1.members[1].node.GetHex()); } EXPECT_EQ("silly stuff", di1.info); @@ -310,14 +310,14 @@ TEST_F(DirectoryInfoTest, SimpleByHand) { DirectoryInfo di2(url2); EXPECT_FALSE(di2.IsEmpty()); - EXPECT_EQ(10, di2.children.size()); - if(di2.children.size() == 10) { + EXPECT_EQ(10, di2.members.size()); + if(di2.members.size() == 10) { for(int i=0; i<10; i++) { string s_node = "0x1990000"+to_string(i); string s_name = "ag"+to_string(i); EXPECT_TRUE(di2.ContainsNode(nodeid_t(s_node))); - EXPECT_EQ(s_name, di2.children[i].name); - EXPECT_EQ(nodeid_t(s_node), di2.children[i].node); + EXPECT_EQ(s_name, di2.members[i].name); + EXPECT_EQ(nodeid_t(s_node), di2.members[i].node); } } EXPECT_EQ("rain is in the plains", di2.info); diff --git a/tests/dirman/component/mpi_dirman_centralized.cpp b/tests/dirman/component/mpi_dirman_centralized.cpp index 3a99be7..558568b 100644 --- a/tests/dirman/component/mpi_dirman_centralized.cpp +++ b/tests/dirman/component/mpi_dirman_centralized.cpp @@ -41,7 +41,7 @@ dirman.resources_mpi[] dht:/static/root_node&info="RootNode" LAST #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true @@ -67,10 +67,10 @@ TEST_F(DirManCentralized, Simple){ //Lookup the root node. We had mpisyncstart figure this out in configuration ok = dirman::GetDirectoryInfo(ResourceURL("ref:/static/root_node"), &dir_info); EXPECT_TRUE(ok); - EXPECT_EQ(1, dir_info.children.size()); - if(dir_info.children.size()==1) { - root_node = dir_info.children.at(0).node; - //cout <<"------>Root node is "<Root node is "<info); EXPECT_EQ("/a/b", di->url.path); EXPECT_EQ("c", di->url.name); - EXPECT_EQ(3, ((int)di->children.size())); + EXPECT_EQ(3, ((int)di->members.size())); ok = di->GetChildReferenceNode("d", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(200,iuo), n); ok = di->GetChildReferenceNode("e", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(201,iuo), n); @@ -117,7 +117,7 @@ TEST_F(OpDirManCreateTest, SimpleSerializeDirInfo){ EXPECT_EQ("This is the thing",di2.info); EXPECT_EQ("/a/b", di2.url.path); EXPECT_EQ("c", di2.url.name); - EXPECT_EQ(3, ((int)di2.children.size())); + EXPECT_EQ(3, ((int)di2.members.size())); ok = di2.GetChildReferenceNode("d", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(200,iuo), n); ok = di2.GetChildReferenceNode("e", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(201,iuo), n); @@ -131,7 +131,7 @@ TEST_F(OpDirManCreateTest, SimpleSerializeResourceURL){ //Create a url we can ship ResourceURL url1("dht:[0x2112]<0x1234>/a/b/c&info=nacho_cheese"); - EXPECT_EQ("dht", url1.resource_type); + EXPECT_EQ("dht", url1.Type()); EXPECT_EQ(nodeid_t(0x1234,iuo), url1.reference_node); EXPECT_EQ(bucket_t(0x2112,iuo), url1.bucket); EXPECT_EQ("/a/b", url1.path); @@ -172,7 +172,7 @@ TEST_F(OpDirManCreateTest, SimpleSerializeResourceURL){ EXPECT_EQ(url1, url2); //Should look like the original - EXPECT_EQ("dht", url2.resource_type); + EXPECT_EQ("dht", url2.Type()); EXPECT_EQ(nodeid_t(0x1234,iuo), url2.reference_node); EXPECT_EQ(bucket_t(0x2112,iuo), url2.bucket); EXPECT_EQ("/a/b", url2.path); diff --git a/tests/dirman/component/mpi_dirman_restart.cpp b/tests/dirman/component/mpi_dirman_restart.cpp index 0dd309c..81a0f6a 100644 --- a/tests/dirman/component/mpi_dirman_restart.cpp +++ b/tests/dirman/component/mpi_dirman_restart.cpp @@ -12,7 +12,7 @@ #include "gtest/gtest.h" #include "faodel-common/Common.hh" -#include "webhook/client/Client.hh" +#include "whookie/client/Client.hh" #include "lunasa/Lunasa.hh" #include "faodel-services/MPISyncStart.hh" @@ -99,8 +99,8 @@ TEST_F(OpDirManRestartTest, GetStatic1) { cout <<"dir1 is : "<JoinDirWithName(ResourceURL("/things/a"), "b", &di2); EXPECT_TRUE(ok); ok = dmcc->JoinDirWithName(ResourceURL("<0x99>/things/a"), "c"); EXPECT_TRUE(ok); @@ -109,11 +109,11 @@ TEST_F(DirManCoreCentralizedTest, Simple){ ok = di2.GetChildReferenceNode("c", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(0x99,iuo), n); ok = di2.GetChildReferenceNode("d", &n); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(0x88,iuo), n); ok = di2.GetChildReferenceNode("x", &n); EXPECT_FALSE(ok); - EXPECT_EQ(3, di2.children.size()); + EXPECT_EQ(3, di2.members.size()); - //Remove some of the children + //Remove some of the members DirectoryInfo di3; - ok = dmcc->LeaveDir(ResourceURL("/things/a/c"), &di3); EXPECT_TRUE(ok); EXPECT_EQ(2, di3.children.size()); + ok = dmcc->LeaveDir(ResourceURL("/things/a/c"), &di3); EXPECT_TRUE(ok); EXPECT_EQ(2, di3.members.size()); ok = di3.GetChildReferenceNode("b"); EXPECT_TRUE(ok); ok = di3.GetChildReferenceNode("c"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("d"); EXPECT_TRUE(ok); @@ -121,21 +121,21 @@ TEST_F(DirManCoreCentralizedTest, Simple){ ok = dmcc->LeaveDir(ResourceURL("/things/a/X"), &di3); //Fake leave EXPECT_FALSE(ok); - EXPECT_EQ(2, di3.children.size()); + EXPECT_EQ(2, di3.members.size()); ok = di3.GetChildReferenceNode("b"); EXPECT_TRUE(ok); ok = di3.GetChildReferenceNode("c"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("d"); EXPECT_TRUE(ok); ok = dmcc->LeaveDir(ResourceURL("/things/a/b"), &di3); EXPECT_TRUE(ok); - EXPECT_EQ(1, di3.children.size()); + EXPECT_EQ(1, di3.members.size()); ok = di3.GetChildReferenceNode("b"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("c"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("d"); EXPECT_TRUE(ok); ok = dmcc->LeaveDir(ResourceURL("/things/a/d"), &di3); EXPECT_TRUE(ok); - EXPECT_EQ(0, di3.children.size()); + EXPECT_EQ(0, di3.members.size()); ok = di3.GetChildReferenceNode("b"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("c"); EXPECT_FALSE(ok); ok = di3.GetChildReferenceNode("d"); EXPECT_FALSE(ok); @@ -166,7 +166,7 @@ TEST_F(DirManCoreCentralizedTest, JoinNoName){ ok = dmcc->JoinDirWithoutName(ResourceURL("<0x92>/things/a")); EXPECT_TRUE(ok); ok = dmcc->JoinDirWithoutName(ResourceURL("<0x93>/things/a")); EXPECT_TRUE(ok); ok = dmcc->JoinDirWithoutName(ResourceURL("<0x94>/things/a"), &di2); EXPECT_TRUE(ok); - EXPECT_EQ(5, di2.children.size()); + EXPECT_EQ(5, di2.members.size()); //cout <JoinDirWithoutName(ResourceURL("<0x92>/things/b")); EXPECT_TRUE(ok); ok = dmcc->JoinDirWithoutName(ResourceURL("<0x93>/things/b")); EXPECT_TRUE(ok); ok = dmcc->JoinDirWithoutName(ResourceURL("<0x94>/things/b"), &di2); EXPECT_TRUE(ok); - EXPECT_EQ(5, di2.children.size()); + EXPECT_EQ(5, di2.members.size()); //cout < *children){ + virtual bool GenChildVector(unsigned int fanout, nodeid_t parent_id, unsigned int num_rows, vector *members){ unsigned int child_level, child_id, child_offset; GetChildInfo(fanout, parent_id.nid-tree_starting_node_id, &child_level, &child_id, &child_offset); @@ -117,7 +117,7 @@ class DirectoryCacheTest : public testing::Test { if(child_level >= num_rows) return false; for(unsigned int i=0; ipush_back( NameAndNode(GetEntryName(child_level, child_offset+i), nodeid_t(child_id+i,iuo)) ); + members->push_back( NameAndNode(GetEntryName(child_level, child_offset+i), nodeid_t(child_id+i,iuo)) ); } return true; } @@ -184,13 +184,13 @@ TEST_F(DirectoryCacheTest, SimpleByHand){ EXPECT_EQ("/x/y", ro.url.path); EXPECT_EQ("z", ro.url.name); EXPECT_EQ(def_bucket, ro.url.bucket); - EXPECT_EQ(0, ro.children.size()); + EXPECT_EQ(0, ro.members.size()); //--- Modify the item, push it back, and verify ---- - ro.children.push_back(NameAndNode("Bobby",nodeid_t(101,iuo))); - ro.children.push_back(NameAndNode("Billy",nodeid_t(102,iuo))); - ro.children.push_back(NameAndNode("Betty",nodeid_t(103,iuo))); + ro.members.push_back(NameAndNode("Bobby",nodeid_t(101,iuo))); + ro.members.push_back(NameAndNode("Billy",nodeid_t(102,iuo))); + ro.members.push_back(NameAndNode("Betty",nodeid_t(103,iuo))); ok = dc->Update(ro); EXPECT_TRUE(ok); @@ -203,15 +203,15 @@ TEST_F(DirectoryCacheTest, SimpleByHand){ EXPECT_EQ("/x/y", ro2.url.path); EXPECT_EQ("z", ro2.url.name); EXPECT_EQ(def_bucket, ro2.url.bucket); - EXPECT_EQ(3, ro2.children.size()); + EXPECT_EQ(3, ro2.members.size()); //cout <<"----"<str(100)<<"/////Done\n"; } @@ -260,7 +260,7 @@ TEST_F(DirectoryCacheTest, SimpleAutomated){ } } -TEST_F(DirectoryCacheTest, TreeUpdateChildren){ +TEST_F(DirectoryCacheTest, TreeUpdateMembers){ unsigned int fanout=3; unsigned int num_rows=3; @@ -290,12 +290,12 @@ TEST_F(DirectoryCacheTest, TreeUpdateChildren){ ok = dc->Lookup(ResourceURL(id_name.second), &ro, &node); EXPECT_TRUE(ok); EXPECT_EQ(id_name.first, node); - EXPECT_EQ(0, ro.children.size()); + EXPECT_EQ(0, ro.members.size()); nodeid_t id = id_name.first; - bool had_children = GenChildVector(fanout, id_name.first, num_rows, &ro.children); - if(!had_children) continue; + bool had_members = GenChildVector(fanout, id_name.first, num_rows, &ro.members); + if(!had_members) continue; ok = dc->Update(ro); EXPECT_TRUE(ok); @@ -309,12 +309,12 @@ TEST_F(DirectoryCacheTest, TreeUpdateChildren){ EXPECT_TRUE(ok); EXPECT_EQ(id_name.first, node); - vector expected_children; - bool had_children = GenChildVector(fanout, id_name.first, num_rows, &expected_children); - EXPECT_EQ(expected_children.size(), ro.children.size()); - if(expected_children.size() == ro.children.size()){ - for(unsigned int i=0; i< expected_children.size(); i++){ - EXPECT_EQ(expected_children[i], ro.children[i]); + vector expected_members; + bool had_members = GenChildVector(fanout, id_name.first, num_rows, &expected_members); + EXPECT_EQ(expected_members.size(), ro.members.size()); + if(expected_members.size() == ro.members.size()){ + for(unsigned int i=0; i< expected_members.size(); i++){ + EXPECT_EQ(expected_members[i], ro.members[i]); } } } @@ -362,23 +362,23 @@ TEST_F(DirectoryCacheTest, TreeJoin){ EXPECT_TRUE(ok); EXPECT_EQ(id_name.first, node); - vector expected_children; - bool had_children = GenChildVector(fanout, id_name.first, num_rows, &expected_children); + vector expected_members; + bool had_members = GenChildVector(fanout, id_name.first, num_rows, &expected_members); - EXPECT_EQ(expected_children.size(), ro.children.size()); - if(expected_children.size() == ro.children.size()){ - for(unsigned int i=0; i< expected_children.size(); i++){ - EXPECT_EQ(expected_children[i], ro.children[i]); + EXPECT_EQ(expected_members.size(), ro.members.size()); + if(expected_members.size() == ro.members.size()){ + for(unsigned int i=0; i< expected_members.size(); i++){ + EXPECT_EQ(expected_members[i], ro.members[i]); } } else { cout <<"Child problem for "<Create(DirectoryInfo("<0x05>/my")); EXPECT_TRUE(ok); ok = dc->Join(ResourceURL("<0x10>/my/thing"), &di0); EXPECT_TRUE(ok); - EXPECT_EQ(1, di0.children.size()); + EXPECT_EQ(1, di0.members.size()); di0.GetChildReferenceNode("thing",&ref_node); EXPECT_EQ(nodeid_t(0x10,iuo),ref_node); @@ -412,7 +412,7 @@ TEST_F(DirectoryCacheTest, JoinLeave) { DirectoryInfo di1; ok = dc->Lookup(ResourceURL("/my/thing"), &di1, &ref_node); EXPECT_TRUE(ok); EXPECT_EQ(nodeid_t(0x10, iuo), ref_node); - EXPECT_EQ(di1.children.size(), 4); + EXPECT_EQ(di1.members.size(), 4); di1.GetChildReferenceNode("a", &ref_node); EXPECT_EQ(nodeid_t(0x20,iuo), ref_node); di1.GetChildReferenceNode("b", &ref_node); EXPECT_EQ(nodeid_t(0x21,iuo), ref_node); di1.GetChildReferenceNode("c", &ref_node); EXPECT_EQ(nodeid_t(0x22,iuo), ref_node); @@ -428,12 +428,12 @@ TEST_F(DirectoryCacheTest, JoinLeave) { ok = di1.GetChildReferenceNode("c", &ref_node); EXPECT_FALSE(ok); ok = di1.GetChildReferenceNode("d", &ref_node); EXPECT_EQ(nodeid_t(0x23,iuo), ref_node); - //Get rid of all children, one-by-one - for(auto name_node : di1.children){ + //Get rid of all members, one-by-one + for(auto name_node : di1.members){ ok = dc->Leave(ResourceURL("/my/thing/"+name_node.name)); EXPECT_TRUE(ok); } ok = dc->Lookup(ResourceURL("/my/thing"), &di1, &ref_node); EXPECT_TRUE(ok); - EXPECT_EQ(0, di1.children.size()); + EXPECT_EQ(0, di1.members.size()); //Add first tree back in @@ -455,20 +455,20 @@ TEST_F(DirectoryCacheTest, JoinLeave) { ok = dc->Lookup(ResourceURL("/my/thing"), &di2, &ref_node); EXPECT_TRUE(ok); di2.GetChildReferenceNode("bb", &ref_node); EXPECT_EQ(nodeid_t(0x31,iuo), ref_node); di2.GetChildReferenceNode("aa", &ref_node); EXPECT_EQ(nodeid_t(0x21,iuo), ref_node); - EXPECT_EQ(2, di2.children.size()); + EXPECT_EQ(2, di2.members.size()); //Left child tree aa ok = dc->Lookup(ResourceURL("/my/thing/aa"), &di2, &ref_node); EXPECT_TRUE(ok); di2.GetChildReferenceNode("1", &ref_node); EXPECT_EQ(nodeid_t(0x22,iuo), ref_node); di2.GetChildReferenceNode("2", &ref_node); EXPECT_EQ(nodeid_t(0x23,iuo), ref_node); di2.GetChildReferenceNode("3", &ref_node); EXPECT_EQ(nodeid_t(0x24,iuo), ref_node); - EXPECT_EQ(3, di2.children.size()); + EXPECT_EQ(3, di2.members.size()); //Right child tree bb ok = dc->Lookup(ResourceURL("/my/thing/bb"), &di2, &ref_node); EXPECT_TRUE(ok); di2.GetChildReferenceNode("1", &ref_node); EXPECT_EQ(nodeid_t(0x32,iuo), ref_node); di2.GetChildReferenceNode("2", &ref_node); EXPECT_EQ(nodeid_t(0x33,iuo), ref_node); - EXPECT_EQ(2, di2.children.size()); + EXPECT_EQ(2, di2.members.size()); //Delete bb. Should leave /my /my/thing /my/thing/aa @@ -478,13 +478,13 @@ TEST_F(DirectoryCacheTest, JoinLeave) { //Verify rest of stuff still there ok = dc->Lookup(ResourceURL("/my/thing"), &di2); EXPECT_TRUE(ok); di2.GetChildReferenceNode("aa", &ref_node); EXPECT_EQ(nodeid_t(0x21,iuo), ref_node); - EXPECT_EQ(1, di2.children.size()); + EXPECT_EQ(1, di2.members.size()); ok = dc->Lookup(ResourceURL("/my/thing/bb"), &di2); EXPECT_FALSE(ok); ok = dc->Lookup(ResourceURL("/my/thing/aa"), &di2); EXPECT_TRUE(ok); di2.GetChildReferenceNode("1", &ref_node); EXPECT_EQ(nodeid_t(0x22,iuo), ref_node); di2.GetChildReferenceNode("2", &ref_node); EXPECT_EQ(nodeid_t(0x23,iuo), ref_node); di2.GetChildReferenceNode("3", &ref_node); EXPECT_EQ(nodeid_t(0x24,iuo), ref_node); - EXPECT_EQ(3, di2.children.size()); + EXPECT_EQ(3, di2.members.size()); //vector urls1; //dc->GetAllURLs(&urls1); diff --git a/tests/kelpie/CMakeLists.txt b/tests/kelpie/CMakeLists.txt index a835fc2..c868fc3 100644 --- a/tests/kelpie/CMakeLists.txt +++ b/tests/kelpie/CMakeLists.txt @@ -2,19 +2,25 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/mpi ) SET(EXTRA_TEST_LIBS "-lz -ldl") # Build the test support lib------------------------------------------------- -add_library(kelpie_mpi_test_support +if(Faodel_ENABLE_MPI_SUPPORT) + + add_library(kelpie_mpi_test_support component/support/Globals.hh component/support/Globals.cpp component/support/ExperimentLauncher.hh component/support/ExperimentLauncher.cpp) -target_link_libraries( kelpie_mpi_test_support - opbox - MPI::MPI_CXX - GTest::GTest - GTest::Main - ) -set_target_properties( kelpie_mpi_test_support PROPERTIES LINKER_LANGUAGE CXX ) + + target_link_libraries( kelpie_mpi_test_support + opbox + MPI::MPI_CXX + GTest::GTest + GTest::Main + ) + set_target_properties( kelpie_mpi_test_support PROPERTIES LINKER_LANGUAGE CXX ) + +endif() + #---------------------------------------------------------------------------- set(COMMON_TEST_LIBS @@ -29,11 +35,14 @@ set(SERIAL_TEST_LIBS GTest::GTest GTest::Main ) + set(MPI_TEST_LIBS kelpie_mpi_test_support ${COMMON_TEST_LIBS} ) + + #--------------+-----------------------------+---------------+---------+ # Format: | Name | Directory | Autorun | #--------------+-----------------------------+---------------+---------+ @@ -43,9 +52,14 @@ add_serial_test( tb_kelpie_localkv unit true ) add_serial_test( tb_kelpie_message_direct unit/messages true ) add_serial_test( tb_kelpie_iom_pio_basic unit/ioms true ) add_serial_test( tb_kelpie_iom_all_basic unit/ioms true ) -add_serial_test( tb_kelpie_nonet_iompio component/nonet true ) +add_serial_test( tb_kelpie_iom_service_basic unit/ioms false ) if( Faodel_ENABLE_MPI_SUPPORT ) + + #TODO: the nonet has mpi stuff in it to make multiple tests run + add_serial_test( tb_kelpie_nonet_iompio component/nonet true ) + + add_mpi_test( mpi_kelpie_simple_peer component 2 true) add_mpi_test( mpi_kelpie_dht component 4 true) add_mpi_test( mpi_kelpie_rft component 16 true) diff --git a/tests/kelpie/component/mpi_kelpie_dht.cpp b/tests/kelpie/component/mpi_kelpie_dht.cpp index 38ec5f2..3951444 100644 --- a/tests/kelpie/component/mpi_kelpie_dht.cpp +++ b/tests/kelpie/component/mpi_kelpie_dht.cpp @@ -18,7 +18,7 @@ #include "dirman/DirMan.hh" #include "kelpie/Kelpie.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "support/Globals.hh" @@ -35,7 +35,7 @@ struct Params { int num_cols; int ldo_size; }; -Params P = { 2, 10, 20*1024 }; +Params P = { 8, 10, 20*1024 }; @@ -52,7 +52,7 @@ target.dirman.host_root #kelpie.type standard #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true @@ -180,7 +180,7 @@ vector> generateAndPublish(kelpie::Pool dh } ); EXPECT_EQ(0, rc); } - while(num_left) {sched_yield(); } //TODO: add a timeout + while(num_left) { sched_yield(); } //TODO: add a timeout return kvs; } @@ -270,19 +270,19 @@ void checkWantUnbounded(kelpie::Pool dht, const vector= 4 && "mpi_kelpie_dht needs to be 4 or larger"); if(G.mpi_rank==0){ diff --git a/tests/kelpie/component/mpi_kelpie_iom_dht.cpp b/tests/kelpie/component/mpi_kelpie_iom_dht.cpp index 9d30802..7230e25 100644 --- a/tests/kelpie/component/mpi_kelpie_iom_dht.cpp +++ b/tests/kelpie/component/mpi_kelpie_iom_dht.cpp @@ -39,7 +39,7 @@ lunasa.eager_memory_manager malloc # Enable all debug by labeling this node's role as debug_node debug_node.mpisyncstart.debug true debug_node.bootstrap.debug true -debug_node.webhook.debug true +debug_node.whookie.debug true debug_node.opbox.debug true debug_node.dirman.debug true debug_node.dirman.cache.mine.debug true @@ -154,19 +154,19 @@ dirman.resources[] dht:<0x1234>[0x5678]/other/thing&num=2&ag0=0x4444&ag1=0x5 auto pool_particle = kelpie::Connect("ref:/myapp/particle"); EXPECT_EQ(0xeea6081, faodel::hash32("myiom1")); EXPECT_EQ(faodel::hash32("myiom1"), pool_particle.GetIomHash()); - EXPECT_EQ("dht", pool_particle.GetURL().resource_type); + EXPECT_EQ("dht", pool_particle.GetURL().Type()); //The pool should have the myiom2 hash associated with it auto pool_fluid = kelpie::Connect("ref:/myapp/fluid"); EXPECT_EQ(0xeea6082, faodel::hash32("myiom2")); EXPECT_EQ(faodel::hash32("myiom2"), pool_fluid.GetIomHash()); - EXPECT_EQ("local", pool_fluid.GetURL().resource_type); + EXPECT_EQ("local", pool_fluid.GetURL().Type()); //The pool should have the myiom2 hash associated with it auto pool_stuff = kelpie::Connect("ref:/myapp/stuff"); EXPECT_EQ(0xeea6083, faodel::hash32("myiom3")); EXPECT_EQ(faodel::hash32("myiom3"), pool_stuff.GetIomHash()); - EXPECT_EQ("local", pool_fluid.GetURL().resource_type); + EXPECT_EQ("local", pool_fluid.GetURL().Type()); el_bcastCommand(CMD_DUMP_RESOURCES, "/myapp/fluid"); el_bcastCommand(CMD_DUMP_RESOURCES, "/myapp/particle"); @@ -207,8 +207,8 @@ dirman.resources[] local:/EMPIRE/fields&iom=empire_fields EXPECT_EQ(0x31d0fd3d, faodel::hash32("empire_fields")); EXPECT_EQ(faodel::hash32("empire_particles"), pool_particles.GetIomHash()); EXPECT_EQ(faodel::hash32("empire_fields"), pool_fields.GetIomHash()); - EXPECT_EQ("local", pool_particles.GetURL().resource_type); - EXPECT_EQ("local", pool_fields.GetURL().resource_type); + EXPECT_EQ("local", pool_particles.GetURL().Type()); + EXPECT_EQ("local", pool_fields.GetURL().Type()); el_bcastCommand(CMD_DUMP_RESOURCES, "/EMPIRE/particles"); el_bcastCommand(CMD_DUMP_RESOURCES, "/EMPIRE/fields"); diff --git a/tests/kelpie/component/mpi_kelpie_rft.cpp b/tests/kelpie/component/mpi_kelpie_rft.cpp index 2999a31..0899797 100644 --- a/tests/kelpie/component/mpi_kelpie_rft.cpp +++ b/tests/kelpie/component/mpi_kelpie_rft.cpp @@ -18,7 +18,7 @@ #include "dirman/DirMan.hh" #include "kelpie/Kelpie.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "support/Globals.hh" @@ -54,7 +54,7 @@ target.dirman.host_root #kelpie.type standard #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true @@ -132,16 +132,16 @@ TEST_F(MPIRFTTest, CheckRFTs) { auto di_back1 = rft_back1.GetDirectoryInfo(); //Full size - EXPECT_EQ(G.mpi_size, di_full.children.size()); - EXPECT_EQ(G.mpi_size, di_full0.children.size()); - EXPECT_EQ(G.mpi_size, di_full1.children.size()); + EXPECT_EQ(G.mpi_size, di_full.members.size()); + EXPECT_EQ(G.mpi_size, di_full0.members.size()); + EXPECT_EQ(G.mpi_size, di_full1.members.size()); //Back size: everyone but us int num_back = G.mpi_size-1; //not us - EXPECT_EQ(num_back, di_back.children.size()); - EXPECT_EQ(num_back, di_back0.children.size()); - EXPECT_EQ(num_back, di_back1.children.size()); + EXPECT_EQ(num_back, di_back.members.size()); + EXPECT_EQ(num_back, di_back0.members.size()); + EXPECT_EQ(num_back, di_back1.members.size()); @@ -151,11 +151,11 @@ TEST_F(MPIRFTTest, CheckRFTs) { count = rft_full.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(my_id, node_id); count = rft_full0.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(my_id, node_id); - count = rft_full1.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_full.children[1].node, node_id); + count = rft_full1.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_full.members[1].node, node_id); - count = rft_back.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.children[0].node, node_id); - count = rft_back0.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.children[0].node, node_id); - count = rft_back1.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.children[1].node, node_id); + count = rft_back.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.members[0].node, node_id); + count = rft_back0.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.members[0].node, node_id); + count = rft_back1.FindTargetNode(key, &node_id); EXPECT_EQ(1, count); EXPECT_EQ(di_back.members[1].node, node_id); } diff --git a/tests/kelpie/component/mpi_kelpie_simple_peer.cpp b/tests/kelpie/component/mpi_kelpie_simple_peer.cpp index ea4fffe..5381218 100644 --- a/tests/kelpie/component/mpi_kelpie_simple_peer.cpp +++ b/tests/kelpie/component/mpi_kelpie_simple_peer.cpp @@ -19,7 +19,7 @@ #include "dirman/DirMan.hh" #include "kelpie/Kelpie.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "support/Globals.hh" @@ -43,7 +43,7 @@ target.dirman.host_root kelpie.type standard #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true @@ -74,7 +74,7 @@ TEST_F(MPISimplePeerTest, BasicDHTCreateAndPublish){ ResourceURL url("dht:/mydht"); kelpie::Pool dht = kelpie::Connect(url); DirectoryInfo dir_info = dht.GetDirectoryInfo(); - EXPECT_EQ(G.mpi_size-1, dir_info.children.size()); + EXPECT_EQ(G.mpi_size-1, dir_info.members.size()); //First: Verify remote doesn't know what this item is diff --git a/tests/kelpie/component/mpi_kelpie_standalone.cpp b/tests/kelpie/component/mpi_kelpie_standalone.cpp index 3023fd6..f51cf95 100644 --- a/tests/kelpie/component/mpi_kelpie_standalone.cpp +++ b/tests/kelpie/component/mpi_kelpie_standalone.cpp @@ -15,17 +15,17 @@ #include "gtest/gtest.h" #include "faodel-common/Common.hh" -#include "webhook/WebHook.hh" +#include "whookie/Whookie.hh" #include "lunasa/Lunasa.hh" #include "opbox/OpBox.hh" #include "kelpie/Kelpie.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" std::string default_config_string = R"EOF( #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true @@ -101,7 +101,7 @@ int main( int argc, char **argv ) { // Tell the DHT it can shut down if( mpi_rank == 0 ) { faodel::nodeid_t root_id( root_id_str ); - webhook::retrieveData( root_id, "/killme", nullptr ); + whookie::retrieveData( root_id, "/killme", nullptr ); } MPI_Barrier( MPI_COMM_WORLD ); diff --git a/tests/kelpie/component/nonet/tb_kelpie_nonet_iompio.cpp b/tests/kelpie/component/nonet/tb_kelpie_nonet_iompio.cpp index 28b513e..73745ce 100644 --- a/tests/kelpie/component/nonet/tb_kelpie_nonet_iompio.cpp +++ b/tests/kelpie/component/nonet/tb_kelpie_nonet_iompio.cpp @@ -32,7 +32,7 @@ default.ioms myiom1;myiom2;myiom3 # Uncomment these options to get debug info for each component #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true diff --git a/tests/kelpie/component/support/ExperimentLauncher.hh b/tests/kelpie/component/support/ExperimentLauncher.hh index ec936aa..3a956c7 100644 --- a/tests/kelpie/component/support/ExperimentLauncher.hh +++ b/tests/kelpie/component/support/ExperimentLauncher.hh @@ -7,7 +7,7 @@ #include -const int CMD_NEW_KELPIE_START = -1; //Here's a config, launch with webhook+mpisync +const int CMD_NEW_KELPIE_START = -1; //Here's a config, launch with whookie+mpisync const int CMD_TEARDOWN = -2; const int CMD_KILL = -3; diff --git a/tests/kelpie/component/support/Globals.cpp b/tests/kelpie/component/support/Globals.cpp index 43452e4..ac115fe 100644 --- a/tests/kelpie/component/support/Globals.cpp +++ b/tests/kelpie/component/support/Globals.cpp @@ -28,7 +28,7 @@ Globals::~Globals(){ void Globals::StartAll(int &argc, char **argv, faodel::Configuration &config){ stringstream ss; - string webhook_port; + string whookie_port; int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); diff --git a/tests/kelpie/component/support/Globals.hh b/tests/kelpie/component/support/Globals.hh index 20250e0..c6dbc7b 100644 --- a/tests/kelpie/component/support/Globals.hh +++ b/tests/kelpie/component/support/Globals.hh @@ -9,7 +9,7 @@ #include "faodel-common/Common.hh" #include "opbox/OpBox.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "kelpie/Kelpie.hh" diff --git a/tests/kelpie/unit/ioms/tb_kelpie_iom_all_basic.cpp b/tests/kelpie/unit/ioms/tb_kelpie_iom_all_basic.cpp index 8b55b12..99b8175 100644 --- a/tests/kelpie/unit/ioms/tb_kelpie_iom_all_basic.cpp +++ b/tests/kelpie/unit/ioms/tb_kelpie_iom_all_basic.cpp @@ -30,7 +30,7 @@ std::string default_config_string = R"EOF( # Uncomment these options to get debug info for each component #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.iom_registry.debug true diff --git a/tests/kelpie/unit/ioms/tb_kelpie_iom_pio_basic.cpp b/tests/kelpie/unit/ioms/tb_kelpie_iom_pio_basic.cpp index 9397150..8a5a7ce 100644 --- a/tests/kelpie/unit/ioms/tb_kelpie_iom_pio_basic.cpp +++ b/tests/kelpie/unit/ioms/tb_kelpie_iom_pio_basic.cpp @@ -22,7 +22,7 @@ std::string default_config_string = R"EOF( # Uncomment these options to get debug info for each component #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true #kelpie.debug true diff --git a/tests/kelpie/unit/ioms/tb_kelpie_iom_service_basic.cpp b/tests/kelpie/unit/ioms/tb_kelpie_iom_service_basic.cpp new file mode 100644 index 0000000..144eb7b --- /dev/null +++ b/tests/kelpie/unit/ioms/tb_kelpie_iom_service_basic.cpp @@ -0,0 +1,306 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +/* + * There is some significant overlap between this file and the iom_all_basic module. + * The best decomposition seems to be that those tests involve persistent storage + * on a locally accessible filesystem, while these require a network connection to a + * service. The actual IOM testing is largely the same, but the way in which it has to + * integrate into GTest (a single test class) complicated things (like endpoint and keyspace name handling) + * enough to warrant breaking this out. I didn't see the immediate benefit in breaking the duplicated pieces + * (for IOM setup etc) out into a base class from which this and the filesystem storage class + * would inherit, but that also seems like the right OO design thing to do. + */ + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "kelpie/localkv/LocalKV.hh" +#include "kelpie/ioms/IomRegistry.hh" + +#ifdef FAODEL_HAVE_CASSANDRA +#include "kelpie/ioms/IomCassandra.hh" +#endif + +using namespace std; +using namespace faodel; +using namespace kelpie; + +//The configuration used in this example +std::string default_config_string = R"EOF( + +# Uncomment these options to get debug info for each component +#bootstrap.debug true +#whookie.debug true +#opbox.debug true +#dirman.debug true +#kelpie.iom_registry.debug true + +# We start/stop multiple times (which lunasa's tcmalloc does not like), so +# we have to switch to a plain malloc allocator +lunasa.lazy_memory_manager malloc +lunasa.eager_memory_manager malloc + +)EOF"; + + +template < typename T > +class IomSimple : public ::testing::Test { +protected: + void SetUp() override { + this->config.Append(default_config_string); + bootstrap::Init(config, lunasa::bootstrap); + this->lkv = new LocalKV(); + this->lkv->Init(config); + bootstrap::Start(); + } + + void TearDown() override { + delete this->lkv; + bootstrap::Finish(); + } + + internal_use_only_t iuo; + Configuration config; + LocalKV *lkv; +}; + +TYPED_TEST_CASE_P(IomSimple); + + +struct test_data_t { + uint32_t block_id; + uint32_t data_bytes; + char name[256]; + uint8_t data[0]; +}; + +lunasa::DataObject createLDO(int id, string name, int data_bytes) { + + lunasa::DataObject ldo(sizeof(test_data_t), + sizeof(test_data_t)+data_bytes, + lunasa::DataObject::AllocatorType::eager); + + auto mptr = ldo.GetMetaPtr(); + auto dptr = ldo.GetDataPtr(); + + mptr->block_id = id; + mptr->data_bytes=0; + + std::memset(dptr->name, 0, 256); + std::memset(dptr->name, 0, 256); + + string meta_name = "id-"+std::to_string(id); + strncpy(mptr->name, meta_name.c_str(), 255); + strncpy(dptr->name, name.c_str(), 255); + + dptr->block_id = id; + dptr->data_bytes=data_bytes; + for(uint32_t i=0; idata[i] = (i&0x0FF); + + return ldo; +} + +bool checkLDO(const lunasa::DataObject &ldo, int id) { + EXPECT_EQ(sizeof(test_data_t), ldo.GetMetaSize()); + auto mptr = ldo.GetMetaPtr(); + auto dptr = ldo.GetDataPtr(); + + EXPECT_EQ(id, mptr->block_id); + EXPECT_EQ(id, dptr->block_id); + EXPECT_EQ(0, mptr->data_bytes); + + string mid = string(mptr->name); + EXPECT_EQ("id-"+std::to_string(id), mid); + + int bad_count=0; + for(int i=0; idata_bytes; i++) + if( dptr->data[i] != (i&0x0FF) ) bad_count++; + EXPECT_EQ(0, bad_count); + + return true; +} + +//Make sure generators are working ok +TYPED_TEST_P(IomSimple, ldo_gentest) { + vector ldos; + for(int i=0; i<10; i++) + ldos.push_back(createLDO(i, "bozo-"+to_string(i), i*100)); + for(int i=0; i<10; i++) + checkLDO(ldos[i], i); +} + +/* + * This GTest class is supposed to be as generic as the filesystem-based one in + * tb_kelpie_iom_all_basic. However, we're polluting the settings keyword space + * a little bit with keywords that not all the service-type IOMs might respond to. + * 'endpoint' is general enough, 'keyspace' and 'table' less so. However, not all + * IOM subclasses have to respond to all keywords. + */ + +TYPED_TEST_P(IomSimple, write_direct) { + + string endpoint = "localhost"; + + internal::IomBase *iom = new TypeParam("myiom", {{"endpoint",endpoint}}); + + vector keys; + bucket_t bucket("my_bucket"); + for(int i=0; i<10; i++){ + auto ldo = createLDO(i, "bozo-"+to_string(i), i*2); + kelpie::Key k("mybigitem",std::to_string(i)); + iom->WriteObject(bucket, k, ldo); + keys.push_back(k); + } + + vector> found_items; + vector missing_keys; + iom->ReadObjects(bucket, keys, &found_items, &missing_keys); + EXPECT_EQ(keys.size(), found_items.size()); + EXPECT_EQ(0, missing_keys.size()); + for(auto &key_ldo : found_items) { + int i=0; + while((iSetting("endpoint"); EXPECT_EQ(endpoint, sp1); + auto sk1=ioms[0]->Setting("keyspace"); EXPECT_EQ(ks1, sk1); + auto sp2=ioms[1]->Setting("endpoint"); EXPECT_EQ(endpoint, sp2); + auto sk2=ioms[1]->Setting("keyspace"); EXPECT_EQ(ks2, sk2); + auto settings1 = ioms[0]->Settings(); + auto settings2 = ioms[1]->Settings(); + EXPECT_EQ(endpoint, settings1["endpoint"]); + EXPECT_EQ(ks1,settings1["keyspace"]); + EXPECT_EQ(endpoint, settings2["endpoint"]); + EXPECT_EQ(ks2,settings2["keyspace"]); + // TODO: THESE NEED TO CHANGE IF WE USE AN IOM THAT KEEPS MORE SETTINGS + // OTHERWISE THIS WILL CAUSE THE TEST TO FAIL + // pmw: I'm commenting this out for now until we can take the time + // to think about how to best handle this. Ideally each IOM + // class definition would be able to report the expected size of + // their settings map here. But that's decoupled in those definitions + // from how those maps are defined (literals), so there's the possibility + // of those getting out of sync. That in turn could get fixed by not using + // literals in the IOM classes, so you could always ask a real map object + // what its size is, but you lose some convenience of expression and is that + // worth it and hopefully my choice to just comment out these test clauses maybe + // starts to make some sense for right now. + // EXPECT_EQ(1, settings1.size()); + // EXPECT_EQ(1, settings2.size()); + + registry.finish(); +} + + + +//Use a registry to track three separate storage locations +TYPED_TEST_P(IomSimple, iom_registry) { + + internal::IomRegistry registry; + registry.init(Configuration("")); + + string endpoint = "localhost"; + char kx1[] = "GTestAAA"; + char kx2[] = "GTestBBB"; + char kx3[] = "GTestCCC"; + + registry.RegisterIom(TypeParam::type_str, "myiom1", {{"endpoint",endpoint},{"keyspace",kx1},{"teardown","true"}} ); + registry.RegisterIom(TypeParam::type_str, "myiom2", {{"endpoint",endpoint},{"keyspace",kx2},{"teardown","true"}} ); + + registry.start(); //Make the next one go in mutex-controlled section + registry.RegisterIom(TypeParam::type_str, "myiom3", {{"endpoint",endpoint},{"keyspace",kx3}} ); + + //See if we can locate each one + kelpie::internal::IomBase * ioms[3]; + ioms[0] = registry.Find("myiom1"); ASSERT_NE(nullptr, ioms[0]); + ioms[1] = registry.Find("myiom2"); ASSERT_NE(nullptr, ioms[1]); + ioms[2] = registry.Find("myiom3"); ASSERT_NE(nullptr, ioms[2]); + auto iomX = registry.Find("myiomX"); EXPECT_EQ(nullptr, iomX); + + bucket_t buckets[3]; + buckets[0]=bucket_t("mybucketA"); + buckets[1]=bucket_t("mybucketB"); + buckets[2]=bucket_t("mybucketC"); + + //Write an item to each iom, using the same bucket + vector keys; + for(int i=0; i<3; i++) { + auto ldo = createLDO(i, "bozo-"+to_string(i), i*2); + kelpie::Key k("mybigitem",std::to_string(i)); + ioms[i]->WriteObject(buckets[0], k, ldo); + keys.push_back(k); + } + + //Verify we can get each item back + for(int i=0; i<3; i++) { + lunasa::DataObject ldo; + rc_t rc = ioms[i]->ReadObject(buckets[0], keys[i], &ldo); + EXPECT_EQ(KELPIE_OK, rc); + checkLDO(ldo, i); + + } + + registry.finish(); +} + +// Must enumerate all tests in the generic fixture class here +REGISTER_TYPED_TEST_CASE_P(IomSimple, + ldo_gentest, + write_direct, + UsingConfigurationByRole, + iom_registry); + + +// Must enumerate all desired IOM subclasses in this typedef +typedef ::testing::Types< + #ifdef FAODEL_HAVE_CASSANDRA + kelpie::internal::IomCassandra +#endif + > IomTypes; + +INSTANTIATE_TYPED_TEST_CASE_P(IomTypesInstantiation, IomSimple, IomTypes); diff --git a/tests/kelpie/unit/messages/tb_kelpie_message_direct.cpp b/tests/kelpie/unit/messages/tb_kelpie_message_direct.cpp index 5ba1aee..0165f2f 100644 --- a/tests/kelpie/unit/messages/tb_kelpie_message_direct.cpp +++ b/tests/kelpie/unit/messages/tb_kelpie_message_direct.cpp @@ -27,12 +27,12 @@ class MsgDirectTest : public testing::Test { faodel::Configuration config; if(enable_debug){ config.Append("bootstrap.debug","true"); - config.Append("webhook.debug", "true"); + config.Append("whookie.debug", "true"); config.Append("lunasa.debug", "true"); config.Append("opbox.debug","true"); } //Force this to an mpi implementation to make running easier - config.Append("net.transport.name","mpi"); + //config.Append("net.transport.name","mpi"); bootstrap::Start(config, kelpie::bootstrap); } diff --git a/tests/kelpie/unit/tb_kelpie_localkv.cpp b/tests/kelpie/unit/tb_kelpie_localkv.cpp index 436fbee..1f47ee0 100644 --- a/tests/kelpie/unit/tb_kelpie_localkv.cpp +++ b/tests/kelpie/unit/tb_kelpie_localkv.cpp @@ -52,7 +52,7 @@ class LocalKVTest : public testing::Test { } void TearDown() override { - delete lkv; //This deregisters webhooks, so do it before bootstrap finishes + delete lkv; //This deregisters whookies, so do it before bootstrap finishes bootstrap::Finish(); } @@ -60,7 +60,7 @@ class LocalKVTest : public testing::Test { vector> ids; int DIM; Configuration config; - LocalKV *lkv; //Note: we keep this as a pointer because this needs to be destroyed *before* webhook shuts down + LocalKV *lkv; //Note: we keep this as a pointer because this needs to be destroyed *before* whookie shuts down }; void setBuf(int *buf, int num, int owner, int x, int y){ @@ -240,4 +240,169 @@ TEST_F(LocalKVTest, IgnoreWrites) { rc = lkv->get(bucket, k1, &ldo_return, nullptr, nullptr); EXPECT_EQ(KELPIE_OK, rc); EXPECT_EQ(1024, ldo_return.GetUserSize()); +} + +TEST_F(LocalKVTest, ListRowStar) { + + int rc; + bucket_t bucket("bucky"); + + string row_names[]={"nothing1","nothing2","nothing3","nothing4", + "thing1","thing2","thing3", + "nothing5","nothing6", + "thing4"}; + + map row_to_size; + int i=0; + for( auto s : row_names) { + lunasa::DataObject ldo(100+i); + rc = lkv->put(bucket, kelpie::Key(s), ldo, PoolBehavior::WriteToLocal, nullptr,nullptr); EXPECT_EQ(KELPIE_OK, rc); + row_to_size[s] = 100+i; + i++; + } + + { //Get thing*|"" (four rows) + ObjectCapacities oc; + rc = lkv->list(bucket, kelpie::Key("thing*"), &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(oc.keys.size(), oc.capacities.size()); + EXPECT_EQ(4, oc.keys.size()); + for(int i = 0; ilist(bucket, kelpie::Key("thing3"), &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(oc.keys.size(), oc.capacities.size()); + EXPECT_EQ(1, oc.keys.size()); + if(oc.keys.size()>0) { + EXPECT_EQ(Key("thing3"), oc.keys[0]); + EXPECT_EQ(row_to_size["thing3"], oc.capacities[0]); + } + + } + +} +TEST_F(LocalKVTest, ListRowColStars) { + + int rc; + bucket_t bucket("bucky"); + + string row_names[] = {"some", "random", "column", "names", "go", "heree", "sowhat"}; + + string col_names[] = {"nothing1", "nothing2", "nothing3", "nothing4", + "thing1", "thing2", "thing3", + "nothing5", "nothing6", + "", + "thing4"}; + + map keymap_sizes; //records how big the ldo is + + //Insert a bunch or keys into lkv + int i = 0; + for(auto r : row_names) { + for(auto c : col_names) { + Key k(r,c); + lunasa::DataObject ldo(100 + i); + rc = lkv->put(bucket, k, ldo, PoolBehavior::WriteToLocal, nullptr, nullptr); + EXPECT_EQ(KELPIE_OK, rc); + keymap_sizes[k] = 100 +i; + i++; + } + } + + + //Good keys: Look for a list of exact matches + vector exact_keys; + exact_keys.push_back(Key("names", "thing3")); + exact_keys.push_back(Key("random","nothing1")); + exact_keys.push_back(Key("go", "")); + exact_keys.push_back(Key("some", "thing4")); + for(auto k : exact_keys) { + ObjectCapacities oc; + rc = lkv->list(bucket, k, &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(1, oc.keys.size()); EXPECT_EQ(1, oc.capacities.size()); + if((oc.keys.size()==1) && (oc.capacities.size() == 1)) { + EXPECT_EQ( k, oc.keys[0]); + EXPECT_EQ( keymap_sizes[k], oc.capacities[0]); + } + } + + //Missing keys: shouldn't find anything + vector missing_keys; + missing_keys.push_back(Key("Xnames", "thing3")); //bad row + missing_keys.push_back(Key("names", "thing3X")); //bad col + missing_keys.push_back(Key("Xname", "Xthing3")); //bad row/col + for(auto k : missing_keys) { + ObjectCapacities oc; + rc = lkv->list(bucket, k, &oc); + EXPECT_EQ(KELPIE_ENOENT, rc); + EXPECT_EQ(0, oc.keys.size()); EXPECT_EQ(0,oc.capacities.size()); + } + + + { //Exact Row, Col* + ObjectCapacities oc; + rc = lkv->list(bucket, Key("go", "thing*"), &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(4, oc.keys.size()); EXPECT_EQ(4,oc.capacities.size()); + vector found_cols; + for(int i=0; i expected_cols{"thing1","thing2","thing3", "thing4"}; + EXPECT_TRUE( (found_cols==expected_cols)); + } + + + { //Row*, Exact col + ObjectCapacities oc; + rc = lkv->list(bucket, Key("so*", "thing3"), &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(2, oc.keys.size()); EXPECT_EQ(2,oc.capacities.size()); + vector found_rows; + for(int i=0; i expected_rows{"some","sowhat"}; + EXPECT_TRUE( (found_rows==expected_rows)); + } + + { //Row*, Col* + ObjectCapacities oc; + rc = lkv->list(bucket, Key("so*", "thing*"), &oc); + EXPECT_EQ(KELPIE_OK, rc); + EXPECT_EQ(8, oc.keys.size()); EXPECT_EQ(8,oc.capacities.size()); + vector found_rows; + vector found_cols; + for(int i=0; i expected_rows{"some","some","some","some","sowhat","sowhat","sowhat","sowhat"}; + vector expected_cols{"thing1","thing1","thing2", "thing2","thing3","thing3","thing4", "thing4"}; + EXPECT_TRUE( (found_rows==expected_rows)); + EXPECT_TRUE( (found_cols==expected_cols)); + } + + } \ No newline at end of file diff --git a/tests/lunasa/CMakeLists.txt b/tests/lunasa/CMakeLists.txt index 63461e7..8f3c566 100644 --- a/tests/lunasa/CMakeLists.txt +++ b/tests/lunasa/CMakeLists.txt @@ -31,6 +31,7 @@ add_serial_test( tb_lunasa_performance component true ) add_serial_test( tb_lunasa_threaded_performance component true ) add_serial_test( tb_lunasa_statistics component true ) add_serial_test( tb_lunasa_data_type_registry component true ) +add_serial_test( tb_lunasa_copy_ldo component true ) if( Faodel_ENABLE_MPI_SUPPORT ) if(Faodel_NETWORK_LIBRARY STREQUAL "nnti") diff --git a/tests/lunasa/component/nnti/mpi_lunasa_nnti_put.cpp b/tests/lunasa/component/nnti/mpi_lunasa_nnti_put.cpp index cfe7953..25174a0 100644 --- a/tests/lunasa/component/nnti/mpi_lunasa_nnti_put.cpp +++ b/tests/lunasa/component/nnti/mpi_lunasa_nnti_put.cpp @@ -19,7 +19,7 @@ #include "nnti/nnti_wr.hpp" #include "nnti/nntiConfig.h" #include "nnti/transport_factory.hpp" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include #include @@ -142,7 +142,7 @@ class LunasaPutUserTest : public testing::Test { #endif send_promise.set_value(1); delete ldo; - return NNTI_EIO; + return NNTI_OK; } }; @@ -159,7 +159,7 @@ class LunasaPutUserTest : public testing::Test { << ") and context(" << (void*)context << ")" << std::endl; #endif send_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } }; @@ -258,7 +258,7 @@ class LunasaPutUserTest : public testing::Test { nnti::datatype::nnti_work_request wr(transport, base_wr, put_callback); transport->put(&wr, &wid); } - return NNTI_EIO; + return NNTI_OK; } }; @@ -346,7 +346,7 @@ class LunasaPutUserTest : public testing::Test { transport->put(&wr, &wid); recv_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } public: @@ -401,7 +401,7 @@ class LunasaPutUserTest : public testing::Test { int seed = m->header.seed; recv_promise.set_value(std::pair(length, seed)); - return NNTI_EIO; + return NNTI_OK; } public: @@ -471,8 +471,8 @@ class LunasaPutUserTest : public testing::Test { bootstrap::Init(config, lunasa::bootstrap); bootstrap::Start(); - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + nodeid = whookie::Server::GetNodeID(); transport = nnti::transports::factory::get_instance(config); transport->start(); diff --git a/tests/lunasa/component/nnti/mpi_lunasa_nnti_register_memory.cpp b/tests/lunasa/component/nnti/mpi_lunasa_nnti_register_memory.cpp index c108453..ffba00b 100644 --- a/tests/lunasa/component/nnti/mpi_lunasa_nnti_register_memory.cpp +++ b/tests/lunasa/component/nnti/mpi_lunasa_nnti_register_memory.cpp @@ -20,7 +20,7 @@ #include "nnti/nnti_callback.hpp" #include "nnti/nnti_wr.hpp" #include "nnti/transport_factory.hpp" -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; @@ -288,8 +288,8 @@ int main(int argc, char *argv[]) { lunasa::RegisterPinUnpin(RegisterMemory, UnregisterMemory); - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - faodel::nodeid_t nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + faodel::nodeid_t nodeid = whookie::Server::GetNodeID(); transport = nnti::transports::factory::get_instance(config); transport->start(); diff --git a/tests/lunasa/component/nnti/mpi_lunasa_nnti_send.cpp b/tests/lunasa/component/nnti/mpi_lunasa_nnti_send.cpp index 527b5e7..0a61728 100644 --- a/tests/lunasa/component/nnti/mpi_lunasa_nnti_send.cpp +++ b/tests/lunasa/component/nnti/mpi_lunasa_nnti_send.cpp @@ -12,7 +12,7 @@ #include "faodel-common/Configuration.hh" #include "faodel-common/NodeID.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "nnti/nnti.h" #include "nnti/nnti_logger.hpp" @@ -56,7 +56,7 @@ class LunasaSendTest : public testing::Test { << ") and context(" << (void*)context << ")" << std::endl; #endif send_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } }; @@ -105,7 +105,7 @@ class LunasaSendTest : public testing::Test { } recv_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } public: @@ -169,8 +169,8 @@ class LunasaSendTest : public testing::Test { bootstrap::Init(config, lunasa::bootstrap); bootstrap::Start(); - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + nodeid = whookie::Server::GetNodeID(); transport = nnti::transports::factory::get_instance(config); transport->start(); diff --git a/tests/lunasa/component/nnti/mpi_lunasa_nnti_send_user.cpp b/tests/lunasa/component/nnti/mpi_lunasa_nnti_send_user.cpp index 2c407c4..ebdcf27 100644 --- a/tests/lunasa/component/nnti/mpi_lunasa_nnti_send_user.cpp +++ b/tests/lunasa/component/nnti/mpi_lunasa_nnti_send_user.cpp @@ -18,7 +18,7 @@ #include "nnti/nnti_callback.hpp" #include "nnti/nnti_wr.hpp" #include "nnti/transport_factory.hpp" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include #include @@ -56,7 +56,7 @@ class LunasaSendTest : public testing::Test { << ") and context(" << (void*)context << ")" << std::endl; #endif send_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } }; @@ -105,7 +105,7 @@ class LunasaSendTest : public testing::Test { } recv_promise.set_value(1); - return NNTI_EIO; + return NNTI_OK; } public: @@ -169,8 +169,8 @@ class LunasaSendTest : public testing::Test { bootstrap::Init(config, lunasa::bootstrap); bootstrap::Start(); - assert(webhook::Server::IsRunning() && "Webhook not started before NetNnti started"); - nodeid = webhook::Server::GetNodeID(); + assert(whookie::Server::IsRunning() && "Whookie not started before NetNnti started"); + nodeid = whookie::Server::GetNodeID(); transport = nnti::transports::factory::get_instance(config); transport->start(); diff --git a/tests/lunasa/component/tb_lunasa_backburner_ldo.cpp b/tests/lunasa/component/tb_lunasa_backburner_ldo.cpp index 4efbbb3..6946f6e 100644 --- a/tests/lunasa/component/tb_lunasa_backburner_ldo.cpp +++ b/tests/lunasa/component/tb_lunasa_backburner_ldo.cpp @@ -23,7 +23,7 @@ using namespace lunasa; string default_config_string = R"EOF( #bootstrap.debug true -#webhook.debug true +#whookie.debug true #lunasa.debug true # Must use simple malloc for multiple start/stop tests diff --git a/tests/lunasa/component/tb_lunasa_copy_ldo.cpp b/tests/lunasa/component/tb_lunasa_copy_ldo.cpp new file mode 100644 index 0000000..c5982fe --- /dev/null +++ b/tests/lunasa/component/tb_lunasa_copy_ldo.cpp @@ -0,0 +1,219 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + +#include "gtest/gtest.h" + +#include + +#include "faodel-common/Common.hh" +#include "lunasa/Lunasa.hh" +#include "lunasa/DataObject.hh" + +using namespace std; +using namespace faodel; +using namespace lunasa; + +string default_config = R"EOF( +lunasa.lazy_memory_manager malloc +lunasa.eager_memory_manager malloc +)EOF"; + +static const auto MAX_THREADS = 8; + +class LunasaCopyLDOTest : public testing::Test { +protected: + void SetUp() override { + Configuration config(default_config); + config.AppendFromReferences(); + + bootstrap::Init(config, lunasa::bootstrap); + bootstrap::Start(); + } + + void TearDown() override { + bootstrap::Finish(); + } +}; + +struct TestRunner { + virtual void operator()() = 0; +}; +struct CopyAssignmentRunner : public TestRunner { + DataObject original; + + CopyAssignmentRunner(DataObject ldo) + : original(ldo) + { } + + void operator()() { + DataObject copied; + + for (int i=0;i<1000000;i++) { + copied = original; + char *dataPtr = (char*)copied.GetDataPtr(); + dataPtr[0]='6'; + } + } +}; +struct MoveAssignmentRunner : public TestRunner { + DataObject original; + + MoveAssignmentRunner(DataObject ldo) + : original(ldo) + { } + + void operator()() { + DataObject moved; + for (int i=0;i<1000000;i++) { + moved = std::move(original); + + char *dataPtr = (char*)moved.GetDataPtr(); + dataPtr[0]='6'; + + original = std::move(moved); // move it back + } + } +}; +struct CopyConstructorRunner : public TestRunner { + DataObject original; + + CopyConstructorRunner(DataObject ldo) + : original(ldo) + { } + + void operator()() { + for (int i=0;i<1000000;i++) { + DataObject copied(original); + char *dataPtr = (char*)copied.GetDataPtr(); + dataPtr[0]='6'; + } + } +}; +struct MoveConstructorRunner : public TestRunner { + DataObject original; + + MoveConstructorRunner(DataObject ldo) + : original(ldo) + { } + + void operator()() { + for (int i=0;i<1000000;i++) { + DataObject moved(std::move(original)); + + char *dataPtr = (char*)moved.GetDataPtr(); + dataPtr[0]='6'; + + original = std::move(moved); // move it back + } + } +}; +struct CopyNewDeleteRunner : public TestRunner { + struct DeleteLdo { + DataObject *ldo; + + DeleteLdo(DataObject *ldo) + : ldo(ldo) + { + } + void operator()(void) + { + delete ldo; + } + }; + + void operator()() { + for (int j=0;j<1000;j++) { + std::thread thr[MAX_THREADS]; + for (int i=0;ieq_create(128, NNTI_EQF_UNEXPECTED, &eq); - t->alloc(3200, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, func_cb, nullptr, &buf_base, &buf_hdl); + t->alloc(3200, + (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE|NNTI_BF_LOCAL_ATOMIC|NNTI_BF_REMOTE_ATOMIC), + eq, + func_cb, + nullptr, + &buf_base, + &buf_hdl); MPI_Barrier(MPI_COMM_WORLD); @@ -180,8 +186,20 @@ TEST_F(NntiAtomicOpTest, start1) { rc = t->connect(server_url[0], 1000, &peer_hdl); rc = t->eq_create(128, NNTI_EQF_UNEXPECTED, &eq); - rc = t->alloc(3200, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &buf_base, &buf_hdl); - rc = t->alloc(320, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &ack_base, &ack_hdl); + rc = t->alloc(3200, + (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE|NNTI_BF_LOCAL_ATOMIC|NNTI_BF_REMOTE_ATOMIC), + eq, + obj_cb, + nullptr, + &buf_base, + &buf_hdl); + rc = t->alloc(320, + (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), + eq, + obj_cb, + nullptr, + &ack_base, + &ack_hdl); NNTI_buffer_t target_hdl; NNTI_peer_t recv_peer; diff --git a/tests/nnti/cpp-api/CMakeLists.txt b/tests/nnti/cpp-api/CMakeLists.txt index 9295104..722fe71 100644 --- a/tests/nnti/cpp-api/CMakeLists.txt +++ b/tests/nnti/cpp-api/CMakeLists.txt @@ -17,7 +17,6 @@ add_library(cpptestutils ) set_target_properties(cpptestutils PROPERTIES LINKER_LANGUAGE CXX ) target_link_libraries( cpptestutils PUBLIC nnti ) -add_dependencies(cpptestutils generate-nnti_packable) #---------------------------------------------------------------------------- set(SERIAL_TEST_LIBS @@ -55,6 +54,7 @@ if( Faodel_ENABLE_MPI_SUPPORT ) add_mpi_test( PingPongCallbackTest . 2 true ) add_mpi_test( QueueSendTest1 . 2 true ) add_mpi_test( QueueSendTest2 . 2 true ) + add_mpi_test( RdmaLengthTest . 2 true ) add_mpi_test( RdmaOpTest . 2 true ) add_mpi_test( ShortSendTest . 2 true ) add_mpi_test( UnexpectedCallbackTest . 2 true ) @@ -63,4 +63,3 @@ if( Faodel_ENABLE_MPI_SUPPORT ) add_mpi_test( UrlPidTest . 1 true ) add_mpi_test( ZeroCopySendTest . 2 true ) endif() - diff --git a/tests/nnti/cpp-api/RdmaLengthTest.cpp b/tests/nnti/cpp-api/RdmaLengthTest.cpp new file mode 100644 index 0000000..a560f75 --- /dev/null +++ b/tests/nnti/cpp-api/RdmaLengthTest.cpp @@ -0,0 +1,266 @@ +// Copyright 2018 National Technology & Engineering Solutions of Sandia, +// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. + + +#include "nnti/nnti_pch.hpp" + +#include + +#include "gtest/gtest.h" + +#include "nnti/nntiConfig.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "nnti/nnti_logger.hpp" + +#include "nnti/nnti_util.hpp" + +#include "nnti/nnti_transport.hpp" +#include "nnti/nnti_buffer.hpp" +#include "nnti/nnti_wid.hpp" +#include "nnti/transport_factory.hpp" + +#include "test_utils.hpp" + +using namespace std; +using namespace faodel; + +string default_config_string = R"EOF( +# default to using mpi, but allow override in config file pointed to by CONFIG +nnti.transport.name mpi +)EOF"; + +//const uint32_t outer=100; +//const uint32_t inner=1000; +const uint32_t blocksize=8192; + + +class NntiRdmaOpTest : public testing::Test { +protected: + Configuration config; + + nnti::transports::transport *t=nullptr; + + int mpi_rank, mpi_size; + int root_rank; + + char server_url[1][NNTI_URL_LEN]; + const uint32_t num_servers = 1; + uint32_t num_clients; + bool i_am_server = false; + + void SetUp () override { + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + root_rank = 0; + config = Configuration(default_config_string); + config.AppendFromReferences(); + + MPI_Barrier(MPI_COMM_WORLD); + + test_setup(0, + NULL, + config, + "RdmaOpTest", + server_url, + mpi_size, + mpi_rank, + num_servers, + num_clients, + i_am_server, + t); + } + + void TearDown () override { + NNTI_result_t nnti_rc = NNTI_OK; + bool init; + + init = t->initialized(); + EXPECT_TRUE(init); + + if (init) { + nnti_rc = t->stop(); + EXPECT_EQ(nnti_rc, NNTI_OK); + } + } +}; + +TEST_F(NntiRdmaOpTest, start1) { + NNTI_result_t rc; + + NNTI_peer_t peer_hdl; + + nnti::datatype::nnti_event_callback null_cb(t); + nnti::datatype::nnti_event_callback func_cb(t, cb_func); + nnti::datatype::nnti_event_callback obj_cb(t, callback()); + + if (i_am_server) { + NNTI_event_queue_t eq; + NNTI_event_t event; + NNTI_event_t result_event; + uint32_t which; + NNTI_buffer_t buf_hdl; + char *buf_base=nullptr; + uint32_t buf_size=3200; + NNTI_work_request_t base_wr = NNTI_WR_INITIALIZER; + + rc = t->eq_create(128, NNTI_EQF_UNEXPECTED, &eq); + t->alloc(blocksize, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, func_cb, nullptr, &buf_base, &buf_hdl); + + MPI_Barrier(MPI_COMM_WORLD); + + NNTI_buffer_t target_hdl; + NNTI_buffer_t ack_hdl; + NNTI_peer_t peer_hdl; + + rc = recv_hdl(t, buf_hdl, buf_base, buf_size, &target_hdl, &peer_hdl, eq); + EXPECT_EQ(rc, NNTI_OK); + + rc = recv_hdl(t, buf_hdl, buf_base, buf_size, &ack_hdl, &peer_hdl, eq); + EXPECT_EQ(rc, NNTI_OK); + + // We start off with a test that transfers blocksize bytes, so it should PASS. + nnti::datatype::nnti_event_callback obj_cb(t, callback()); + rc = get_data_async(t, target_hdl, 0, buf_hdl, 0, blocksize, peer_hdl, obj_cb, nullptr); + EXPECT_EQ(rc, NNTI_OK); + rc = wait_data(t, eq); + EXPECT_EQ(rc, NNTI_OK); + EXPECT_TRUE(verify_buffer(buf_base, 0, blocksize, blocksize)); + + rc = populate_buffer(t, 0, blocksize, 0, buf_hdl, buf_base, blocksize); + EXPECT_EQ(rc, NNTI_OK); + + rc = put_data_async(t, buf_hdl, 0, target_hdl, 0, blocksize, peer_hdl, obj_cb, nullptr); + EXPECT_EQ(rc, NNTI_OK); + rc = wait_data(t, eq); + EXPECT_EQ(rc, NNTI_OK); + + + // Next we do a test that transfers blocksize*2 bytes. + // In a debug build, args checking should FAIL this test with NNTI_EMSGSIZE. + // In a release build, args checking is disabled so the failure could occur immediately or in an event. + rc = get_data_async(t, target_hdl, 0, buf_hdl, 0, blocksize*2, peer_hdl, obj_cb, nullptr); +#ifdef NNTI_ENABLE_ARGS_CHECKING + EXPECT_EQ(rc, NNTI_EMSGSIZE); +#else + // We can't do an EXPECT_*() test here because the failure could happen now or later in an event. +// EXPECT_EQ(rc, NNTI_OK); + if (rc == NNTI_OK) { + NNTI_event_t event; + // get_data_async() succeeded, so we need to wait for an event. + rc = wait_data(t, eq, &event); + // we expect the the wait() to succeed. + EXPECT_EQ(rc, NNTI_OK); + // we expect event.result to be !NNTI_OK. + EXPECT_NE(event.result, NNTI_OK); + // + EXPECT_TRUE(verify_buffer(buf_base, 0, blocksize, blocksize)); + } else { + // the get() failed, so we have nothing else to do here. + } +#endif + + rc = populate_buffer(t, 0, blocksize, 0, buf_hdl, buf_base, blocksize); + EXPECT_EQ(rc, NNTI_OK); + + rc = put_data_async(t, buf_hdl, 0, target_hdl, 0, blocksize*2, peer_hdl, obj_cb, nullptr); +#ifdef NNTI_ENABLE_ARGS_CHECKING + EXPECT_EQ(rc, NNTI_EMSGSIZE); +#else + // We can't do an EXPECT_*() test here because the failure could happen now or later in an event. +// EXPECT_EQ(rc, NNTI_OK); + if (rc == NNTI_OK) { + NNTI_event_t event; + // put_data_async() succeeded, so we need to wait for an event. + rc = wait_data(t, eq, &event); + // we expect the the wait() to succeed. + EXPECT_EQ(rc, NNTI_OK); + // we expect event.result to be !NNTI_OK. + EXPECT_NE(event.result, NNTI_OK); + // + EXPECT_TRUE(verify_buffer(buf_base, 0, blocksize, blocksize)); + } else { + // the put() failed, so we have nothing else to do here. + } +#endif + + rc = send_ack(t, buf_hdl, ack_hdl, peer_hdl, eq); + EXPECT_EQ(rc, NNTI_OK); + + } else { + NNTI_event_queue_t eq; + NNTI_event_t event; + NNTI_event_t result_event; + uint32_t which; + NNTI_buffer_t buf_hdl; + NNTI_buffer_t ack_hdl; + char *buf_base=nullptr; + char *ack_base=nullptr; + uint32_t buf_size=3200; + uint32_t ack_size=320; + NNTI_work_request_t base_wr = NNTI_WR_INITIALIZER; + + // give the server a chance to startup + MPI_Barrier(MPI_COMM_WORLD); + + rc = t->connect(server_url[0], 1000, &peer_hdl); + rc = t->eq_create(128, NNTI_EQF_UNEXPECTED, &eq); + rc = t->alloc(blocksize, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &buf_base, &buf_hdl); + rc = t->alloc(320, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &ack_base, &ack_hdl); + + NNTI_buffer_t target_hdl; + NNTI_peer_t recv_peer; + + rc = send_hdl(t, buf_hdl, buf_base, buf_size, peer_hdl, eq); + EXPECT_EQ(rc, NNTI_OK); + + rc = populate_buffer(t, 0, blocksize, 0, buf_hdl, buf_base, blocksize); + EXPECT_EQ(rc, NNTI_OK); + + rc = send_hdl(t, ack_hdl, ack_base, ack_size, peer_hdl, eq); + EXPECT_EQ(rc, NNTI_OK); + + rc = recv_ack(t, ack_hdl, &recv_peer, eq); + EXPECT_EQ(rc, NNTI_OK); + + EXPECT_TRUE(verify_buffer(buf_base, 0, blocksize, blocksize)); + + t->disconnect(peer_hdl); + } + + MPI_Barrier(MPI_COMM_WORLD); +} + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + int provided; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + + int mpi_rank,mpi_size; + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + EXPECT_EQ(2, mpi_size); + assert(2==mpi_size); + + int rc = RUN_ALL_TESTS(); + cout <<"Tester completed all tests.\n"; + + MPI_Barrier(MPI_COMM_WORLD); + bootstrap::Finish(); + + MPI_Finalize(); + + return (rc); +} diff --git a/tests/nnti/cpp-api/RdmaOpTest.cpp b/tests/nnti/cpp-api/RdmaOpTest.cpp index 8e8f087..9e257c1 100644 --- a/tests/nnti/cpp-api/RdmaOpTest.cpp +++ b/tests/nnti/cpp-api/RdmaOpTest.cpp @@ -41,6 +41,10 @@ string default_config_string = R"EOF( nnti.transport.name mpi )EOF"; +const uint32_t outer=10; +const uint32_t inner=100; +const uint32_t blocksize=8192; + class NntiRdmaOpTest : public testing::Test { protected: @@ -112,7 +116,7 @@ TEST_F(NntiRdmaOpTest, start1) { NNTI_work_request_t base_wr = NNTI_WR_INITIALIZER; rc = t->eq_create(128, NNTI_EQF_UNEXPECTED, &eq); - t->alloc(3200, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, func_cb, nullptr, &buf_base, &buf_hdl); + t->alloc(blocksize*inner, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, func_cb, nullptr, &buf_base, &buf_hdl); MPI_Barrier(MPI_COMM_WORLD); @@ -130,22 +134,29 @@ TEST_F(NntiRdmaOpTest, start1) { log_error("RdmaOpTest", "recv_target_hdl() failed: %d", rc); } - for (int j=0;j<100;j++) { - for (int i=0;i<10;i++) { - rc = get_data(t, target_hdl, i*320, buf_hdl, i*320, 320, peer_hdl, eq); + nnti::datatype::nnti_event_callback obj_cb(t, callback()); + for (int j=0;jconnect(server_url[0], 1000, &peer_hdl); rc = t->eq_create(128, NNTI_EQF_UNEXPECTED, &eq); - rc = t->alloc(3200, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &buf_base, &buf_hdl); + rc = t->alloc(blocksize*inner, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &buf_base, &buf_hdl); rc = t->alloc(320, (NNTI_buffer_flags_t)(NNTI_BF_LOCAL_READ|NNTI_BF_LOCAL_WRITE|NNTI_BF_REMOTE_READ|NNTI_BF_REMOTE_WRITE), eq, obj_cb, nullptr, &ack_base, &ack_hdl); NNTI_buffer_t target_hdl; @@ -180,8 +191,8 @@ TEST_F(NntiRdmaOpTest, start1) { log_error("RdmaOpTest", "send_target_hdl() failed: %d", rc); } - for (int i=0;i<10;i++) { - rc = populate_buffer(t, i, i, buf_hdl, buf_base, buf_size); + for (int i=0;idisconnect(peer_hdl); diff --git a/tests/nnti/cpp-api/test_utils.cpp b/tests/nnti/cpp-api/test_utils.cpp index 8a16fe1..b07c085 100644 --- a/tests/nnti/cpp-api/test_utils.cpp +++ b/tests/nnti/cpp-api/test_utils.cpp @@ -27,7 +27,7 @@ #include "faodel-common/Bootstrap.hh" #include "faodel-common/NodeID.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "nnti/nnti_logger.hpp" @@ -72,7 +72,7 @@ test_setup(int argc, num_clients = num_procs - num_servers; - faodel::bootstrap::Start(config, webhook::bootstrap); + faodel::bootstrap::Start(config, whookie::bootstrap); t = nnti::transports::factory::get_instance(config); @@ -114,7 +114,7 @@ test_setup(int argc, num_clients = num_procs - num_servers; - faodel::bootstrap::Start(config, webhook::bootstrap); + faodel::bootstrap::Start(config, whookie::bootstrap); t = nnti::transports::factory::get_instance(config); @@ -145,7 +145,7 @@ test_setup(int argc, config.Append(s); } - faodel::bootstrap::Start(config, webhook::bootstrap); + faodel::bootstrap::Start(config, whookie::bootstrap); t = nnti::transports::factory::get_instance(config); @@ -588,14 +588,13 @@ verify_buffer(char *buf_base, NNTI_result_t wait_data(nnti::transports::transport *t, - NNTI_event_queue_t eq) + NNTI_event_queue_t eq, + NNTI_event_t *event) { NNTI_result_t rc = NNTI_OK; - - NNTI_event_t event; uint32_t which; - rc = t->eq_wait(&eq, 1, 10000, &which, &event); + rc = t->eq_wait(&eq, 1, 10000, &which, event); if (rc != NNTI_OK) { log_error("test_utils", "eq_wait() failed: %d", rc); } @@ -603,6 +602,15 @@ wait_data(nnti::transports::transport *t, return rc; } +NNTI_result_t +wait_data(nnti::transports::transport *t, + NNTI_event_queue_t eq) +{ + NNTI_event_t event; + + return wait_data(t, eq, &event); +} + NNTI_result_t send_unexpected_async(nnti::transports::transport *t, NNTI_buffer_t hdl, @@ -682,6 +690,8 @@ send_data_async(nnti::transports::transport *t, } cleanup: + log_debug("test_utils", "send_hdl - exit"); + return rc; } @@ -776,8 +786,16 @@ send_data(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "send_data_async() failed: %d", rc); + goto cleanup; + } rc = wait_data(t, eq); + if (rc != NNTI_OK) { + log_error("test_utils", "wait_data() failed: %d", rc); + goto cleanup; + } cleanup: return rc; @@ -892,9 +910,11 @@ get_data_async(nnti::transports::transport *t, rc = t->get(&wr, &wid); if (rc != NNTI_OK) { - log_error("test_utils", "send() failed: %d", rc); + log_error("test_utils", "get() failed: %d", rc); + goto cleanup; } +cleanup: log_debug("test_utils", "get_data_async - exit"); return rc; @@ -963,9 +983,18 @@ get_data(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "get_data_async() failed: %d", rc); + goto cleanup; + } rc = wait_data(t, eq); + if (rc != NNTI_OK) { + log_error("test_utils", "wait_data() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "get_data - exit"); return rc; @@ -994,7 +1023,12 @@ get_data(nnti::transports::transport *t, eq, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "get_data() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "get_data - exit"); return rc; @@ -1078,9 +1112,11 @@ put_data_async(nnti::transports::transport *t, rc = t->put(&wr, &wid); if (rc != NNTI_OK) { - log_error("test_utils", "send() failed: %d", rc); + log_error("test_utils", "put() failed: %d", rc); + goto cleanup; } +cleanup: log_debug("test_utils", "put_data_async - exit"); return rc; @@ -1107,7 +1143,12 @@ put_data_async(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "put_data_async() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "put_data_async - exit"); return rc; @@ -1157,9 +1198,18 @@ put_data(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "put_data_async() failed: %d", rc); + goto cleanup; + } rc = wait_data(t, eq); + if (rc != NNTI_OK) { + log_error("test_utils", "wait_data() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "put_data - exit"); return rc; @@ -1243,9 +1293,11 @@ fadd_async(nnti::transports::transport *t, rc = t->atomic_fop(&wr, &wid); if (rc != NNTI_OK) { - log_error("test_utils", "send() failed: %d", rc); + log_error("test_utils", "atomic_fop() failed: %d", rc); + goto cleanup; } +cleanup: log_debug("test_utils", "fadd_async - exit"); return rc; @@ -1293,9 +1345,18 @@ fadd(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "fadd_async() failed: %d", rc); + goto cleanup; + } rc = wait_data(t, eq); + if (rc != NNTI_OK) { + log_error("test_utils", "wait_data() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "fadd - exit"); return rc; @@ -1357,9 +1418,11 @@ cswap_async(nnti::transports::transport *t, rc = t->atomic_cswap(&wr, &wid); if (rc != NNTI_OK) { - log_error("test_utils", "send() failed: %d", rc); + log_error("test_utils", "atomic_cswap() failed: %d", rc); + goto cleanup; } +cleanup: log_debug("test_utils", "cswap_async - exit"); return rc; @@ -1410,9 +1473,18 @@ cswap(nnti::transports::transport *t, peer_hdl, cb, context); + if (rc != NNTI_OK) { + log_error("test_utils", "cswap_async() failed: %d", rc); + goto cleanup; + } rc = wait_data(t, eq); + if (rc != NNTI_OK) { + log_error("test_utils", "wait_data() failed: %d", rc); + goto cleanup; + } +cleanup: log_debug("test_utils", "cswap - exit"); return rc; diff --git a/tests/nnti/cpp-api/test_utils.hpp b/tests/nnti/cpp-api/test_utils.hpp index a40e977..2417123 100644 --- a/tests/nnti/cpp-api/test_utils.hpp +++ b/tests/nnti/cpp-api/test_utils.hpp @@ -169,6 +169,10 @@ verify_buffer(char *buf_base, uint64_t buf_size); NNTI_result_t +wait_data(nnti::transports::transport *t, + NNTI_event_queue_t eq, + NNTI_event_t *event); +NNTI_result_t wait_data(nnti::transports::transport *t, NNTI_event_queue_t eq); diff --git a/tests/opbox/CMakeLists.txt b/tests/opbox/CMakeLists.txt index 646ce1d..11316f6 100644 --- a/tests/opbox/CMakeLists.txt +++ b/tests/opbox/CMakeLists.txt @@ -5,19 +5,31 @@ SET(EXTRA_TEST_LIBS "-lz -ldl") # Build the test support lib------------------------------------------------- -add_library(opbox_mpi_test_support +if( Faodel_ENABLE_MPI_SUPPORT ) + add_library(opbox_mpi_test_support component/support/Globals.hh component/support/Globals.cpp - ) -target_link_libraries( opbox_mpi_test_support - opbox - ${FaodelNetlib_TARGETS} - MPI::MPI_CXX ) -set_target_properties( opbox_mpi_test_support PROPERTIES LINKER_LANGUAGE CXX ) + ) + target_link_libraries( opbox_mpi_test_support + opbox + ${FaodelNetlib_TARGETS} + MPI::MPI_CXX ) + set_target_properties( opbox_mpi_test_support PROPERTIES LINKER_LANGUAGE CXX ) +endif() + #---------------------------------------------------------------------------- set(SERIAL_TEST_LIBS + opbox + GTest::GTest + GTest::Main + Boost::program_options + Boost::serialization + ${EXTRA_TEST_LIBS} +) + +set(MPI_TEST_LIBS opbox_mpi_test_support GTest::GTest GTest::Main @@ -25,15 +37,16 @@ set(SERIAL_TEST_LIBS Boost::serialization ${EXTRA_TEST_LIBS} ) -set(MPI_TEST_LIBS ${SERIAL_TEST_LIBS}) #--------------+---------------------------------+-----------------------------------------------+---------+ # Format: | Name | Directory | Autorun | #--------------+---------------------------------+-----------------------------------------------+---------+ -add_serial_test( tb_opbox_triggerop unit true ) + if( Faodel_ENABLE_MPI_SUPPORT ) + add_mpi_test( mpi_opbox_triggerop unit 1 true ) + add_mpi_test( mpi_opbox_net_init unit 1 true ) add_mpi_test( mpi_opbox_opargs unit 1 true ) diff --git a/tests/opbox/component/mpi_opbox_ping.cpp b/tests/opbox/component/mpi_opbox_ping.cpp index b0963b9..53dd111 100644 --- a/tests/opbox/component/mpi_opbox_ping.cpp +++ b/tests/opbox/component/mpi_opbox_ping.cpp @@ -31,9 +31,9 @@ Globals G; string default_config_string = R"EOF( # Note: node_role is defined when we determine if this is a client or a server -tester.webhook.port 1991 -rooter.webhook.port 1992 -server.webhook.port 2000 +tester.whookie.port 1991 +rooter.whookie.port 1992 +server.whookie.port 2000 dirman.root_role rooter @@ -44,7 +44,7 @@ target.dirman.write_to_file ./dirman.txt dirman.type centralized #bootstrap.debug true -#webhook.debug true +#whookie.debug true #opbox.debug true #dirman.debug true diff --git a/tests/opbox/component/support/Globals.cpp b/tests/opbox/component/support/Globals.cpp index 167bc8d..8a6a210 100644 --- a/tests/opbox/component/support/Globals.cpp +++ b/tests/opbox/component/support/Globals.cpp @@ -28,7 +28,7 @@ Globals::~Globals(){ void Globals::StartAll(int &argc, char **argv, faodel::Configuration &config){ stringstream ss; - string webhook_port; + string whookie_port; int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); diff --git a/tests/opbox/component/support/Globals.hh b/tests/opbox/component/support/Globals.hh index 1e9f389..c5eecee 100644 --- a/tests/opbox/component/support/Globals.hh +++ b/tests/opbox/component/support/Globals.hh @@ -16,7 +16,7 @@ #include "faodel-common/Common.hh" #include "opbox/OpBox.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" diff --git a/tests/opbox/ops/tb_FabConnTest.cpp b/tests/opbox/ops/tb_FabConnTest.cpp index 98477cf..2c4fcc0 100644 --- a/tests/opbox/ops/tb_FabConnTest.cpp +++ b/tests/opbox/ops/tb_FabConnTest.cpp @@ -8,7 +8,7 @@ #include "gtest/gtest.h" #include "faodel-common/Common.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "opbox/OpBox.hh" using namespace std; @@ -26,10 +26,10 @@ tester.rpc_tester_type single tester.resource_manager.type tester tester.resource_manager.path /bob tester.resource_manager.write_to_file .tester-url -tester.webhook.interfaces ipogif0,eth,lo +tester.whookie.interfaces ipogif0,eth,lo # Client: Don't use a tester, just send requests -target.webhook.interfaces ipogif0,eth,lo +target.whookie.interfaces ipogif0,eth,lo target.rpc_tester_type none target.resource_manager.path /bob/1 target.resource_manager.read_from_file .tester-url @@ -55,8 +55,8 @@ class OpboxConnectTest : public testing::Test { TEST_F(OpboxConnectTest, start1) { std::cout << "Our MPI rank is " << mpi_rank << std::endl; - nodeid myid = webhook::Server::GetMyNodeID(); - std::cout << "Our webhook server is: " << myid.GetHttpLink() << endl; + nodeid myid = whookie::Server::GetMyNodeID(); + std::cout << "Our whookie server is: " << myid.GetHttpLink() << endl; opbox::net::Attrs attrs; opbox::net::GetAttrs(attrs); diff --git a/tests/opbox/ops/tb_OpboxOpPingFabTest.cpp b/tests/opbox/ops/tb_OpboxOpPingFabTest.cpp index 37f2ef1..16a9850 100644 --- a/tests/opbox/ops/tb_OpboxOpPingFabTest.cpp +++ b/tests/opbox/ops/tb_OpboxOpPingFabTest.cpp @@ -13,11 +13,11 @@ #include #include "faodel-common/Common.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "opbox/OpBox.hh" -#include "webhook/Server.hh" -#include "webhook/client/Client.hh" +#include "whookie/Server.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" #include "opbox/ops/OpPing.hh" @@ -37,11 +37,11 @@ tester.rpc_tester_type single tester.resource_manager.type tester tester.resource_manager.path /bob tester.resource_manager.write_to_file .tester-url -tester.webhook.interfaces ipogif0,eth,lo +tester.whookie.interfaces ipogif0,eth,lo # Client: Don't use a tester, just send requests client.rpc_tester_type none -client.webhook.interfaces ipogif0,eth,lo +client.whookie.interfaces ipogif0,eth,lo client.resource_manager.path /bob/1 client.resource_manager.read_from_file .tester-url )EOF"; @@ -75,7 +75,7 @@ TEST_F(OpboxOpPingFabTest, start1) { faodel::nodeid_t myid = opbox::GetMyID(); cout << "Our nodeid is " << myid.GetHex() << endl; cout << "Our web address is: " << myid.GetHttpLink("/fab/iblookup") << endl; - rc = webhook::retrieveData(myid, "/fab/iblookup", &result); + rc = whookie::retrieveData(myid, "/fab/iblookup", &result); opbox::net::Attrs attrs; opbox::net::GetAttrs(attrs); diff --git a/tests/opbox/unit/mpi_opbox_message_helpers.cpp b/tests/opbox/unit/mpi_opbox_message_helpers.cpp index ebaeb8a..d3be0c3 100644 --- a/tests/opbox/unit/mpi_opbox_message_helpers.cpp +++ b/tests/opbox/unit/mpi_opbox_message_helpers.cpp @@ -18,7 +18,7 @@ #include "faodel-common/Common.hh" #include "faodel-common/SerializationHelpers.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" #include "opbox/OpBox.hh" diff --git a/tests/opbox/unit/mpi_opbox_triggerop.cpp b/tests/opbox/unit/mpi_opbox_triggerop.cpp index f393131..96b910a 100644 --- a/tests/opbox/unit/mpi_opbox_triggerop.cpp +++ b/tests/opbox/unit/mpi_opbox_triggerop.cpp @@ -15,7 +15,7 @@ #include "faodel-common/Common.hh" #include "faodel-common/SerializationHelpers.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" #include "lunasa/Lunasa.hh" #include "opbox/OpBox.hh" diff --git a/tests/services/CMakeLists.txt b/tests/services/CMakeLists.txt index 8858734..38ff4aa 100644 --- a/tests/services/CMakeLists.txt +++ b/tests/services/CMakeLists.txt @@ -6,7 +6,7 @@ set( SERIAL_TEST_LIBS common - webhook + whookie services GTest::GTest GTest::Main @@ -15,7 +15,7 @@ set( SERIAL_TEST_LIBS set(MPI_TEST_LIBS MPI::MPI_CXX - webhook + whookie services ${SERIAL_TEST_LIBS} ) diff --git a/tests/services/component/mpi_service_mpisyncstart.cpp b/tests/services/component/mpi_service_mpisyncstart.cpp index 3cc7481..4ad9334 100644 --- a/tests/services/component/mpi_service_mpisyncstart.cpp +++ b/tests/services/component/mpi_service_mpisyncstart.cpp @@ -20,7 +20,7 @@ #include "faodel-common/BootstrapInterface.hh" #include "faodel-common/DirectoryInfo.hh" #include "faodel-services/MPISyncStart.hh" -#include "webhook/Server.hh" +#include "whookie/Server.hh" using namespace std; @@ -29,8 +29,8 @@ using namespace faodel; -const int CMD_NEW_WEBHOOK_START = 1; //Here's a config, launch with plain webhook -const int CMD_NEW_MPISYNC_START = 2; //Here's a config, launch with webhook+mpisync +const int CMD_NEW_WHOOKIE_START = 1; //Here's a config, launch with plain whookie +const int CMD_NEW_MPISYNC_START = 2; //Here's a config, launch with whookie+mpisync const int CMD_TEARDOWN = 3; const int CMD_KILL = -1; @@ -111,11 +111,11 @@ TEST_F(BootstrapMPITest, NoConfig){ string c1 = R"EOF( dirman.type centralized #bootstrap.debug true -#webhook.debug true +#whookie.debug true )EOF"; - test_bcastConfig(CMD_NEW_WEBHOOK_START,c1); - bootstrap::Start(Configuration(c1), webhook::bootstrap); + test_bcastConfig(CMD_NEW_WHOOKIE_START,c1); + bootstrap::Start(Configuration(c1), whookie::bootstrap); } TEST_F(BootstrapMPITest, Config1){ @@ -124,11 +124,11 @@ TEST_F(BootstrapMPITest, Config1){ dirman_root_mpi 0 dirman.type centralized #bootstrap.debug true -#webhook.debug true +#whookie.debug true )EOF"; - test_bcastConfig(CMD_NEW_WEBHOOK_START, c1); - bootstrap::Start(Configuration(c1), webhook::bootstrap); + test_bcastConfig(CMD_NEW_WHOOKIE_START, c1); + bootstrap::Start(Configuration(c1), whookie::bootstrap); } @@ -138,7 +138,7 @@ TEST_F(BootstrapMPITest, MPISyncStart) { mpisyncstart.enable true #bootstrap.debug true -#webhook.debug true +#whookie.debug true #mpisyncstart.debug true )EOF"; @@ -178,7 +178,7 @@ dirman.resources_mpi[] dht:/my/double&info="single" 0-middle for (int i = 0; i < 3; i++) { urls[i] = ResourceURL(urls_mod[i]); dir_info[i] = DirectoryInfo(urls[i]); - EXPECT_EQ("dht", urls[i].resource_type); + EXPECT_EQ("dht", urls[i].Type()); EXPECT_EQ("/my", urls[i].path); } EXPECT_EQ("all", urls[0].name); @@ -191,7 +191,7 @@ dirman.resources_mpi[] dht:/my/double&info="single" 0-middle // All non-zero ranks just run in this loop, waiting for orders // that tell them what to do new next. The commands are: // -// NEW_WEBHOOK_START: Here's a config, start without mpisync +// NEW_WHOOKIE_START: Here's a config, start without mpisync // NEW_MPISYNC_START: Here's a config, start with mpisync // TEARDOWN: End of the config, finish the bootstrap // KILL: all tests are done, exit out of targetloop @@ -202,9 +202,9 @@ void targetLoop(){ do { MPI_Bcast( &msg, sizeof(msg), MPI_CHAR, 0, MPI_COMM_WORLD); switch(msg.command) { - case CMD_NEW_WEBHOOK_START: { + case CMD_NEW_WHOOKIE_START: { string cstr = string(msg.message, msg.message_length); - bootstrap::Start(Configuration(cstr), webhook::bootstrap); + bootstrap::Start(Configuration(cstr), whookie::bootstrap); break; } case CMD_NEW_MPISYNC_START: { diff --git a/tests/whookie/CMakeLists.txt b/tests/whookie/CMakeLists.txt index 3730a01..360f108 100644 --- a/tests/whookie/CMakeLists.txt +++ b/tests/whookie/CMakeLists.txt @@ -5,7 +5,7 @@ set(SERIAL_TEST_LIBS - webhook + whookie common GTest::GTest GTest::Main @@ -21,11 +21,11 @@ set(MPI_TEST_LIBS #--------------+-----------------------------+----------------------------------------+---------+ # Format: | Name | Directory | Autorun | #--------------+-----------------------------+----------------------------------------+---------+ -add_serial_test( tb_webhook_clientserver component true ) -add_serial_test( simple_webhook_example standalone false ) +add_serial_test( tb_whookie_clientserver component true ) +add_serial_test( simple_whookie_example standalone false ) if( Faodel_ENABLE_MPI_SUPPORT ) - add_mpi_test( mpi_webhook_multiprocess_threaded component 2 false ) - add_mpi_test( mpi_webhook_restart component 2 false ) + add_mpi_test( mpi_whookie_multiprocess_threaded component 2 false ) + add_mpi_test( mpi_whookie_restart component 2 false ) endif() diff --git a/tests/whookie/component/mpi_whookie_multiprocess_threaded.cpp b/tests/whookie/component/mpi_whookie_multiprocess_threaded.cpp index c8023af..f289a09 100644 --- a/tests/whookie/component/mpi_whookie_multiprocess_threaded.cpp +++ b/tests/whookie/component/mpi_whookie_multiprocess_threaded.cpp @@ -14,8 +14,8 @@ #include "faodel-common/Common.hh" -#include "webhook/Server.hh" -#include "webhook/client/Client.hh" +#include "whookie/Server.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" using namespace std; @@ -23,7 +23,7 @@ using namespace faodel; string default_config_string = R"EOF( -webhook.interfaces ipogif0,eth,lo +whookie.interfaces ipogif0,eth,lo )EOF"; @@ -54,11 +54,11 @@ class ClientServer : public testing::Test { if (mpi_rank == 0) { desired_port=1990; - //port=webhook::start(desired_port); + //port=whookie::start(desired_port); num_tests++; //Keep track so main can close out this many tests cerr<<"TODO\n"; exit(-1); - //strcpy(server_hostname, webhook::Server::hostname().c_str()); + //strcpy(server_hostname, whookie::Server::hostname().c_str()); server_port = port; } MPI_Bcast(server_hostname, 1024, MPI_BYTE, 0, MPI_COMM_WORLD); @@ -71,9 +71,9 @@ class ClientServer : public testing::Test { void TearDown() override { //TODO: ideally we'd put a stop here, but when the count goes to zero, the - // global webhook will stop all threads and close in a way that eats + // global whookie will stop all threads and close in a way that eats // the port. For now, handle - //webhook::stop(); //stop kills it for everyone + //whookie::stop(); //stop kills it for everyone } int port; int desired_port; @@ -88,7 +88,7 @@ class ClientServer : public testing::Test { TEST_F(ClientServer, Simple){ if (mpi_rank == 0) { - webhook::Server::registerHook("/test_simple", [] (const map &args, stringstream &results) { + whookie::Server::registerHook("/test_simple", [] (const map &args, stringstream &results) { //cout <<"Got op\n"; //Grab the value @@ -109,7 +109,7 @@ TEST_F(ClientServer, Simple){ for(int i=0; i<10; i++){ stringstream ss_newval; ss_newval < using namespace std; @@ -41,13 +41,13 @@ const int CMD_START=1; const int CMD_FINI=2; const int CMD_KILL=3; -string data_value="unset"; //The state variable that gets set by the "/getset_data" webhook +string data_value="unset"; //The state variable that gets set by the "/getset_data" whookie //Launch command to have servers start. We need to collect their ids void startOthers(nodeid_t *nodes) { int cmd = CMD_START; MPI_Bcast(&cmd, sizeof(cmd), MPI_CHAR, 0, MPI_COMM_WORLD); - nodeid_t my_id = webhook::Server::GetNodeID(); + nodeid_t my_id = whookie::Server::GetNodeID(); MPI_Allgather(&my_id, sizeof(faodel::nodeid_t), MPI_CHAR, nodes, sizeof(faodel::nodeid_t), MPI_CHAR, @@ -59,11 +59,11 @@ void startOthers(nodeid_t *nodes) { //Actual command to start server void startSelf(nodeid_t *nodes){ - bootstrap::Start(faodel::Configuration(default_config_string), webhook::bootstrap); - nodeid_t my_id = webhook::Server::GetNodeID(); + bootstrap::Start(faodel::Configuration(default_config_string), whookie::bootstrap); + nodeid_t my_id = whookie::Server::GetNodeID(); - //Webhook: record value if passed. Return new value - webhook::Server::registerHook("/getset_data", + //Whookie: record value if passed. Return new value + whookie::Server::registerHook("/getset_data", [] (const map &args, stringstream &results){ auto new_val = args.find("newval"); @@ -107,7 +107,7 @@ void ServerNodeLoop(int mpi_size) { -class WebhookRestartTest : public testing::Test { +class WhookieRestartTest : public testing::Test { protected: virtual void SetUp() { MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); @@ -122,8 +122,8 @@ class WebhookRestartTest : public testing::Test { delete[] nodes; } nodeid_t startHead() { - bootstrap::Start(faodel::Configuration(default_config_string), webhook::bootstrap); - return my_id = webhook::Server::GetNodeID(); + bootstrap::Start(faodel::Configuration(default_config_string), whookie::bootstrap); + return my_id = whookie::Server::GetNodeID(); } void stopHead() { bootstrap::Finish(); @@ -131,10 +131,10 @@ class WebhookRestartTest : public testing::Test { int setRemoteValue(nodeid_t node, string val, string *result){ - return webhook::retrieveData(node, "/getset_data&newval="+val, result); + return whookie::retrieveData(node, "/getset_data&newval="+val, result); } int getRemoteValue(nodeid_t node, string *result){ - return webhook::retrieveData(node, "/getset_data", result); + return whookie::retrieveData(node, "/getset_data", result); } internal_use_only_t iuo; //Shortcut to getting at node_t ctor int rc; @@ -149,7 +149,7 @@ class WebhookRestartTest : public testing::Test { -TEST_F(WebhookRestartTest, NormalStartStop) { +TEST_F(WhookieRestartTest, NormalStartStop) { string val; startHead(); @@ -189,7 +189,7 @@ TEST_F(WebhookRestartTest, NormalStartStop) { } //Write and read everything now that everyone has done a restart -TEST_F(WebhookRestartTest, AllRestart) { +TEST_F(WhookieRestartTest, AllRestart) { string val; startHead(); @@ -220,7 +220,7 @@ TEST_F(WebhookRestartTest, AllRestart) { } //Restart ourselves. Verify we can still reach servers -TEST_F(WebhookRestartTest, SelfRestart) { +TEST_F(WhookieRestartTest, SelfRestart) { string val; startHead(); @@ -253,7 +253,7 @@ TEST_F(WebhookRestartTest, SelfRestart) { } //Restart ourselves. Verify we can still reach servers -TEST_F(WebhookRestartTest, OthersRestart) { +TEST_F(WhookieRestartTest, OthersRestart) { string val; startHead(); diff --git a/tests/whookie/component/tb_whookie_clientserver.cpp b/tests/whookie/component/tb_whookie_clientserver.cpp index cf58357..1dcd5fd 100644 --- a/tests/whookie/component/tb_whookie_clientserver.cpp +++ b/tests/whookie/component/tb_whookie_clientserver.cpp @@ -11,8 +11,8 @@ #include "faodel-common/Common.hh" -#include "webhook/Server.hh" -#include "webhook/client/Client.hh" +#include "whookie/Server.hh" +#include "whookie/client/Client.hh" #include "faodel-common/QuickHTML.hh" #include "faodel-common/Bootstrap.hh" @@ -23,10 +23,10 @@ using namespace std; int num_tests=0; string default_config = R"EOF( -webhook.port 1996 +whookie.port 1996 #bootstrap.debug true -#webhook.debug true +#whookie.debug true )EOF"; @@ -36,16 +36,16 @@ class ClientServer : public testing::Test { protected: void SetUp() override { - server_node = webhook::Server::GetNodeID(); + server_node = whookie::Server::GetNodeID(); //cout < &args, stringstream &results) { + whookie::Server::registerHook("/test_simple1", [&value1] (const map &args, stringstream &results) { //cout <<"Got op\n"; //Grab the value auto new_val = args.find("newval"); @@ -77,7 +77,7 @@ TEST_F(ClientServer, Simple){ //Add a hook to let user set a variable. This one doesn't put html around it. string value2; - webhook::Server::registerHook("/test_simple2", [&value2] (const map &args, stringstream &results) { + whookie::Server::registerHook("/test_simple2", [&value2] (const map &args, stringstream &results) { //cout <<"Got op\n"; //Grab the value auto new_val = args.find("newval"); @@ -95,19 +95,19 @@ TEST_F(ClientServer, Simple){ for(int i=0; i<10; i++){ stringstream ss_newval; ss_newval < &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); - rc=webhook::Server::registerHook("/regtest1/thing1", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); - rc=webhook::Server::registerHook("/regtest1/thing2", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); - rc=webhook::Server::registerHook("/regtest2", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); + rc=whookie::Server::registerHook("/regtest1", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); + rc=whookie::Server::registerHook("/regtest1/thing1", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); + rc=whookie::Server::registerHook("/regtest1/thing2", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); + rc=whookie::Server::registerHook("/regtest2", [] (const map &args, stringstream &results) { cout <<"Got op\n"; }); EXPECT_EQ(0,rc); //Deregister some things - rc=webhook::Server::deregisterHook("/regtest1"); EXPECT_EQ(0,rc); - rc=webhook::Server::deregisterHook("/regtest1/thing1"); EXPECT_EQ(0,rc); - rc=webhook::Server::deregisterHook("/regtest1/thing2"); EXPECT_EQ(0,rc); - rc=webhook::Server::deregisterHook("/regtest2"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/regtest1"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/regtest1/thing1"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/regtest1/thing2"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/regtest2"); EXPECT_EQ(0,rc); } @@ -135,7 +135,7 @@ TEST_F(ClientServer, ReplyStream){ //Add a hook to let user set a variable string value1; - webhook::Server::registerHook("/test_replystream", [&value1] (const map &args, stringstream &results) { + whookie::Server::registerHook("/test_replystream", [&value1] (const map &args, stringstream &results) { faodel::ReplyStream rs(args, "ReplyStream", &results); auto new_val = args.find("newval"); @@ -157,7 +157,7 @@ TEST_F(ClientServer, ReplyStream){ int rc; string result; - rc = webhook::retrieveData(server_node, "/test_replystream&format=txt", &result); + rc = whookie::retrieveData(server_node, "/test_replystream&format=txt", &result); EXPECT_EQ(0,rc); string exp_string="Here is the top part of the page\n" "New Section Header\n" @@ -172,7 +172,7 @@ TEST_F(ClientServer, ReplyStream){ EXPECT_EQ(exp_string, result); - rc=webhook::Server::deregisterHook("/test_replystream"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/test_replystream"); EXPECT_EQ(0,rc); } @@ -183,7 +183,7 @@ TEST_F(ClientServer, ManyRequests){ //Add a hook to let user set a variable string value1; - webhook::Server::registerHook("/test_vals", [&value1] (const map &args, stringstream &results) { + whookie::Server::registerHook("/test_vals", [&value1] (const map &args, stringstream &results) { faodel::ReplyStream rs(args, "ReplyStream", &results); auto new_val = args.find("newval"); @@ -201,7 +201,7 @@ TEST_F(ClientServer, ManyRequests){ string test_val="test_val"; for(int i=0; i<100; i++){ - rc = webhook::retrieveData(server_node, "/test_vals&format=txt&newval="+test_val, &result); + rc = whookie::retrieveData(server_node, "/test_vals&format=txt&newval="+test_val, &result); EXPECT_EQ(0,rc); EXPECT_EQ(test_val+"\n", result); } @@ -214,7 +214,7 @@ TEST_F(ClientServer, ManyRequests){ string result; for(int j=0; j<1000; j++) { string test_val="test_"+to_string(i)+"_"+to_string(j); - int rc = webhook::retrieveData(tmp_server_node, "/test_vals&format=txt&newval="+test_val, &result); + int rc = whookie::retrieveData(tmp_server_node, "/test_vals&format=txt&newval="+test_val, &result); EXPECT_EQ(0,rc); EXPECT_EQ(test_val+"\n", result); } @@ -225,7 +225,7 @@ TEST_F(ClientServer, ManyRequests){ t.join(); } - rc=webhook::Server::deregisterHook("/test_vals"); EXPECT_EQ(0,rc); + rc=whookie::Server::deregisterHook("/test_vals"); EXPECT_EQ(0,rc); } @@ -234,9 +234,9 @@ int main(int argc, char **argv){ ::testing::InitGoogleTest(&argc, argv); - faodel::bootstrap::Start(faodel::Configuration(default_config), webhook::bootstrap ); - faodel::nodeid_t nid = webhook::Server::GetNodeID(); - cout <<"Webhook address: "< #include -#include "webhook/WebHook.hh" -#include "webhook/Server.hh" +#include "whookie/Whookie.hh" +#include "whookie/Server.hh" using namespace std; string default_config_string = R"EOF( -webhook.debug true -webhook.port 1990 -#webhook.interfaces ipogif0,eth,lo +whookie.debug true +whookie.port 1990 +#whookie.interfaces ipogif0,eth,lo )EOF"; @@ -28,19 +28,19 @@ webhook.port 1990 * @param[in] ss info */ void SayHello(){ - cout<<"Hello from webhook\n"; + cout<<"Hello from whookie\n"; } int main(int argc, char* argv[]) { - webhook::Server::registerHook("/bob", [] (const map &args, stringstream &results){ + whookie::Server::registerHook("/bob", [] (const map &args, stringstream &results){ html::mkHeader(results, "Bob's Page"); html::mkTable(results, args, "Bobs args"); html::mkFooter(results); }); - webhook::Server::registerHook("/SayHello", [] (const map &args, stringstream &results){ + whookie::Server::registerHook("/SayHello", [] (const map &args, stringstream &results){ html::mkHeader(results, "Triggering Hello"); html::mkSection(results, "Triggering Hello"); html::mkText(results, "Each time you go to this page, the executable should say hello.\n"); @@ -51,9 +51,9 @@ int main(int argc, char* argv[]) { - faodel::bootstrap::Start(faodel::Configuration(default_config_string), webhook::bootstrap); + faodel::bootstrap::Start(faodel::Configuration(default_config_string), whookie::bootstrap); - faodel::nodeid_t nid = webhook::Server::GetNodeID(); + faodel::nodeid_t nid = whookie::Server::GetNodeID(); cout<<"Simple example that starts a webserver, registers a handler, and then waits for\n" <<"some time before shutting down. When running on a local desktop, you can look\n" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt deleted file mode 100644 index f18f760..0000000 --- a/tools/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.2) - -project(faodel_tools LANGUAGES CXX C) - - -# By default, cmake looks in your ~/.cmake directory for information -# about packages. While that should be fine for most users, we disable -# it in these examples because developers often have different versions -# of libraries in different places and cmake always guesses wrong. -set( CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY TRUE ) - - -#find_package( Faodel CONFIG REQUIRED ) - -add_subdirectory( faodel-info ) -add_subdirectory( kelpie-server ) -add_subdirectory( whookie ) - diff --git a/tools/faodel-cli/CMakeLists.txt b/tools/faodel-cli/CMakeLists.txt new file mode 100644 index 0000000..0248091 --- /dev/null +++ b/tools/faodel-cli/CMakeLists.txt @@ -0,0 +1,39 @@ +set( + SOURCES + build_info.cpp + config_info.cpp + dirman_server.cpp + kelpie_client.cpp + kelpie_server.cpp + resource.cpp + whookie_client.cpp + faodel_cli.cpp + #..insert others here +) + +if( NNTI_BUILD_IBVERBS ) + set( + SOURCES + ${SOURCES} + build_info_ib.cpp +) +endif() + +add_executable( + faodel + ${SOURCES} +) +set_target_properties( + faodel + PROPERTIES LINKER_LANGUAGE CXX ) + +target_link_libraries( + faodel + kelpie + ${FaodelNetlib_TARGETS} ) + +install( + TARGETS faodel + EXPORT faodelTargets + RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin +) \ No newline at end of file diff --git a/tools/faodel-cli/build_info.cpp b/tools/faodel-cli/build_info.cpp new file mode 100644 index 0000000..d1577fa --- /dev/null +++ b/tools/faodel-cli/build_info.cpp @@ -0,0 +1,202 @@ +#include +#include +#include + +#include "lunasa/Lunasa.hh" + +#include "faodelConfig.h" +#include "faodel-common/Common.hh" +#include "opbox/OpBox.hh" +#include "whookie/Server.hh" + + +#if USE_NNTI==1 +#include "nnti/nntiConfig.h" +#endif + +#define XSTR(s) STR(s) +#define STR(s) #s + +#include "faodel_cli.hh" + +using namespace std; +using namespace faodel; + +//Local prototypes +int buildInfo(const vector &args); + + +bool dumpHelpBuild(string subcommand) { + string help_binfo[5] = { + "build-info", "binfo", "", "Display FAODEL build information", + R"( +This command provides a way for you to get information about how the faodel +libraries were built (eg, was MPI used, what version of BOOST was used, which +network transports are enabled in the communication library, etc). +)" + }; + + return dumpSpecificHelp(subcommand, help_binfo); + +} + +int checkBuildCommands(const std::string &cmd, const vector &args) { + + if((cmd == "build-info") || (cmd == "binfo")) return buildInfo(args); + + //No command + return ENOENT; +} + + +void show_found(int found, const char *target) { + fprintf(stdout, "%20s: %s\n", target, (found ? "Found" : "Not Found")); +} + +void show_found(int found, const char *target, const char *version) { + fprintf(stdout, "%20s: %s (%s)\n", target, (found ? "Found" : "Not Found"), version); +} + +void show_cmake_external_programs(void) { + fprintf(stdout, "%20s: %s (%s)\n", "compiler", XSTR(CMAKE_CXX_COMPILER_ID), XSTR(CMAKE_CXX_COMPILER_VERSION)); + if(DOXYGEN_FOUND) { + show_found(DOXYGEN_FOUND, "Doxygen", XSTR(DOXYGEN_VERSION)); + } else { + show_found(DOXYGEN_FOUND, "Doxygen"); + } + fprintf(stdout, "\n"); +} + +void show_cmake_tpls(void) { + //show_found(Kokkos_FOUND, "Kokkos"); + show_found(LIBHIO_FOUND, "libhio"); + if(Boost_FOUND) { + show_found(Boost_FOUND, "Boost", XSTR(Boost_VERSION)); + } else { + show_found(Boost_FOUND, "Boost"); + } + show_found(GTest_FOUND, "googletest"); + if(LIBFABRIC_FOUND) { + show_found(LIBFABRIC_FOUND, "libfabric", XSTR(Libfabric_pc_VERSION)); + } else { + show_found(LIBFABRIC_FOUND, "libfabric"); + } + if(UGNI_FOUND) { + show_found(UGNI_FOUND, "libugni", XSTR(UGNI_PC_VERSION)); + } else { + show_found(UGNI_FOUND, "libugni"); + } + show_found(DRC_FOUND, "CrayDRC"); + show_found(IBVerbs_FOUND, "libverbs"); + + if(MPI_FOUND) { + show_found(MPI_FOUND, "MPI", XSTR(MPI_C_VERSION)); + } else { + show_found(MPI_FOUND, "MPI"); + } + fprintf(stdout, "\n"); +} + +void show_cmake_common_config(void) { + fprintf(stdout, "Faodel Common Config\n"); + fprintf(stdout, "%20s: %s\n", "Threading Model", XSTR(Faodel_THREADING_MODEL)); + fprintf(stdout, "\n"); +} + +void show_cmake_lunasa_config(void) { + fprintf(stdout, "Lunasa Config\n"); +#if Faodel_ENABLE_TCMALLOC + fprintf(stdout, " Building with tcmalloc from gperftools\n"); +#endif + fprintf(stdout, "\n"); +} + +void show_cmake_nnti_config(void) { + fprintf(stdout, "NNTI Config\n"); + +#if USE_NNTI == 1 + +#if (NNTI_BUILD_IBVERBS) +# if (NNTI_HAVE_VERBS_EXP_H) + fprintf(stdout, " Building the IBVerbs Transport with the libverbs expanded API (mlx4 or mlx5)\n"); +# else + fprintf(stdout, " Building the IBVerbs Transport with the libverbs standard API (mlx4 ONLY)\n"); +# endif +#else +# if(NNTI_DISABLE_IBVERBS_TRANSPORT) + fprintf(stdout, " IBVerbs Transport explicitly disabled\n"); +# else + fprintf(stdout, " Not building the IBVerbs Transport\n"); +# endif +#endif + +#if (NNTI_BUILD_UGNI) + fprintf(stdout, " Building the UGNI Transport\n"); +#else +# if(NNTI_DISABLE_UGNI_TRANSPORT) + fprintf(stdout, " UGNI Transport explicitly disabled\n"); +# else + fprintf(stdout, " Not building the UGNI Transport\n"); +# endif +#endif + +#if (NNTI_BUILD_MPI) + fprintf(stdout, " Building the MPI Transport\n"); +#else +# if(NNTI_DISABLE_MPI_TRANSPORT) + fprintf(stdout, " MPI Transport explicitly disabled\n"); +# else + fprintf(stdout, " Not building the MPI Transport\n"); +# endif +#endif + +#if (NNTI_USE_XDR) + fprintf(stdout, " Using XDR for serialization\n"); +#elif (NNTI_USE_CEREAL) + fprintf(stdout, " Using Cereal for serialization\n"); +#else + fprintf(stdout, " ERROR - Couldn't find a serialization library\n"); +#endif + +#elif USE_LIBFABRIC == 1 + + fprintf(stdout, " NNTI disabled. Using libfabric instead.\n"); + +#else + + fprintf(stdout, " NNTI disabled. No network selected.\n"); + +#endif + + fprintf(stdout, "\n"); +} + +void show_cmake_opbox_config(void) { + fprintf(stdout, "Opbox Config\n"); + fprintf(stdout, "%20s: %s\n", "Network Module", XSTR(Faodel_NETWORK_LIBRARY)); + fprintf(stdout, "\n"); +} + + +#if NNTI_BUILD_IBVERBS +void ib_sanity_check(); +#endif + +int buildInfo(const vector &args) { + cout << "======================================================================" << endl; + show_cmake_external_programs(); + show_cmake_tpls(); + show_cmake_common_config(); + show_cmake_lunasa_config(); + show_cmake_nnti_config(); + show_cmake_opbox_config(); + + #if NNTI_BUILD_IBVERBS + ib_sanity_check(); + #endif + + + + cout << "======================================================================" << endl; + return 0; +} diff --git a/tools/faodel-cli/build_info_ib.cpp b/tools/faodel-cli/build_info_ib.cpp index f996571..09bbaa6 100644 --- a/tools/faodel-cli/build_info_ib.cpp +++ b/tools/faodel-cli/build_info_ib.cpp @@ -53,6 +53,7 @@ bool atomic_result_is_be(struct ibv_context *ctx) #if (NNTI_HAVE_IBV_EXP_QUERY_DEVICE && NNTI_HAVE_IBV_EXP_ATOMIC_HCA_REPLY_BE) int ibv_rc=0; struct ibv_exp_device_attr exp_dev_attr; + memset(&exp_dev_attr, 0, sizeof(exp_dev_attr)); exp_dev_attr.comp_mask = IBV_EXP_DEVICE_ATTR_RESERVED - 1; ibv_rc = ibv_exp_query_device(ctx, &exp_dev_attr); if (ibv_rc) { @@ -89,7 +90,7 @@ void ib_sanity_check() bool have_exp_qp = exp_qp(); bool have_exp_atomic_cap = exp_atomic_cap(&dev_attr); - bool byte_swap_atomic_result = atomic_result_is_be(ctx); + //bool byte_swap_atomic_result = atomic_result_is_be(ctx); cout << "========================IBVerbs Sanity Check==========================" << endl; if (have_exp_qp) { diff --git a/tools/faodel-cli/config_info.cpp b/tools/faodel-cli/config_info.cpp new file mode 100644 index 0000000..60c844d --- /dev/null +++ b/tools/faodel-cli/config_info.cpp @@ -0,0 +1,140 @@ +#include +#include +#include + +#include "faodelConfig.h" +#include "faodel-common/Configuration.hh" +#include "lunasa/Lunasa.hh" + +#include "faodel-common/Common.hh" +#include "opbox/OpBox.hh" +#include "whookie/Server.hh" + +#include "faodel_cli.hh" + + +using namespace std; +using namespace faodel; + + + +int configInfo(const vector &args); + + +bool dumpHelpConfig(string subcommand) { + string help_cinfo[5] ={ + "config-info", "cinfo", "", "Display the Configuration tools will use", + R"( +When FAODEL tools start, they will load configuration data from a file +specified by the FAODEL_CONFIG environment variable. You can set a number of +runtime parameters with this configuration (eg, debug levels, pre-defined +resources, and services that should run on specific nodes). This tool will +dump out the configuration that FAODEL will start with. +)" + }; + return dumpSpecificHelp(subcommand, help_cinfo); +} + +int checkConfigCommands(const std::string &cmd, const vector &args) { + + if((cmd == "config-info") || (cmd == "cinfo")) return configInfo(args); + + //No command + return ENOENT; +} + + +void show_Configuration() { + + faodel::Configuration config; + + std::stringstream ss; + ss << "===================Faodel Configuration Variable======================" << endl; + + string ename; + config.GetString(&ename,"config.additional_files.env_name.if_defined"); + ss << "Environment Variable Name: "<< ename< &args) { + + show_Configuration(); + + //Now try starting things up to see if the configs are usable + bootstrap::Start(Configuration(""), opbox::bootstrap); + show_Common(); + show_Whookie(); + show_Lunasa(); + bootstrap::Finish(); + + return 0; +} \ No newline at end of file diff --git a/tools/faodel-cli/dirman_server.cpp b/tools/faodel-cli/dirman_server.cpp new file mode 100644 index 0000000..3aff7d6 --- /dev/null +++ b/tools/faodel-cli/dirman_server.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include "whookie/client/Client.hh" +#include "whookie/Server.hh" +#include "kelpie/Kelpie.hh" +#include "dirman/DirMan.hh" + +#include "faodel_cli.hh" + +using namespace std; + +int startDirman(const vector &args); +int stopDirman(const vector &args); + + +bool dirman_keepgoing=true; + + + +bool dumpHelpDirman(string subcommand) { + + string help_dstart[5] = { + "dirman-start", "dstart", "", "Start a dirman server", + R"( +DirMan is a service for keeping track of what resources are available in a +system. A user typically launches one dirman server and then establishes +one or more resource pools for hosting data. This command launches a dirman +server and then waits for the user to issue a dstop command to stop it. + +In order to make it easier to find the dirman server in later commands, +dirman-start creates a file with its nodeid when it launches. By default this +file is located at ./.faodel-dirman. You can override this location by +setting the dirman.write_root.file value in your $FAODEL_CONFIG file, or +by passing the location in through the environment variable +FAODEL_DIRMAN_ROOT_NODE_FILE. + +Examples: + + # Start and generate ./.faodel-dirman + $ faodel dstart + $ export FAODEL_DIRMAN_ROOT_NODE_FILE=$(pwd)/.faodel-dirman + $ faodel fstop + + # Start and specify file + $ export FAODEL_DIRMAN_ROOT_NODE_FILE=~/.my-dirman + $ faodel dstart + $ faodel dstop + +)" + }; + + string help_dstop[5] = { + "dirman-stop", "dstop", "", "Stop a dirman server", + R"( +This command communicates with a running a dirman server and issues a command +to shut it down. Stopping a dirman server does not destroy running resources, +it just makes them undiscoverable by clients. Similar to other services, the +node id for the dirman server is loaded from a file specified by environment +variables or a configuration. The service will look for: + + - $FAODEL_DIRMAN_ROOT_NODE_FILE environment variable + - ./faodel-dirman if nothing is specified + +Examples: + # Use the default ./.faodel-dirman file + $ faodel dstop + + # Specify a different file dirman node id file + $ FAODEL_DIRMAN_ROOT_NODE_FILE=~/.my-dirman faodel dstop +)" + }; + + bool found=false; + found |= dumpSpecificHelp(subcommand, help_dstart); + found |= dumpSpecificHelp(subcommand, help_dstop); + return found; +} + +int checkDirmanCommands(const std::string &cmd, const vector &args) { + + if( (cmd == "dirman-start") || (cmd == "dstart")) return startDirman(args); + else if((cmd == "dirman-stop") || (cmd == "dstop")) return stopDirman(args); + + //No command + return ENOENT; +} + +void KillDirmanHook() { + cout<<"Dirman received shutdown request\n"; + dirman_keepgoing=false; +} + +int startDirman(const vector &args) { + + string root_write_file; //todo: parse args and stick in + + faodel::Configuration config; + + config.Append("whookie.app_name", "DirMan Centralized Server"); + config.Append("dirman.host_root", "true"); + config.Append("dirman.type", "centralized"); + + //Set logging + modifyConfigLogging(&config, {"dirman","whookie"}, {"dirman.cache.mine", "dirman.cache.others"}); + + //Dump our id to a file + if((!config.Contains("dirman.write_root.file")) || (!root_write_file.empty())) { + if(root_write_file.empty()){ + root_write_file = "./.faodel-dirman"; + } + config.Append("dirman.write_root.file", root_write_file); + } + + dirman_keepgoing = true; + + //Startup in a way that adds a shutdown hook + faodel::bootstrap::Init(config, dirman::bootstrap); + whookie::Server::registerHook("/dirman/shutdown", [] (const map &args, stringstream &results) { + KillDirmanHook(); + }); + faodel::bootstrap::Start(); + + //Wait for someone to call our shutdown service + do { + this_thread::sleep_for(chrono::seconds(1)); + } while(dirman_keepgoing); + + + faodel::bootstrap::Finish(); + return 0; +} + +int stopDirman(const vector &args) { + + string dirman_type; + faodel::Configuration config; + config.GetLowercaseString(&dirman_type, "dirman.type"); + if(dirman_type == "") { + config.Append("dirman.type", "centralized"); + } + + //Modify for debugging settings + modifyConfigLogging(&config, {"dirman", "whookie"}, {}); + + faodel::bootstrap::Start(config, dirman::bootstrap); + + faodel::nodeid_t dirman_node = dirman::GetAuthorityNode(); + + whookie::retrieveData(dirman_node, "/dirman/shutdown", nullptr); + + faodel::bootstrap::Finish(); + return 0; +} \ No newline at end of file diff --git a/tools/faodel-cli/faodel_cli.cpp b/tools/faodel-cli/faodel_cli.cpp new file mode 100644 index 0000000..b9c1bfd --- /dev/null +++ b/tools/faodel-cli/faodel_cli.cpp @@ -0,0 +1,220 @@ +#include +#include +#include +#include + +#include "faodel-common/StringHelpers.hh" +#include "dirman/DirMan.hh" +#include "kelpie/Kelpie.hh" + +#include "faodel_cli.hh" + +using namespace std; +using namespace faodel; + +//Global +int global_verbose_level=0; + + +//Helper functions +bool dumpSpecificHelp(string subcommand, const string options[5]) { + + //cout <<"Checking "<0) cout <<"I cli: "<1) cout <<"D cli: "< &basic_service_names, const vector &very_verbose_service_names) { + + for(auto s : basic_service_names) { + if(global_verbose_level>0) config->Append(s+".log.info", "true"); + if(global_verbose_level>1) config->Append(s+".debug", "true"); + } + + //Some services like dirman you can turn on lower level components, like the caches + if(global_verbose_level>2) { + for(auto s : very_verbose_service_names) { + config->Append(s + ".debug", "true"); + } + } + +} + + +int dumpHelp(string subcommand) { + cout <<"faodel COMMAND \n"; + cout <<" options:\n" + <<" -v/-V or --verbose/--very-verbose : Display runtime/debug info\n" + <<" -d id or --dirman-node id : Use hex id for dirman node (overrides env vars)\n" + <<" commands:\n"; + + bool found=false; + found |= dumpHelpBuild(subcommand); + found |= dumpHelpConfig(subcommand); + found |= dumpHelpWhookieClient(subcommand); + found |= dumpHelpDirman(subcommand); + found |= dumpHelpResource(subcommand); + found |= dumpHelpKelpieServer(subcommand); + found |= dumpHelpKelpieClient(subcommand); + + string help_help[5] = { + "help", "help", "", "Provide more info about specific commands\n", + R"( +Provide more information about specific commands. + +Example: + + faodel help dirman-start +)" + }; + + found |= dumpSpecificHelp(subcommand, help_help); + + if(!found) { + cout <<"Error: help command '"< args; + + //See if we're called from a symlink to a specific tool. These commands all have + //a filename that starts with faodel (eg faodel-binfo), so we can strip the prefix + //off the name and get the command from what's left. + string prefix="faodel-"; + string exe_name = string(basename(argv[0])); + if((exe_name != "faodel") && (faodel::StringBeginsWith(exe_name, prefix))) { + if(argc<1) return dumpHelp(""); + cmd = exe_name.erase(0, prefix.length()); + cmd = faodel::ToLowercase(cmd); + } + + //Set some default env vars so we pick up dirman info right. We may change in option parsing + setDefaultEnvVars(); + + //Extract out simple args that are common to all commands + for(int i = 1; i=argc) { + cerr <<"Error: provided -d or --dirman-node, but did not provide a node id\n"; + return -1; + } + //Change env vars so this overrides.. this does not override anything in config file + setenv("FAODEL_DIRMAN_ROOT_NODE", argv[i], 1); + unsetenv("FAODEL_DIRMAN_ROOT_NODE_FILE"); //dirman would look for this first. Remove it. + } + else if(cmd.empty()) cmd = faodel::ToLowercase(sarg); //This is our command + else args.push_back(argv[i]); //This is an arg + } + + if(cmd.empty()) { + cout<<"No command found.\n"; + return dumpHelp(""); + } + + try { + + faodel::Configuration config; + rc=ENOENT; + + //Convert starts and stops into standard service-action format + if(((cmd=="start") || (cmd=="stop")) && (!args.empty())) { + string service = faodel::ToLowercase(args[0]); + args.erase(args.begin()); + cmd = service + "-" + cmd; //eg dirman-start + } + + //Figure out which command this is and process it + if(rc==ENOENT) rc = checkBuildCommands(cmd, args); + if(rc==ENOENT) rc = checkConfigCommands(cmd, args); + if(rc==ENOENT) rc = checkWhookieClientCommands(cmd, args); + if(rc==ENOENT) rc = checkDirmanCommands(cmd, args); + if(rc==ENOENT) rc = checkResourceCommands(cmd, args); + if(rc==ENOENT) rc = checkKelpieServerCommands(cmd, args); + if(rc==ENOENT) rc = checkKelpieClientCommands(cmd, args); + + + //Help menus + if((rc==ENOENT) && (cmd=="help")){ + rc=dumpHelp( (args.empty())? "" : args[0]); //normal exit + } + + if(rc==ENOENT) { + cout<<"No valid command found..?\n"; + dumpHelp(""); //no valid command found + } + + } catch(const std::exception &e) { + cout <<"Caught std exception\n" << e.what() < +#include + +#include "faodel-common/Configuration.hh" + +extern int global_verbose_level; + + +void info(const std::string &s); +void dbg(const std::string &s); +void warn(const std::string &s); + + +// From faodel_cli.hh +bool dumpSpecificHelp(std::string subcommand, const std::string options[5]); + +void modifyConfigLogging(faodel::Configuration *config, + const std::vector &basic_service_names, + const std::vector &very_verbose_service_names); + + +// Consolidated from all the different subcommands +bool dumpHelpBuild(std::string subcommand); +bool dumpHelpConfig(std::string subcommand); +bool dumpHelpDirman(std::string subcommand); +bool dumpHelpResource(std::string subcommand); +bool dumpHelpKelpieServer(std::string subcommand); +bool dumpHelpKelpieClient(std::string subcommand); +bool dumpHelpWhookieClient(std::string subcommand); + + +int checkBuildCommands( const std::string &cmd, const std::vector &args); +int checkConfigCommands( const std::string &cmd, const std::vector &args); +int checkDirmanCommands( const std::string &cmd, const std::vector &args); +int checkResourceCommands( const std::string &cmd, const std::vector &args); +int checkKelpieServerCommands( const std::string &cmd, const std::vector &args); +int checkKelpieClientCommands( const std::string &cmd, const std::vector &args); +int checkWhookieClientCommands( const std::string &cmd, const std::vector &args); + + +#endif //FAODEL_FAODEL_CLI_HH diff --git a/tools/faodel-cli/kelpie_client.cpp b/tools/faodel-cli/kelpie_client.cpp new file mode 100644 index 0000000..c24750d --- /dev/null +++ b/tools/faodel-cli/kelpie_client.cpp @@ -0,0 +1,536 @@ +#include +#include +#include + +#include "faodel-common/StringHelpers.hh" +#include "dirman/DirMan.hh" +#include "kelpie/Kelpie.hh" + +#include "faodel_cli.hh" + +using namespace std; +using namespace faodel; + +int kelpieClientPut(const vector &args); +int kelpieClientGet(bool only_meta, const vector &args); +int kelpieClientInfo(const vector &args); +int kelpieClientList(const vector &args); + + +bool dumpHelpKelpieClient(string subcommand) { + + string help_kput[5] = { + "kelpie-put", "kput", "", "Publish data to kelpie", + R"( +kelpie-put arguments: + -p/pool pool_url : The pool to publish to (resolves w/ DirMan) + (pool may be specified in $FAODEL_POOL) + + -k1/--key1 rowname : Row part of key + -k2/--key2 colname : Optional column part of key + or + -k/--key "rowname|colname" : Specify both parts of key, separated by '|' + + -f/--file filename : Read data from file instead of stdin + -m/--meta "string" : Add optional meta data to the object + +The kelpie-put command provides a simple way to publish a data object into +a pool. A user must specify a pool and a key name for the object. If no +file argument is provided, kelpie-put will read from stdin until it +gets an EOF. This version of the command is intended for publishing a +single, contiguous object and will truncate the data if it exceeds +the kelpie.chunk_size specified in Configuration (default = 512MB). + +Examples: + + # Populate from the command line + faodel kput --pool ref:/my/dht --key bob -m "My Stuff" + type text on cmd line + here, then hit con-d con-d to end + + # Use another tool to unpack a file and pipe into an object + xzcat myfile.xz | faodel kput --pool ref:/my/dht --key1 myfile + + # Load from a file and store in row stuff, column file.txt + faodel kput --pool ref:/my/dht --file file.txt --key "stuff|file.txt" +)" + }; + string help_kget[5] = { + "kelpie-get", "kget", "", "Retrieve an item", + R"( +kelpie-get arguments: + -p/pool pool_url : The pool to retrieve from (resolves w/ DirMan) + (pool may be specified in $FAODEL_POOL) + + -k1/--key1 rowname : Row part of key + -k2/--key2 colname : Optional column part of key + or + -k/--key "rowname|colname" : Specify both parts of key, separated by '|' + + -f/--file filename : Read data from file instead of stdin + -m/--meta-only : Only display the meta data for the object + +The kelpie-get command provides a simple way to retrieve an object from a +pool. A user must specify the pool and key name for an object. If no file +argument is provided, the data will be dumped to stdout. A user may also +select the meta-only option to display only the meta data section of the +object. + +Examples: + + # Dump an object to stdout and use standard unix tools + faodel kget --pool ref:/my/dht --key mything | wc -l + + # Dump an object to file + faodel kget --pool ref:/my/dht --key "stuff|file.txt" --file file2.txt +)" + }; + + string help_kgetm[5] = { + "kelpie-get-meta", "kgetm", "", "Retrieve metadata for item", + R"( +The kelpie-get-meta command is an alias for "kelpie-get --meta-only". It +uses the same arguments as kelpie-get. +)" + }; + + string help_kinfo[5] = { + "kelpie-info", "kinfo", "", "Retrieve info for different keys", + R"( +kelpie-info arguments: + -p/pool pool_url : The pool to retrieve from (resolves w/ DirMan) + (pool may be specified in $FAODEL_POOL) + +The kelpie-info command provides users with a way to get information about +specific keys in a pool. + +Example: + + # Get sizes of different objects + faodel kinfo --pool ref:/my/dht mykey1 mykey2 "mykey3|version9" + +)" + }; + + string help_klist[5] = { + "kelpie-list", "klist", "", "Retrieve key names/sizes", + R"( +kelpie-list arguments: + -p/pool pool_url : The pool to retrieve from (resolves w/ DirMan) + (pool may be specified in $FAODEL_POOL) + +The kelpie-list command provides users with a way to learn what keys are +stored in a pool. A simple wildcard can be used to find keys that +match a specific prefix. A wildcard can be on the row, column, both, +or neither. eg + "myrow1" : show only the key named myrow1 + "myrow1|mycol1" : show only the key named myrow1|mycol1 + "myrow1|*" : show all the keys in myrow1 + "myrow*|mycol3" : show mycol3 for all myrows + +The output is a list of keys and their corresponding user lengths + +Example: + + # Get sizes of different objects + faodel klist --pool ref:/my/dht mykey1 "rowname1|col*" "row*|col*" + +)" + }; + + + bool found=false; + found |= dumpSpecificHelp(subcommand, help_kput); + found |= dumpSpecificHelp(subcommand, help_kget); + found |= dumpSpecificHelp(subcommand, help_kgetm); + found |= dumpSpecificHelp(subcommand, help_kinfo); + found |= dumpSpecificHelp(subcommand, help_klist); + return found; +} + +int checkKelpieClientCommands(const std::string &cmd, const vector &args) { + + if( (cmd == "kelpie-put") || (cmd == "kput")) return kelpieClientPut(args); + else if((cmd == "kelpie-get") || (cmd == "kget")) return kelpieClientGet(false, args); + else if((cmd == "kelpie-get-meta") || (cmd == "kgetm")) return kelpieClientGet(true, args); + else if((cmd == "kelpie-info") || (cmd == "kinfo")) return kelpieClientInfo(args); + else if((cmd == "kelpie-list") || (cmd == "klist") || (cmd=="kls")) return kelpieClientList(args); + + //No command + return ENOENT; +} + +bool getArgValue(string *result, const vector &args, size_t &i, const string &s1, const string &s2) { + + if((args[i] == s1) || (args[i] == s2)) { + i++; + if(i>=args.size()) { + cerr<<"Error parsing "< &args, + vector *remaining_args, + string *pool_name, string *file_name, + kelpie::Key *key ) { + + string key1, key2, tmp_key; + + for(size_t i = 0; i2) { + cerr << "Could not parse -k/--key argument for '" << tmp_key << "'. Must be of form 'key1' or 'key1|key2'\n"; + exit(-1); + } + key1 = keys[0]; + if(keys.size()>1) + key2 = keys[1]; + + } else { + remaining_args->push_back(args[i]); + } + + } + + //Generate a proper key + if(key1.empty()) { + cerr << "No key provided. Cannot publish.\n"; + exit(-1); + } + *key = kelpie::Key(key1, key2); + + //Pull from env var is no pool name + if(pool_name->empty()) { + char *env_pool = getenv("FAODEL_POOL"); + if(env_pool == nullptr) { + cerr << "No pool provided. Use -p/--pool resource_url or set env var FAODEL_POOL\n"; + exit(-1); + } + *pool_name = string(env_pool); + } +} + +faodel::Configuration kelpieClientStart() { + + faodel::Configuration config; + + //Make sure we're using dirman + string dirman_type; + config.GetLowercaseString(&dirman_type, "dirman.type"); + if(dirman_type == "") config.Append("dirman.type", "centralized"); + + //Modify for debugging settings + modifyConfigLogging(&config, + {"kelpie", "whookie"}, + {"opbox", "dirman"}); + + config.AppendFromReferences(); + + faodel::bootstrap::Start(config, kelpie::bootstrap); + + return config; +} + +int kelpieClientPut(const vector &args) { + + string pool_name, file_name, meta; + kelpie::Key key; + vector custom_args; + + //Pull out basic args + kelpieClientParseBasicArgs(args, &custom_args, &pool_name, &file_name, &key); + + //Pull out put-specific args + for(size_t i=0; i=64*1024){ + cerr <<"Meta sectionn must be less than 16KB\n"; + exit(-1); + } + uint16_t meta_capacity = meta.size() & 0x0FFFF; + + + if(!file_name.empty()) { + + ifstream f; + f.open(file_name, ios::in|ios::binary); + if(!f.is_open()) { + cerr <<"Could not open file "<= 4*1024*1024*1024LL)){ + cerr <<"File "<(), ldo.GetDataSize()); + + kelpie::kv_row_info_t row; + kelpie::kv_col_info_t col; + + pool.Publish(key, ldo, &row, &col); + //cout <<"Publish row col info is "<(); + size_t len; + while((len = fread( &dptr[offset], 1, MAX_CAPACITY - offset, stdin )) > 0) { + //cout <<"Pulled in chunk of len "< &args) { + + string pool_name, file_name, meta; + kelpie::Key key; + vector custom_args; + + //Pull out basic args + kelpieClientParseBasicArgs(args, &custom_args, &pool_name, &file_name, &key); + + //Pull out put-specific args + for(size_t i=0; i() : ldo.GetDataPtr(); + len = (only_meta) ? ldo.GetMetaSize() : ldo.GetDataSize(); + + if(!file_name.empty()) { + ofstream f; + f.open(file_name, ios::out | ios::app | ios::binary); + if(!f.is_open()) { + cerr <<"Problem opening file "< &args) { + + + string tmp_key; + vector keys; + string pool_name; + + int max_key_len; + for(size_t i = 0; i2) { + cerr << "Could not parse -k/--key argument for '" << tmp_key << "'. Must be of form 'key1' or 'key1|key2'\n"; + exit(-1); + } + string k1, k2; + k1=key_parts[0]; + k2=(key_parts.size()>1) ? key_parts[1] : ""; + kelpie::Key key(k1,k2); + int len = key.str().size(); + if(len>max_key_len) max_key_len = len; + keys.push_back(key); + } + } + + //Pull from env var is no pool name + if(pool_name.empty()) { + char *env_pool = getenv("FAODEL_POOL"); + if(env_pool == nullptr) { + cerr << "No pool provided. Use -p/--pool resource_url or set env var FAODEL_POOL\n"; + exit(-1); + } + pool_name = string(env_pool); + } + + auto pp=ResourceURL(pool_name); + + auto config = kelpieClientStart(); + auto pool = kelpie::Connect(pool_name); + + for(auto key : keys) { + kelpie::kv_col_info_t col_info; + rc_t rc = pool.Info(key, &col_info); + cout < &args) { + + + string tmp_key; + vector keys; + string pool_name; + + for(size_t i = 0; i2) { + cerr << "Could not parse -k/--key argument for '" << tmp_key << "'. Must be of form 'key1' or 'key1|key2'\n"; + exit(-1); + } + string k1, k2; + k1=key_parts[0]; + k2=(key_parts.size()>1) ? key_parts[1] : ""; + kelpie::Key key(k1,k2); + keys.push_back(key); + } + } + + //Pull from env var is no pool name + if(pool_name.empty()) { + char *env_pool = getenv("FAODEL_POOL"); + if(env_pool == nullptr) { + cerr << "No pool provided. Use -p/--pool resource_url or set env var FAODEL_POOL\n"; + exit(-1); + } + pool_name = string(env_pool); + } + + auto pp=ResourceURL(pool_name); + + auto config = kelpieClientStart(); + auto pool = kelpie::Connect(pool_name); + + //Get all kets and append to one OC + kelpie::ObjectCapacities oc; + for(auto key : keys) { + pool.List(key, &oc); + } + + int max_key_len=0; + for(size_t i=0; imax_key_len) + max_key_len = len; + } + + for(size_t i=0; i +#include +#include +#include + +#include "whookie/client/Client.hh" +#include "whookie/Server.hh" +#include "kelpie/Kelpie.hh" +#include "dirman/DirMan.hh" + +#include "faodel_cli.hh" + +using namespace std; +using namespace faodel; + +int startKelpieServer(const vector &args); +int stopKelpieServer(const vector &args); + + +bool kelpie_keepgoing=true; +int kelpie_local_num_pools=0; + + + +bool dumpHelpKelpieServer(string subcommand) { + + string help_kstart[5] = { + "kelpie-start", "kstart", "", "Start a kelpie server", + R"( +After defining resource pools with the rdef command, users will need to start +nodes to run kelpie servers that can join as nodes in the pool. When launching +a kelpie server, a user specifies a list of all the pool urls that the server +will join. Internally, a server locates dirman and issues a Join command to +volunteer to be a part of the pool. The server will continue to run until +the user issues a kstop command for all of the pools that a sever initially +was configured to join. + +Example: + + # Start and generate ./.faodel-dirman + $ faodel dstart + $ export FAODEL_DIRMAN_ROOT_NODE_FILE=$(pwd)/.faodel-dirman + + # Define a pool with two members + $ faodel rdef "dht:/my/dht&min_members=2" + $ faodel kstart /my/dht & + $ faodel kstart /my/dht & + + # Stop the pool + $ faodel kstop /my/dht + +)" + }; + string help_kstop[5] = { + "kelpie-stop", "kstop", "", "Stop a kelpie server", + R"( +The kstop tool can be used to shut down a resource pool, which may terminate +one or more kelpie servers. Internally kstop talks to dirman to locate info +about each of the pools the user listed. It will drop each pool from dirman +to prevent new nodes from seeing it, and then issue a request to shutdown +each node in the pool. Each server keeps track of the number of pools it +belongs to, and will terminate when the count becomes zero. + +Note: Stopping a server does not propagate to clients. If you shutdown a + server that clients are using, it is likely the clients will crash. + +Example: + + # Start and generate ./.faodel-dirman + $ faodel dstart & + $ export FAODEL_DIRMAN_ROOT_NODE_FILE=$(pwd)/.faodel-dirman + + # Define a pool with two members + $ faodel rdef "dht:/my/dht&min_members=2" + + # Launch nodes to serve in the pool + $ faodel kstart /my/dht & + $ faodel kstart /my/dht & + + # Stop the pool + $ faodel kstop /my/dht + +)" + }; + + bool found=false; + found |= dumpSpecificHelp(subcommand, help_kstart); + found |= dumpSpecificHelp(subcommand, help_kstop); + return found; +} + +int checkKelpieServerCommands(const std::string &cmd, const vector &args) { + + if( (cmd == "kelpie-start") || (cmd == "kstart")) return startKelpieServer(args); + else if((cmd == "kelpie-stop") || (cmd == "kstop")) return stopKelpieServer(args); + + //No command + return ENOENT; +} + +void KillKelpieHook() { + info("Kelpie received shutdown request"); + kelpie_keepgoing=false; +} + +int startKelpieServer(const vector &args) { + + faodel::Configuration config; + + //Make sure we're using dirman + string dirman_type; + config.GetLowercaseString(&dirman_type, "dirman.type"); + if(dirman_type == "") config.Append("dirman.type", "centralized"); + + config.Append("whookie.app_name", "Kelpie Pool Server"); + + //Modify for debugging settings + modifyConfigLogging(&config, + {"kelpie", "whookie"}, + {"opbox", "dirman"}); + + + kelpie_keepgoing = true; + + //Startup in a way that adds a shutdown hook + faodel::bootstrap::Init(config, kelpie::bootstrap); + whookie::Server::registerHook("/kelpie/shutdown", [] (const map &args, stringstream &results) { + KillKelpieHook(); + }); + faodel::bootstrap::Start(); + + + //Join any resource the user has supplied + for(auto p : args) { + faodel::DirectoryInfo dir; + faodel::ResourceURL url; + try { + url = ResourceURL(p); + + bool ok = dirman::JoinDirWithoutName(url, &dir); + if(ok) { + kelpie_local_num_pools++; + info("Joined pool "+ url.GetFullURL()); + } else { + info("Did not join pool "+url.GetFullURL()); + } + + } catch(const std::exception &e) { + warn("Could not parse url '"+p+"'. Ignoring"); + } + } + + //Wait for someone to call our shutdown service + do { + this_thread::sleep_for(chrono::seconds(1)); + } while(kelpie_keepgoing); + + + faodel::bootstrap::Finish(); + return 0; +} + +int stopKelpieServer(const vector &args) { + + faodel::Configuration config; + + //Make sure we're using dirman + string dirman_type; + config.GetLowercaseString(&dirman_type, "dirman.type"); + if(dirman_type == "") config.Append("dirman.type", "centralized"); + + + faodel::bootstrap::Start(config, kelpie::bootstrap); + + //Locate each resource and instruct all of its nodes to drop by one + for(auto p : args) { + faodel::DirectoryInfo dir; + faodel::ResourceURL url; + try { + url = ResourceURL(p); + + bool ok = dirman::GetDirectoryInfo(url, &dir); + dirman::DropDir(url); //Remove so others don't use + + if(ok) { + for(auto name_node : dir.members ) { + whookie::retrieveData(name_node.node, "/kelpie/shutdown", nullptr); + } + } + + } catch(const std::exception &e) { + warn("Could not parse url '"+p+"'. Ignoring"); + } + } + + + faodel::bootstrap::Finish(); + return 0; +} \ No newline at end of file diff --git a/tools/faodel-cli/resource.cpp b/tools/faodel-cli/resource.cpp new file mode 100644 index 0000000..09da17b --- /dev/null +++ b/tools/faodel-cli/resource.cpp @@ -0,0 +1,337 @@ +#include +#include "faodel-common/StringHelpers.hh" +#include "dirman/DirMan.hh" +#include "kelpie/Kelpie.hh" + +#include "faodel_cli.hh" + +using namespace std; +using namespace faodel; + +int resourceList(const vector &args); +int resourceListRecursive(const vector &args); +int resourceDefine(const vector &args); +int resourceDrop(const vector &args); +int resourceKill(const vector &args); + + +bool dumpHelpResource(string subcommand) { + + string help_rlist[5] = { + "resource-list", "rlist", "", "Retrieve list of known resource names", + R"( +Connect to dirman and get current directory info for one or more resources. + +Example: + + faodel rlist /my/resource1 /my/resource2 +)" + }; + + string help_rdef[5] = { + "resource-define","rdef", "", "Define new resource", + R"( +This command connects to dirman and instructs it to define the resources +specified by urls. Defining a resource is the first step in creating a +resource, and should be thought of as a way to specify parameters for a +resource as opposed to the actual nodes that are part of the resource. A URL +should include the type, path, name, and paramters for the resource (eg +minimum number of nodes or iom names). + +Example: + + faodel rdef "dht:/my/dht1&min_members=4" + faodel rdef "dht:/my/dht2&min_members=3&behavior=defaultlocaliom&iom=io1 + +Behaviors let you control how values are cached at different stages in the +pipeline. You can supply a list of '_' separated values together in the url. +current behaviors are: + + Individual level controls: + writetolocal, writetoremote, writetoiom : publish goes to local/remote/iom + readtolocal, readtoremote : want/need cached at local/remote + + Common aggregations + writearound : publishes only to the iom (no caching) + writeall : publishes to all layers + readtonone : don't cache at local or remote node + + defaultiom : writetoiom_readtonone + defaultlocaliom : writetoiom_readtonone + defaultremoteiom : writetoiom_readtoremote + defaultcachingiom : writetoall_readtolocal_readtoremote + +)" + }; + + string help_rdrop[5] = { + "resource-drop", "rdrop", "", "Remove references to resources in dirman", + R"( +This command instructs dirman to remove references to resources specified by +one or more urls. This command ONLY removes references on the dirman server +and does NOT invalidate the info in existing clients. Nodes that are part of a +resource will continue to run. + +Example: + + faodel rdrop /my/dht1 +)" + }; + +#if 0 + string help_rkill[5] = { + "resource-kill", "rkill", "", "Shutdown resources (drop+kill)", + R"( +NOT IMPLEMENTED YET: This feature is not yet implemented + +This command instructs dirman to remove references to one or more resources +specified by urls (similar to rdrop). It will also connect to the nodes in the +resource and instruct them to shutdown. + +Example: + + faodel rkill /my/dht1 +)" + }; +#endif + + + bool found=false; + found |= dumpSpecificHelp(subcommand, help_rlist); + found |= dumpSpecificHelp(subcommand, help_rdef); + found |= dumpSpecificHelp(subcommand, help_rdrop); + //found |= dumpSpecificHelp(subcommand, help_rkill); + return found; +} + +int checkResourceCommands(const std::string &cmd, const vector &args) { + + if( (cmd == "resource-list") || (cmd == "rlist")) return resourceList(args); + else if((cmd == "resource-listr") || (cmd == "rlistr")) return resourceListRecursive(args); + else if((cmd == "resource-define") || (cmd == "rdef")) return resourceDefine(args); + else if((cmd == "resource-drop") || (cmd == "rdrop")) return resourceDrop(args); + //else if((cmd == "resource-kill") || (cmd == "rkill")) return resourceKill(args); + + //No command + return ENOENT; +} + + +void resourceInit(faodel::Configuration *config) { + + string dirman_type; + config->GetLowercaseString(&dirman_type, "dirman.type"); + if(dirman_type == "") { + config->Append("dirman.type", "centralized"); + } + + modifyConfigLogging(config, {"dirman"}, {"dirman.cache.mine", "dirman.cache.others"}); + + faodel::bootstrap::Start(*config, dirman::bootstrap); + +} + +void resourceFinish() { + if(faodel::bootstrap::IsStarted()) { + faodel::bootstrap::Finish(); + } +} + + +int resourceList(const vector &args) { + + auto paths = args; + if(paths.size()==0) paths.push_back("/"); + + faodel::Configuration config; + resourceInit(&config); + + int rc=0; + bool ok; + string dir_path; + + for(auto p : paths) { + DirectoryInfo dir; + ResourceURL url; + try { + + url = ResourceURL(p); + ok = dirman::GetDirectoryInfo(url, &dir); + if(ok) { + cout<<"Located: "< &args) { + + auto paths = args; + if(paths.size()==0) paths.push_back("/"); //Show default root + + faodel::Configuration config; + resourceInit(&config); + + int rc=0; + bool ok; + string dir_path; + + map results; + size_t max_slen=0; + + while(!paths.empty()) { + + //Pop front + string p=paths[0]; + paths.erase(paths.begin()); + + //Skip any we've already looked up + if(results.find(p) != results.end()) continue; + + DirectoryInfo dir; + ResourceURL url; + + try { + url = ResourceURL(p); + dir = DirectoryInfo(); + ok = dirman::GetDirectoryInfo(url, &dir); + if(ok) { + + //Store the answer + results[p] = dir.url.GetFullURL(); + + //Generate all the kids + string base = dir.url.GetBucketPathName(); + if(dir.url.IsRoot()) base.pop_back(); //So we can just add "/child" + + for(auto child : dir.members) { + string target = base+"/"+child.name; + if(target.size()>max_slen) max_slen = target.size(); + paths.push_back(target); + } + + } else { + warn("Missing: '"+p+"'"); + //rc=-1; //Missing isn't necessrily a failure. + } + } catch(const std::exception &e) { + warn("Could not parse '"+p+"'"); + rc=-1; + } + } + + //Dump out all results + for(auto &path : results) + cout << setw(max_slen) << std::left << path.first<<" : " << path.second< &args) { + int rc=0; + bool ok; + if(args.empty()){ + cout <<"No resources provided. Done.\n"; + return 0; + } + + faodel::Configuration config; + resourceInit(&config); + + for(auto r : args) { + + ResourceURL url; + try { + url = ResourceURL(r); + + //Sanity check + string behaviors=url.GetOption("behavior"); + if(!behaviors.empty()) { + kelpie::PoolBehavior::ParseString(behaviors); + } + + //Issue the definition + ok = dirman::DefineNewDir(url); + cout <<"Resource '"< &args) { + + int rc=0; + bool ok; + faodel::Configuration config; + resourceInit(&config); + + for(auto r : args) { + + ResourceURL url; + try { + url = ResourceURL(r); + ok = dirman::DropDir(url); + info("Drop issued for: '"+url.GetFullURL()+"'"); + if(!ok) rc=-1; + + } catch(const std::exception &e) { + warn("Resource '"+r+"' was not a valud url"); + rc=-1; + } + } + + + resourceFinish(); + return rc; +} + +//Note: Original plan was to do a drop and then send kills to pool members +// This is a combination of kstop and rdrop now. +int resourceKill(const vector &args) { + int rc=0; + KTODO("resourceKill"); + return rc; +} diff --git a/tools/faodel-cli/whookie_client.cpp b/tools/faodel-cli/whookie_client.cpp new file mode 100644 index 0000000..0028304 --- /dev/null +++ b/tools/faodel-cli/whookie_client.cpp @@ -0,0 +1,120 @@ +#include + +#include "whookie/client/Client.hh" + +#include "faodel_cli.hh" + +using namespace std; +using namespace whookie; + +int whookieClientGet(const vector &args); + + +bool dumpHelpWhookieClient(string subcommand) { + string help_wget[5] = { + "whookie-get", "wget", "", "Retrieve a faodel service webpage", + R"( +whookie-get arguments: + -h/--html : Return the data in html format + -t/--text : Return the page in plain text + url : the url to fetch + +The whookie-get command provides a way for you to issue queries to a faodel +application's whookie server and get responses back. It is meant to provide a +simple command-line web client (like wget or curl) on platforms where these +tools aren't available or a proxy gets it the way. By default it issues requests +with the 'text' format enabled to make it easier to parse results. +)" + }; + + bool found=false; + found |= dumpSpecificHelp(subcommand, help_wget); + return found; +} + +int checkWhookieClientCommands(const std::string &cmd, const vector &args) { + if( (cmd == "whookie-get") || (cmd=="wget")) return whookieClientGet(args); + return ENOENT; +} + +void whookieClientParseBasicArgs(const vector &args, + string *url_string, + string *format) { + + *url_string =""; + + for(size_t i=0; i &args) { + + string mode, url; + string format="text"; + + whookieClientParseBasicArgs(args, &url, &format); + + if(url.compare(0,7,"http://") || (url.size()<8)) { + cerr<<"URL must begin with 'http://'. Received '"<1){ + //valid port and a / + port=plain_url.substr(pos1+1,pos2-1); + pos1+=pos2; + } else { + //invalid port and a / + pos1+=pos2; + } + } + if(pos1!=string::npos){ + path=plain_url.substr(pos1); + } + + path += "&format="+format; + + string data; + whookie::retrieveData(host, port, path, &data); + cout < -#include - -#include "faodelConfig.h" - -#if USE_NNTI==1 -#include "nnti/nntiConfig.h" -#endif - -#define XSTR(s) STR(s) -#define STR(s) #s - -using namespace std; - - -void show_found(int found, const char *target) -{ - fprintf(stdout, "%20s: %s\n", target, (found ? "Found" : "Not Found")); -} -void show_found(int found, const char *target, const char *version) -{ - fprintf(stdout, "%20s: %s (%s)\n", target, (found ? "Found" : "Not Found"), version); -} -void show_cmake_external_programs(void) -{ - fprintf(stdout, "%20s: %s (%s)\n", "compiler", XSTR(CMAKE_CXX_COMPILER_ID), XSTR(CMAKE_CXX_COMPILER_VERSION)); - if (DOXYGEN_FOUND) { - show_found(DOXYGEN_FOUND, "Doxygen", XSTR(DOXYGEN_VERSION)); - } else { - show_found(DOXYGEN_FOUND, "Doxygen"); - } - fprintf(stdout, "\n"); -} -void show_cmake_tpls(void) -{ - //show_found(Kokkos_FOUND, "Kokkos"); - show_found(LIBHIO_FOUND, "libhio"); - if (Boost_FOUND) { - show_found(Boost_FOUND, "Boost", XSTR(Boost_VERSION)); - } else { - show_found(Boost_FOUND, "Boost"); - } - show_found(GTest_FOUND, "googletest"); - if (LIBFABRIC_FOUND) { - show_found(LIBFABRIC_FOUND, "libfabric", XSTR(Libfabric_pc_VERSION)); - } else { - show_found(LIBFABRIC_FOUND, "libfabric"); - } - if (UGNI_FOUND) { - show_found(UGNI_FOUND, "libugni", XSTR(UGNI_PC_VERSION)); - } else { - show_found(UGNI_FOUND, "libugni"); - } - show_found(DRC_FOUND, "CrayDRC"); - show_found(IBVerbs_FOUND, "libverbs"); - - if (MPI_FOUND) { - show_found(MPI_FOUND, "MPI", XSTR(MPI_C_VERSION)); - } else { - show_found(MPI_FOUND, "MPI"); - } - fprintf(stdout, "\n"); -} -void show_cmake_common_config(void) -{ - fprintf(stdout, "Faodel Common Config\n"); - fprintf(stdout, "%20s: %s\n", "Threading Model", XSTR(Faodel_THREADING_MODEL)); - fprintf(stdout, "\n"); -} -void show_cmake_lunasa_config(void) -{ - fprintf(stdout, "Lunasa Config\n"); -#if Faodel_ENABLE_TCMALLOC - fprintf(stdout, " Building with tcmalloc from gperftools\n"); -#endif - fprintf(stdout, "\n"); -} -void show_cmake_nnti_config(void) -{ - fprintf(stdout, "NNTI Config\n"); - -#if USE_NNTI==1 - -#if (NNTI_BUILD_IBVERBS) -# if (NNTI_HAVE_VERBS_EXP_H) - fprintf(stdout, " Building the IBVerbs Transport with the libverbs expanded API (mlx4 or mlx5)\n"); -# else - fprintf(stdout, " Building the IBVerbs Transport with the libverbs standard API (mlx4 ONLY)\n"); -# endif -#else -# if(NNTI_DISABLE_IBVERBS_TRANSPORT) - fprintf(stdout, " IBVerbs Transport explicitly disabled\n"); -# else - fprintf(stdout, " Not building the IBVerbs Transport\n"); -# endif -#endif - -#if (NNTI_BUILD_UGNI) - fprintf(stdout, " Building the UGNI Transport\n"); -#else -# if(NNTI_DISABLE_UGNI_TRANSPORT) - fprintf(stdout, " UGNI Transport explicitly disabled\n"); -# else - fprintf(stdout, " Not building the UGNI Transport\n"); -# endif -#endif - -#if (NNTI_BUILD_MPI) - fprintf(stdout, " Building the MPI Transport\n"); -#else -# if(NNTI_DISABLE_MPI_TRANSPORT) - fprintf(stdout, " MPI Transport explicitly disabled\n"); -# else - fprintf(stdout, " Not building the MPI Transport\n"); -# endif -#endif - -#elif USE_LIBFABRIC==1 - - fprintf(stdout, " NNTI disabled. Using libfabric instead.\n"); - -#else - - fprintf(stdout, " NNTI disabled. No network selected.\n"); - -#endif - - fprintf(stdout, "\n"); -} -void show_cmake_opbox_config(void) -{ - fprintf(stdout, "Opbox Config\n"); - fprintf(stdout, "%20s: %s\n", "Network Module", XSTR(Faodel_NETWORK_LIBRARY)); - fprintf(stdout, "\n"); -} -void show_cmake_config(void) -{ - cout << "======================================================================" << endl; - show_cmake_external_programs(); - show_cmake_tpls(); - show_cmake_common_config(); - show_cmake_lunasa_config(); - show_cmake_nnti_config(); - show_cmake_opbox_config(); - cout << "======================================================================" << endl; -} diff --git a/tools/faodel-info/faodel_info.cpp b/tools/faodel-info/faodel_info.cpp deleted file mode 100644 index 540203e..0000000 --- a/tools/faodel-info/faodel_info.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include -#include -#include - -#include "faodelConfig.h" -#include "faodel-common/Common.hh" - -#include "opbox/OpBox.hh" -#include "webhook/Server.hh" - - -void show_cmake_config(void); -void ib_sanity_check(void); - - -using namespace std; -using namespace faodel; - - - -bool verbose = false; - -void warn(string s){ - cerr << "\033[1;31m" << "Warning:" << ":\033[0m "<1) && (string(argv[1])=="-v")) verbose=true; - show_cmake_config(); - show_runtime(); - - return 0; -} diff --git a/tools/kelpie-server/CMakeLists.txt b/tools/kelpie-server/CMakeLists.txt index d05337b..2ab7879 100644 --- a/tools/kelpie-server/CMakeLists.txt +++ b/tools/kelpie-server/CMakeLists.txt @@ -1,4 +1,6 @@ -if( Faodel_ENABLE_MPI_SUPPORT ) + +if(Faodel_ENABLE_MPI_SUPPORT) + set( SOURCES kelpie_server_main.cpp @@ -9,8 +11,11 @@ if( Faodel_ENABLE_MPI_SUPPORT ) target_link_libraries( kelpie-server kelpie) #Faodel::kelpie ) - install(TARGETS kelpie-server - EXPORT faodelTargets - RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin - ) -endif() + install( + TARGETS kelpie-server + EXPORT faodelTargets + RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin + ) + + +endif(Faodel_ENABLE_MPI_SUPPORT) diff --git a/tools/whookie/CMakeLists.txt b/tools/whookie/CMakeLists.txt deleted file mode 100644 index d7d32cd..0000000 --- a/tools/whookie/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ - -set( - SOURCES - whookie.cpp -) - -add_executable( whookie ${SOURCES} ) -set_target_properties( whookie PROPERTIES LINKER_LANGUAGE CXX ) - -target_link_libraries( whookie webhook common) #Faodel::kelpie ) - -install(TARGETS whookie - EXPORT faodelTargets - RUNTIME DESTINATION "${BINARY_INSTALL_DIR}" COMPONENT bin - ) \ No newline at end of file diff --git a/tools/whookie/whookie.cpp b/tools/whookie/whookie.cpp deleted file mode 100644 index 77fbd18..0000000 --- a/tools/whookie/whookie.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This is a simple client tool for doing web queries to a FAODEL-based -// service. It's only intended to serve as a simple query tool when -// curl or wget are not available. - -#include - -#include "webhook/client/Client.hh" - -using namespace std; - -int parseURL(const string &url, string &host, string &port, string &path){ - - port="80"; - host="localhost"; - path="/"; - - - if((url.compare(0,7,"http://")||(url.size()<8))){ - cout<<"URL must begin with 'http://'\n"; - return 1; - } - string plain_url = url.substr(7); - size_t pos1=plain_url.find_first_of(":/"); - if(pos1 == string::npos){ - //No markers, assume it's the host - host=plain_url; - } else { - host=plain_url.substr(0,pos1); - if(plain_url.at(pos1)==':'){ - size_t pos2 = plain_url.substr(pos1).find_first_of("/"); - - if(pos2==string::npos){ - //No / - if(pos1+11){ - //valid port and a / - port=plain_url.substr(pos1+1,pos2-1); - pos1+=pos2; - } else { - //invalid port and a / - pos1+=pos2; - } - } - if(pos1!=string::npos){ - path=plain_url.substr(pos1); - } - } - return 0; -} - -int main(int argc, char **argv){ - - if(argc!=2){ - cout << "usage: whookie \n"; - return 1; - } - string host,port,path; - - parseURL(string(argv[1]), host, port,path); - - string data; - webhook::retrieveData(host, port, path, &data); - - - cout << host< +) +install(TARGETS cereal + EXPORT FaodelTargets + DESTINATION lib) # ignored diff --git a/tpl/cereal/LICENSE b/tpl/cereal/LICENSE new file mode 100644 index 0000000..acc4fd8 --- /dev/null +++ b/tpl/cereal/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2014, Randolph Voorhies, Shane Grant +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tpl/cereal/README.md b/tpl/cereal/README.md new file mode 100644 index 0000000..1001f50 --- /dev/null +++ b/tpl/cereal/README.md @@ -0,0 +1,85 @@ +cereal - A C++11 library for serialization +========================================== + +

cereal is a header-only C++11 serialization library. cereal takes arbitrary data types and reversibly turns them into different representations, such as compact binary encodings, XML, or JSON. cereal was designed to be fast, light-weight, and easy to extend - it has no external dependencies and can be easily bundled with other code or used standalone.

+ +### cereal has great documentation + +Looking for more information on how cereal works and its documentation? Visit [cereal's web page](http://USCiLab.github.com/cereal) to get the latest information. + +### cereal is easy to use + +Installation and use of of cereal is fully documented on the [main web page](http://USCiLab.github.com/cereal), but this is a quick and dirty version: + +* Download cereal and place the headers somewhere your code can see them +* Write serialization functions for your custom types or use the built in support for the standard library cereal provides +* Use the serialization archives to load and save data + +```cpp +#include +#include +#include +#include + +struct MyRecord +{ + uint8_t x, y; + float z; + + template + void serialize( Archive & ar ) + { + ar( x, y, z ); + } +}; + +struct SomeData +{ + int32_t id; + std::shared_ptr> data; + + template + void save( Archive & ar ) const + { + ar( data ); + } + + template + void load( Archive & ar ) + { + static int32_t idGen = 0; + id = idGen++; + ar( data ); + } +}; + +int main() +{ + std::ofstream os("out.cereal", std::ios::binary); + cereal::BinaryOutputArchive archive( os ); + + SomeData myData; + archive( myData ); + + return 0; +} +``` + +### cereal has a mailing list + +Either get in touch over email or [on the web](https://groups.google.com/forum/#!forum/cerealcpp). + + + +## cereal has a permissive license + +cereal is licensed under the [BSD license](http://opensource.org/licenses/BSD-3-Clause). + +## cereal build status + +* develop : [![Build Status](https://travis-ci.org/USCiLab/cereal.png?branch=develop)](https://travis-ci.org/USCiLab/cereal) +[![Build status](https://ci.appveyor.com/api/projects/status/91aou6smj36or0vb/branch/develop?svg=true)](https://ci.appveyor.com/project/AzothAmmo/cereal/branch/develop) + +--- + +Were you looking for the Haskell cereal? Go here. diff --git a/tpl/cereal/appveyor.yml b/tpl/cereal/appveyor.yml new file mode 100644 index 0000000..121eba1 --- /dev/null +++ b/tpl/cereal/appveyor.yml @@ -0,0 +1,35 @@ +# can use variables like {build} and {branch} +version: 1.2.{build} +pull_requests: + do_not_increment_build_number: true + +branches: + only: + - develop + +configuration: + - Debug + - Release + +environment: + matrix: + - VS_VERSION_MAJOR: 12 + - VS_VERSION_MAJOR: 14 + BOOST_ROOT: C:\Libraries\boost_1_59_0 + +platform: + - Win32 + - x64 + +before_build: "scripts\\appveyor.bat" + +build: + parallel: true + project: build/cereal.sln + verbosity: minimal + +test_script: "scripts\\appveyor.bat test" + +artifacts: + - path: build\Testing + - path: out diff --git a/tpl/cereal/doc/CMakeLists.txt b/tpl/cereal/doc/CMakeLists.txt new file mode 100644 index 0000000..ec0316e --- /dev/null +++ b/tpl/cereal/doc/CMakeLists.txt @@ -0,0 +1,18 @@ +find_package(Doxygen) +if(DOXYGEN_FOUND) + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doxygen.in" "${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg" @ONLY) + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + COMMENT "Generating API documentation with Doxygen" VERBATIM + ) + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../scripts/updatedoc.in" "${CMAKE_CURRENT_BINARY_DIR}/updatedoc.sh" @ONLY) + add_custom_target(update-doc + COMMAND "${CMAKE_CURRENT_BINARY_DIR}/updatedoc.sh" + DEPENDS doc + COMMENT "Copying documentation to gh-pages branch" VERBATIM + ) + +endif(DOXYGEN_FOUND) \ No newline at end of file diff --git a/tpl/cereal/doc/DoxygenLayout.xml b/tpl/cereal/doc/DoxygenLayout.xml new file mode 100644 index 0000000..819c4f4 --- /dev/null +++ b/tpl/cereal/doc/DoxygenLayout.xml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tpl/cereal/doc/doxygen.in b/tpl/cereal/doc/doxygen.in new file mode 100644 index 0000000..ddf1398 --- /dev/null +++ b/tpl/cereal/doc/doxygen.in @@ -0,0 +1,1870 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a 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. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "cereal" + +# 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 = + +# 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 = "A C++11 library for serialization" + +# 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) +# base path where the generated documentation will be put. +# 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 = @CMAKE_CURRENT_BINARY_DIR@/ + +# 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 cause performance problems for the file system. + +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. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) 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. + +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" "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. + +ALWAYS_DETAILED_SEC = NO + +# 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. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then 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. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then 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 specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +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 include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = include + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +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 +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# 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 comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +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 behaviour. +# 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 behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +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. + +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. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# 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. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +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. + +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, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. 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 +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) 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. + +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. + +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); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip 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. + +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 (the +# default) will make doxygen 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. + +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. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) 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. + +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). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data 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 (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT 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. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +#SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE 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 appear 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. + +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 and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +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. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +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. + +EXTRACT_LOCAL_CLASSES = YES + +# 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 (the default) only methods in the interface are included. + +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 namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) 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. + +HIDE_UNDOC_MEMBERS = NO + +# 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 (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +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 (the default) these blocks will be appended to the +# function's detailed documentation block. + +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 (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +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. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = 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. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# 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 default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# 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 default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# 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 default), 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. + +SORT_BY_SCOPE_NAME = NO + +# 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. + +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. + +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. + +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. + +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. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of 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 initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +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. + +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 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 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 , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +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. + +LAYOUT_FILE = "@CMAKE_CURRENT_SOURCE_DIR@/DoxygenLayout.xml" + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# 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. + +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 +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED 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. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR 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. + +WARN_IF_DOC_ERROR = YES + +# The 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 (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# 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) + +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 stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be 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. + +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../include @CMAKE_CURRENT_SOURCE_DIR@/ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. 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. + +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 pattern (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++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.hpp +FILE_PATTERNS += *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +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 = @CMAKE_CURRENT_SOURCE_DIR@/../external + +# 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. + +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 = .* *.cpp */external/* + +# 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 + +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. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# 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. + +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 +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non 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 be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +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 option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_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 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 also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +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. + +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. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# 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. + +REFERENCES_LINK_SOURCE = 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. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) 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. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# 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. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# 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 one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +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. If left blank `html' will be used as the default path. + +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). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER ="@CMAKE_CURRENT_SOURCE_DIR@/footer.html" + +# 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. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +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. + +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. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet 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. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# 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. + +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. + +HTML_COLORSTYLE_GAMMA = 80 + +# 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. + +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. + +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. + +HTML_INDEX_NUM_ENTRIES = 100 + +# 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, 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. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# 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. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, 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. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_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. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, 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). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# 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. + +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. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +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 +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +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. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, 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. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, 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. + +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. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW 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. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) 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. + +ENUM_VALUES_PER_LINE = 4 + +# 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. + +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. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# 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. + +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 before the changes have effect. + +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. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +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. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# 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. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +# XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +# XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = cereal.doxytags + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/tpl/cereal/doc/footer.html b/tpl/cereal/doc/footer.html new file mode 100644 index 0000000..e76312e --- /dev/null +++ b/tpl/cereal/doc/footer.html @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/tpl/cereal/doc/mainpage.dox b/tpl/cereal/doc/mainpage.dox new file mode 100644 index 0000000..066c83c --- /dev/null +++ b/tpl/cereal/doc/mainpage.dox @@ -0,0 +1,47 @@ +/** +\mainpage cereal code documentation + +\tableofcontents + +Aside from the documentation presented on the main cereal site, this doxygen +page offers code level documentation. + +\section modules Browse modules + +cereal's code is organized into modules of similar functionality. Take a look at the modules +section to learn more. Average users will not need to understand the workings of any code that falls under the +internal module. + +\section files Browse files + +If you need reference on a specific file, the files page lists all files in cereal. +*/ + + //! \defgroup Archives Input and Output Archive Types + + /*! \defgroup Access Access Control and Disambiguation + Provides ways to give cereal access to protected member functions, disambiguate + which serialization function cereal should use, and provide ways of using smart + pointers with types that have no default constructor. */ + + /*! \defgroup Utility Utility Functionality + Name-value pairs, binary data wrappers, exceptions, and other utility functions */ + + /*! \defgroup TypeSupport Support for Serializing Various Types + Serialization of many types is shipped with cereal, including most of the standard library as well as a few others. */ + + /*! \defgroup STLSupport Standard Library Support + Serialization methods for nearly all types found in the C++ standard library. + \ingroup TypeSupport */ + + /*! \defgroup TypeConcepts Abstract Type Concept Support + Serialization methods for more abstract type concepts that can generalize over many types. + \ingroup TypeSupport */ + + /*! \defgroup OtherTypes Miscellaneous Types Support + Support for various other types such as smart pointers to polymorphic base classes, boost::variant, etc. + \ingroup TypeSupport */ + + /*! \defgroup Internal Internal Functionality + Various classes and functions that are critical for the operation of cereal but of no + interest to users */ diff --git a/tpl/cereal/include/cereal/access.hpp b/tpl/cereal/include/cereal/access.hpp new file mode 100644 index 0000000..63c1f74 --- /dev/null +++ b/tpl/cereal/include/cereal/access.hpp @@ -0,0 +1,448 @@ +/*! \file access.hpp + \brief Access control, default construction, and serialization disambiguation */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ACCESS_HPP_ +#define CEREAL_ACCESS_HPP_ + +#include +#include +#include +#include + +#include "cereal/macros.hpp" +#include "cereal/details/helpers.hpp" + +namespace cereal +{ + // ###################################################################### + //! A class that allows cereal to load smart pointers to types that have no default constructor + /*! If your class does not have a default constructor, cereal will not be able + to load any smart pointers to it unless you overload LoadAndConstruct + for your class, and provide an appropriate load_and_construct method. You can also + choose to define a member static function instead of specializing this class. + + The specialization of LoadAndConstruct must be placed within the cereal namespace: + + @code{.cpp} + struct MyType + { + MyType( int x ); // note: no default ctor + int myX; + + // Define a serialize or load/save pair as you normally would + template + void serialize( Archive & ar ) + { + ar( myX ); + } + }; + + // Provide a specialization for LoadAndConstruct for your type + namespace cereal + { + template <> struct LoadAndConstruct + { + // load_and_construct will be passed the archive that you will be loading + // from as well as a construct object which you can use as if it were the + // constructor for your type. cereal will handle all memory management for you. + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int x; + ar( x ); + construct( x ); + } + + // if you require versioning, simply add a const std::uint32_t as the final parameter, e.g.: + // load_and_construct( Archive & ar, cereal::construct & construct, std::uint32_t const version ) + }; + } // end namespace cereal + @endcode + + Please note that just as in using external serialization functions, you cannot get + access to non-public members of your class by befriending cereal::access. If you + have the ability to modify the class you wish to serialize, it is recommended that you + use member serialize functions and a static member load_and_construct function. + + load_and_construct functions, regardless of whether they are static members of your class or + whether you create one in the LoadAndConstruct specialization, have the following signature: + + @code{.cpp} + // generally Archive will be templated, but it can be specific if desired + template + static void load_and_construct( Archive & ar, cereal::construct & construct ); + // with an optional last parameter specifying the version: const std::uint32_t version + @endcode + + Versioning behaves the same way as it does for standard serialization functions. + + @tparam T The type to specialize for + @ingroup Access */ + template + struct LoadAndConstruct + { }; + + // forward decl for construct + //! @cond PRIVATE_NEVERDEFINED + namespace memory_detail{ template struct LoadAndConstructLoadWrapper; } + //! @endcond + + //! Used to construct types with no default constructor + /*! When serializing a type that has no default constructor, cereal + will attempt to call either the class static function load_and_construct + or the appropriate template specialization of LoadAndConstruct. cereal + will pass that function a reference to the archive as well as a reference + to a construct object which should be used to perform the allocation once + data has been appropriately loaded. + + @code{.cpp} + struct MyType + { + // note the lack of default constructor + MyType( int xx, int yy ); + + int x, y; + double notInConstructor; + + template + void serialize( Archive & ar ) + { + ar( x, y ); + ar( notInConstructor ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int x, y; + ar( x, y ); + + // use construct object to initialize with loaded data + construct( x, y ); + + // access to member variables and functions via -> operator + ar( construct->notInConstructor ); + + // could also do the above section by: + double z; + ar( z ); + construct->notInConstructor = z; + } + }; + @endcode + + @tparam T The class type being serialized + */ + template + class construct + { + public: + //! Construct and initialize the type T with the given arguments + /*! This will forward all arguments to the underlying type T, + calling an appropriate constructor. + + Calling this function more than once will result in an exception + being thrown. + + @param args The arguments to the constructor for T + @throw Exception If called more than once */ + template + void operator()( Args && ... args ); + // implementation deferred due to reliance on cereal::access + + //! Get a reference to the initialized underlying object + /*! This must be called after the object has been initialized. + + @return A reference to the initialized object + @throw Exception If called before initialization */ + T * operator->() + { + if( !itsValid ) + throw Exception("Object must be initialized prior to accessing members"); + + return itsPtr; + } + + //! Returns a raw pointer to the initialized underlying object + /*! This is mainly intended for use with passing an instance of + a constructed object to cereal::base_class. + + It is strongly recommended to avoid using this function in + any other circumstance. + + @return A raw pointer to the initialized type */ + T * ptr() + { + return operator->(); + } + + private: + template friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper; + + construct( T * p ) : itsPtr( p ), itsEnableSharedRestoreFunction( [](){} ), itsValid( false ) {} + construct( T * p, std::function enableSharedFunc ) : // g++4.7 ice with default lambda to std func + itsPtr( p ), itsEnableSharedRestoreFunction( enableSharedFunc ), itsValid( false ) {} + construct( construct const & ) = delete; + construct & operator=( construct const & ) = delete; + + T * itsPtr; + std::function itsEnableSharedRestoreFunction; + bool itsValid; + }; + + // ###################################################################### + //! A class that can be made a friend to give cereal access to non public functions + /*! If you desire non-public serialization functions within a class, cereal can only + access these if you declare cereal::access a friend. + + @code{.cpp} + class MyClass + { + private: + friend class cereal::access; // gives access to the private serialize + + template + void serialize( Archive & ar ) + { + // some code + } + }; + @endcode + @ingroup Access */ + class access + { + public: + // ####### Standard Serialization ######################################## + template inline + static auto member_serialize(Archive & ar, T & t) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar)) + { return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar); } + + template inline + static auto member_save(Archive & ar, T const & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar)) + { return t.CEREAL_SAVE_FUNCTION_NAME(ar); } + + template inline + static auto member_save_non_const(Archive & ar, T & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar)) + { return t.CEREAL_SAVE_FUNCTION_NAME(ar); } + + template inline + static auto member_load(Archive & ar, T & t) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar)) + { return t.CEREAL_LOAD_FUNCTION_NAME(ar); } + + template inline + static auto member_save_minimal(Archive const & ar, T const & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar)) + { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); } + + template inline + static auto member_save_minimal_non_const(Archive const & ar, T & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar)) + { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); } + + template inline + static auto member_load_minimal(Archive const & ar, T & t, U && u) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u))) + { return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u)); } + + // ####### Versioned Serialization ####################################### + template inline + static auto member_serialize(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version)) + { return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version); } + + template inline + static auto member_save(Archive & ar, T const & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version)) + { return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); } + + template inline + static auto member_save_non_const(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version)) + { return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); } + + template inline + static auto member_load(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar, version)) + { return t.CEREAL_LOAD_FUNCTION_NAME(ar, version); } + + template inline + static auto member_save_minimal(Archive const & ar, T const & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version)) + { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); } + + template inline + static auto member_save_minimal_non_const(Archive const & ar, T & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version)) + { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); } + + template inline + static auto member_load_minimal(Archive const & ar, T & t, U && u, const std::uint32_t version) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u), version)) + { return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u), version); } + + // ####### Other Functionality ########################################## + // for detecting inheritance from enable_shared_from_this + template inline + static auto shared_from_this(T & t) -> decltype(t.shared_from_this()); + + // for placement new + template inline + static void construct( T *& ptr, Args && ... args ) + { + new (ptr) T( std::forward( args )... ); + } + + // for non-placement new with a default constructor + template inline + static T * construct() + { + return new T(); + } + + template inline + static std::false_type load_and_construct(...) + { return std::false_type(); } + + template inline + static auto load_and_construct(Archive & ar, ::cereal::construct & construct) -> decltype(T::load_and_construct(ar, construct)) + { + T::load_and_construct( ar, construct ); + } + + template inline + static auto load_and_construct(Archive & ar, ::cereal::construct & construct, const std::uint32_t version) -> decltype(T::load_and_construct(ar, construct, version)) + { + T::load_and_construct( ar, construct, version ); + } + }; // end class access + + // ###################################################################### + //! A specifier used in conjunction with cereal::specialize to disambiguate + //! serialization in special cases + /*! @relates specialize + @ingroup Access */ + enum class specialization + { + member_serialize, //!< Force the use of a member serialize function + member_load_save, //!< Force the use of a member load/save pair + member_load_save_minimal, //!< Force the use of a member minimal load/save pair + non_member_serialize, //!< Force the use of a non-member serialize function + non_member_load_save, //!< Force the use of a non-member load/save pair + non_member_load_save_minimal //!< Force the use of a non-member minimal load/save pair + }; + + //! A class used to disambiguate cases where cereal cannot detect a unique way of serializing a class + /*! cereal attempts to figure out which method of serialization (member vs. non-member serialize + or load/save pair) at compile time. If for some reason cereal cannot find a non-ambiguous way + of serializing a type, it will produce a static assertion complaining about this. + + This can happen because you have both a serialize and load/save pair, or even because a base + class has a serialize (public or private with friend access) and a derived class does not + overwrite this due to choosing some other serialization type. + + Specializing this class will tell cereal to explicitly use the serialization type you specify + and it will not complain about ambiguity in its compile time selection. However, if cereal detects + an ambiguity in specializations, it will continue to issue a static assertion. + + @code{.cpp} + class MyParent + { + friend class cereal::access; + template + void serialize( Archive & ar ) {} + }; + + // Although serialize is private in MyParent, to cereal::access it will look public, + // even through MyDerived + class MyDerived : public MyParent + { + public: + template + void load( Archive & ar ) {} + + template + void save( Archive & ar ) {} + }; + + // The load/save pair in MyDerived is ambiguous because serialize in MyParent can + // be accessed from cereal::access. This looks the same as making serialize public + // in MyParent, making it seem as though MyDerived has both a serialize and a load/save pair. + // cereal will complain about this at compile time unless we disambiguate: + + namespace cereal + { + // This struct specialization will tell cereal which is the right way to serialize the ambiguity + template struct specialize {}; + + // If we only had a disambiguation for a specific archive type, it would look something like this + template <> struct specialize {}; + } + @endcode + + You can also choose to use the macros CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES or + CEREAL_SPECIALIZE_FOR_ARCHIVE if you want to type a little bit less. + + @tparam T The type to specialize the serialization for + @tparam S The specialization type to use for T + @ingroup Access */ + template + struct specialize : public std::false_type {}; + + //! Convenient macro for performing specialization for all archive types + /*! This performs specialization for the specific type for all types of archives. + This macro should be placed at the global namespace. + + @code{cpp} + struct MyType {}; + CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES( MyType, cereal::specialization::member_load_save ); + @endcode + + @relates specialize + @ingroup Access */ + #define CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES( Type, Specialization ) \ + namespace cereal { template struct specialize {}; } + + //! Convenient macro for performing specialization for a single archive type + /*! This performs specialization for the specific type for a single type of archive. + This macro should be placed at the global namespace. + + @code{cpp} + struct MyType {}; + CEREAL_SPECIALIZE_FOR_ARCHIVE( cereal::XMLInputArchive, MyType, cereal::specialization::member_load_save ); + @endcode + + @relates specialize + @ingroup Access */ + #define CEREAL_SPECIALIZE_FOR_ARCHIVE( Archive, Type, Specialization ) \ + namespace cereal { template <> struct specialize {}; } + + // ###################################################################### + // Deferred Implementation, see construct for more information + template template inline + void construct::operator()( Args && ... args ) + { + if( itsValid ) + throw Exception("Attempting to construct an already initialized object"); + + ::cereal::access::construct( itsPtr, std::forward( args )... ); + itsEnableSharedRestoreFunction(); + itsValid = true; + } +} // namespace cereal + +#endif // CEREAL_ACCESS_HPP_ diff --git a/tpl/cereal/include/cereal/archives/adapters.hpp b/tpl/cereal/include/cereal/archives/adapters.hpp new file mode 100644 index 0000000..0191e32 --- /dev/null +++ b/tpl/cereal/include/cereal/archives/adapters.hpp @@ -0,0 +1,163 @@ +/*! \file adapters.hpp + \brief Archive adapters that provide additional functionality + on top of an existing archive */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ARCHIVES_ADAPTERS_HPP_ +#define CEREAL_ARCHIVES_ADAPTERS_HPP_ + +#include "cereal/details/helpers.hpp" +#include + +namespace cereal +{ + #ifdef CEREAL_FUTURE_EXPERIMENTAL + + // Forward declaration for friend access + template U & get_user_data( A & ); + + //! Wraps an archive and gives access to user data + /*! This adapter is useful if you require access to + either raw pointers or references within your + serialization functions. + + While cereal does not directly support serialization + raw pointers or references, it is sometimes the case + that you may want to supply something such as a raw + pointer or global reference to some constructor. + In this situation this adapter would likely be used + with the construct class to allow for non-default + constructors. + + @note This feature is experimental and may be altered or removed in a future release. See issue #46. + + @code{.cpp} + struct MyUserData + { + int * myRawPointer; + std::reference_wrapper myReference; + }; + + struct MyClass + { + // Note the raw pointer parameter + MyClass( int xx, int * rawP ); + + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int xx; + ar( xx ); + // note the need to use get_user_data to retrieve user data from the archive + construct( xx, cereal::get_user_data( ar ).myRawPointer ); + } + }; + + int main() + { + { + MyUserData md; + md.myRawPointer = &something; + md.myReference = someInstanceOfType; + + std::ifstream is( "data.xml" ); + cereal::UserDataAdapter ar( md, is ); + + std::unique_ptr sc; + ar( sc ); // use as normal + } + + return 0; + } + @endcode + + @relates get_user_data + + @tparam UserData The type to give the archive access to + @tparam Archive The archive to wrap */ + template + class UserDataAdapter : public Archive + { + public: + //! Construct the archive with some user data struct + /*! This will forward all arguments (other than the user + data) to the wrapped archive type. The UserDataAdapter + can then be used identically to the wrapped archive type + + @tparam Args The arguments to pass to the constructor of + the archive. */ + template + UserDataAdapter( UserData & ud, Args && ... args ) : + Archive( std::forward( args )... ), + userdata( ud ) + { } + + private: + //! Overload the rtti function to enable dynamic_cast + void rtti() {} + friend UserData & get_user_data( Archive & ar ); + UserData & userdata; //!< The actual user data + }; + + //! Retrieves user data from an archive wrapped by UserDataAdapter + /*! This will attempt to retrieve the user data associated with + some archive wrapped by UserDataAdapter. If this is used on + an archive that is not wrapped, a run-time exception will occur. + + @note This feature is experimental and may be altered or removed in a future release. See issue #46. + + @note The correct use of this function cannot be enforced at compile + time. + + @relates UserDataAdapter + @tparam UserData The data struct contained in the archive + @tparam Archive The archive, which should be wrapped by UserDataAdapter + @param ar The archive + @throws Exception if the archive this is used upon is not wrapped with + UserDataAdapter. */ + template + UserData & get_user_data( Archive & ar ) + { + try + { + return dynamic_cast &>( ar ).userdata; + } + catch( std::bad_cast const & ) + { + throw ::cereal::Exception("Attempting to get user data from archive not wrapped in UserDataAdapter"); + } + } + #endif // CEREAL_FUTURE_EXPERIMENTAL +} // namespace cereal + +#endif // CEREAL_ARCHIVES_ADAPTERS_HPP_ diff --git a/tpl/cereal/include/cereal/archives/binary.hpp b/tpl/cereal/include/cereal/archives/binary.hpp new file mode 100644 index 0000000..7e00322 --- /dev/null +++ b/tpl/cereal/include/cereal/archives/binary.hpp @@ -0,0 +1,169 @@ +/*! \file binary.hpp + \brief Binary input and output archives */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ARCHIVES_BINARY_HPP_ +#define CEREAL_ARCHIVES_BINARY_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + // ###################################################################### + //! An output archive designed to save data in a compact binary representation + /*! This archive outputs data to a stream in an extremely compact binary + representation with as little extra metadata as possible. + + This archive does nothing to ensure that the endianness of the saved + and loaded data is the same. If you need to have portability over + architectures with different endianness, use PortableBinaryOutputArchive. + + When using a binary archive and a file stream, you must use the + std::ios::binary format flag to avoid having your data altered + inadvertently. + + \ingroup Archives */ + class BinaryOutputArchive : public OutputArchive + { + public: + //! Construct, outputting to the provided stream + /*! @param stream The stream to output to. Can be a stringstream, a file stream, or + even cout! */ + BinaryOutputArchive(std::ostream & stream) : + OutputArchive(this), + itsStream(stream) + { } + + ~BinaryOutputArchive() CEREAL_NOEXCEPT = default; + + //! Writes size bytes of data to the output stream + void saveBinary( const void * data, std::size_t size ) + { + auto const writtenSize = static_cast( itsStream.rdbuf()->sputn( reinterpret_cast( data ), size ) ); + + if(writtenSize != size) + throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize)); + } + + private: + std::ostream & itsStream; + }; + + // ###################################################################### + //! An input archive designed to load data saved using BinaryOutputArchive + /* This archive does nothing to ensure that the endianness of the saved + and loaded data is the same. If you need to have portability over + architectures with different endianness, use PortableBinaryOutputArchive. + + When using a binary archive and a file stream, you must use the + std::ios::binary format flag to avoid having your data altered + inadvertently. + + \ingroup Archives */ + class BinaryInputArchive : public InputArchive + { + public: + //! Construct, loading from the provided stream + BinaryInputArchive(std::istream & stream) : + InputArchive(this), + itsStream(stream) + { } + + ~BinaryInputArchive() CEREAL_NOEXCEPT = default; + + //! Reads size bytes of data from the input stream + void loadBinary( void * const data, std::size_t size ) + { + auto const readSize = static_cast( itsStream.rdbuf()->sgetn( reinterpret_cast( data ), size ) ); + + if(readSize != size) + throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize)); + } + + private: + std::istream & itsStream; + }; + + // ###################################################################### + // Common BinaryArchive serialization functions + + //! Saving for POD types to binary + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, T const & t) + { + ar.saveBinary(std::addressof(t), sizeof(t)); + } + + //! Loading for POD types from binary + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, T & t) + { + ar.loadBinary(std::addressof(t), sizeof(t)); + } + + //! Serializing NVP types to binary + template inline + CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair & t ) + { + ar( t.value ); + } + + //! Serializing SizeTags to binary + template inline + CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag & t ) + { + ar( t.size ); + } + + //! Saving binary data + template inline + void CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, BinaryData const & bd) + { + ar.saveBinary( bd.data, static_cast( bd.size ) ); + } + + //! Loading binary data + template inline + void CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, BinaryData & bd) + { + ar.loadBinary(bd.data, static_cast(bd.size)); + } +} // namespace cereal + +// register archives for polymorphic support +CEREAL_REGISTER_ARCHIVE(cereal::BinaryOutputArchive) +CEREAL_REGISTER_ARCHIVE(cereal::BinaryInputArchive) + +// tie input and output archives together +CEREAL_SETUP_ARCHIVE_TRAITS(cereal::BinaryInputArchive, cereal::BinaryOutputArchive) + +#endif // CEREAL_ARCHIVES_BINARY_HPP_ diff --git a/tpl/cereal/include/cereal/archives/json.hpp b/tpl/cereal/include/cereal/archives/json.hpp new file mode 100644 index 0000000..9d57434 --- /dev/null +++ b/tpl/cereal/include/cereal/archives/json.hpp @@ -0,0 +1,981 @@ +/*! \file json.hpp + \brief JSON input and output archives */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ARCHIVES_JSON_HPP_ +#define CEREAL_ARCHIVES_JSON_HPP_ + +#include "cereal/cereal.hpp" +#include "cereal/details/util.hpp" + +namespace cereal +{ + //! An exception thrown when rapidjson fails an internal assertion + /*! @ingroup Utility */ + struct RapidJSONException : Exception + { RapidJSONException( const char * what_ ) : Exception( what_ ) {} }; +} + +// Override rapidjson assertions to throw exceptions by default +#ifndef CEREAL_RAPIDJSON_ASSERT +#define CEREAL_RAPIDJSON_ASSERT(x) if(!(x)){ \ + throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); } +#endif // RAPIDJSON_ASSERT + +// Enable support for parsing of nan, inf, -inf +#define CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNanAndInfFlag +#define CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag | kParseNanAndInfFlag + +#include "cereal/external/rapidjson/prettywriter.h" +#include "cereal/external/rapidjson/ostreamwrapper.h" +#include "cereal/external/rapidjson/istreamwrapper.h" +#include "cereal/external/rapidjson/document.h" +#include "cereal/external/base64.hpp" + +#include +#include +#include +#include +#include + +namespace cereal +{ + // ###################################################################### + //! An output archive designed to save data to JSON + /*! This archive uses RapidJSON to build serialize data to JSON. + + JSON archives provides a human readable output but at decreased + performance (both in time and space) compared to binary archives. + + JSON archives are only guaranteed to finish flushing their contents + upon destruction and should thus be used in an RAII fashion. + + JSON benefits greatly from name-value pairs, which if present, will + name the nodes in the output. If these are not present, each level + of the output will be given an automatically generated delimited name. + + The precision of the output archive controls the number of decimals output + for floating point numbers and should be sufficiently large (i.e. at least 20) + if there is a desire to have binary equality between the numbers output and + those read in. In general you should expect a loss of precision when going + from floating point to text and back. + + JSON archives do not output the size information for any dynamically sized structure + and instead infer it from the number of children for a node. This means that data + can be hand edited for dynamic sized structures and will still be readable. This + is accomplished through the cereal::SizeTag object, which will cause the archive + to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates + that the container is variable sized and may be edited. + + \ingroup Archives */ + class JSONOutputArchive : public OutputArchive, public traits::TextArchive + { + enum class NodeType { StartObject, InObject, StartArray, InArray }; + + using WriteStream = CEREAL_RAPIDJSON_NAMESPACE::OStreamWrapper; + using JSONWriter = CEREAL_RAPIDJSON_NAMESPACE::PrettyWriter; + + public: + /*! @name Common Functionality + Common use cases for directly interacting with an JSONOutputArchive */ + //! @{ + + //! A class containing various advanced options for the JSON archive + class Options + { + public: + //! Default options + static Options Default(){ return Options(); } + + //! Default options with no indentation + static Options NoIndent(){ return Options( JSONWriter::kDefaultMaxDecimalPlaces, IndentChar::space, 0 ); } + + //! The character to use for indenting + enum class IndentChar : char + { + space = ' ', + tab = '\t', + newline = '\n', + carriage_return = '\r' + }; + + //! Specify specific options for the JSONOutputArchive + /*! @param precision The precision used for floating point numbers + @param indentChar The type of character to indent with + @param indentLength The number of indentChar to use for indentation + (0 corresponds to no indentation) */ + explicit Options( int precision = JSONWriter::kDefaultMaxDecimalPlaces, + IndentChar indentChar = IndentChar::space, + unsigned int indentLength = 4 ) : + itsPrecision( precision ), + itsIndentChar( static_cast(indentChar) ), + itsIndentLength( indentLength ) { } + + private: + friend class JSONOutputArchive; + int itsPrecision; + char itsIndentChar; + unsigned int itsIndentLength; + }; + + //! Construct, outputting to the provided stream + /*! @param stream The stream to output to. + @param options The JSON specific options to use. See the Options struct + for the values of default parameters */ + JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) : + OutputArchive(this), + itsWriteStream(stream), + itsWriter(itsWriteStream), + itsNextName(nullptr) + { + itsWriter.SetMaxDecimalPlaces( options.itsPrecision ); + itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength ); + itsNameCounter.push(0); + itsNodeStack.push(NodeType::StartObject); + } + + //! Destructor, flushes the JSON + ~JSONOutputArchive() CEREAL_NOEXCEPT + { + if (itsNodeStack.top() == NodeType::InObject) + itsWriter.EndObject(); + else if (itsNodeStack.top() == NodeType::InArray) + itsWriter.EndArray(); + } + + //! Saves some binary data, encoded as a base64 string, with an optional name + /*! This will create a new node, optionally named, and insert a value that consists of + the data encoded as a base64 string */ + void saveBinaryValue( const void * data, size_t size, const char * name = nullptr ) + { + setNextName( name ); + writeName(); + + auto base64string = base64::encode( reinterpret_cast( data ), size ); + saveValue( base64string ); + }; + + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the JSONOutputArchive */ + //! @{ + + //! Starts a new node in the JSON output + /*! The node can optionally be given a name by calling setNextName prior + to creating the node + + Nodes only need to be started for types that are themselves objects or arrays */ + void startNode() + { + writeName(); + itsNodeStack.push(NodeType::StartObject); + itsNameCounter.push(0); + } + + //! Designates the most recently added node as finished + void finishNode() + { + // if we ended up serializing an empty object or array, writeName + // will never have been called - so start and then immediately end + // the object/array. + // + // We'll also end any object/arrays we happen to be in + switch(itsNodeStack.top()) + { + case NodeType::StartArray: + itsWriter.StartArray(); + case NodeType::InArray: + itsWriter.EndArray(); + break; + case NodeType::StartObject: + itsWriter.StartObject(); + case NodeType::InObject: + itsWriter.EndObject(); + break; + } + + itsNodeStack.pop(); + itsNameCounter.pop(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNextName = name; + } + + //! Saves a bool to the current node + void saveValue(bool b) { itsWriter.Bool(b); } + //! Saves an int to the current node + void saveValue(int i) { itsWriter.Int(i); } + //! Saves a uint to the current node + void saveValue(unsigned u) { itsWriter.Uint(u); } + //! Saves an int64 to the current node + void saveValue(int64_t i64) { itsWriter.Int64(i64); } + //! Saves a uint64 to the current node + void saveValue(uint64_t u64) { itsWriter.Uint64(u64); } + //! Saves a double to the current node + void saveValue(double d) { itsWriter.Double(d); } + //! Saves a string to the current node + void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast( s.size() )); } + //! Saves a const char * to the current node + void saveValue(char const * s) { itsWriter.String(s); } + //! Saves a nullptr to the current node + void saveValue(std::nullptr_t) { itsWriter.Null(); } + + private: + // Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide + // special overloads to handle these cases. + + //! 32 bit signed long saving to current node + template ::value> = traits::sfinae> inline + void saveLong(T l){ saveValue( static_cast( l ) ); } + + //! non 32 bit signed long saving to current node + template ::value> = traits::sfinae> inline + void saveLong(T l){ saveValue( static_cast( l ) ); } + + //! 32 bit unsigned long saving to current node + template ::value> = traits::sfinae> inline + void saveLong(T lu){ saveValue( static_cast( lu ) ); } + + //! non 32 bit unsigned long saving to current node + template ::value> = traits::sfinae> inline + void saveLong(T lu){ saveValue( static_cast( lu ) ); } + + public: +#ifdef _MSC_VER + //! MSVC only long overload to current node + void saveValue( unsigned long lu ){ saveLong( lu ); }; +#else // _MSC_VER + //! Serialize a long if it would not be caught otherwise + template ::value, + !std::is_same::value, + !std::is_same::value> = traits::sfinae> inline + void saveValue( T t ){ saveLong( t ); } + + //! Serialize an unsigned long if it would not be caught otherwise + template ::value, + !std::is_same::value, + !std::is_same::value> = traits::sfinae> inline + void saveValue( T t ){ saveLong( t ); } +#endif // _MSC_VER + + //! Save exotic arithmetic as strings to current node + /*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */ + template ::value, + !std::is_same::value, + !std::is_same::value, + !std::is_same::value, + !std::is_same::value, + (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> inline + void saveValue(T const & t) + { + std::stringstream ss; ss.precision( std::numeric_limits::max_digits10 ); + ss << t; + saveValue( ss.str() ); + } + + //! Write the name of the upcoming node and prepare object/array state + /*! Since writeName is called for every value that is output, regardless of + whether it has a name or not, it is the place where we will do a deferred + check of our node state and decide whether we are in an array or an object. + + The general workflow of saving to the JSON archive is: + + 1. (optional) Set the name for the next node to be created, usually done by an NVP + 2. Start the node + 3. (if there is data to save) Write the name of the node (this function) + 4. (if there is data to save) Save the data (with saveValue) + 5. Finish the node + */ + void writeName() + { + NodeType const & nodeType = itsNodeStack.top(); + + // Start up either an object or an array, depending on state + if(nodeType == NodeType::StartArray) + { + itsWriter.StartArray(); + itsNodeStack.top() = NodeType::InArray; + } + else if(nodeType == NodeType::StartObject) + { + itsNodeStack.top() = NodeType::InObject; + itsWriter.StartObject(); + } + + // Array types do not output names + if(nodeType == NodeType::InArray) return; + + if(itsNextName == nullptr) + { + std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0"; + saveValue(name); + } + else + { + saveValue(itsNextName); + itsNextName = nullptr; + } + } + + //! Designates that the current node should be output as an array, not an object + void makeArray() + { + itsNodeStack.top() = NodeType::StartArray; + } + + //! @} + + private: + WriteStream itsWriteStream; //!< Rapidjson write stream + JSONWriter itsWriter; //!< Rapidjson writer + char const * itsNextName; //!< The next name + std::stack itsNameCounter; //!< Counter for creating unique names for unnamed nodes + std::stack itsNodeStack; + }; // JSONOutputArchive + + // ###################################################################### + //! An input archive designed to load data from JSON + /*! This archive uses RapidJSON to read in a JSON archive. + + As with the output JSON archive, the preferred way to use this archive is in + an RAII fashion, ensuring its destruction after all data has been read. + + Input JSON should have been produced by the JSONOutputArchive. Data can + only be added to dynamically sized containers (marked by JSON arrays) - + the input archive will determine their size by looking at the number of child nodes. + Only JSON originating from a JSONOutputArchive is officially supported, but data + from other sources may work if properly formatted. + + The JSONInputArchive does not require that nodes are loaded in the same + order they were saved by JSONOutputArchive. Using name value pairs (NVPs), + it is possible to load in an out of order fashion or otherwise skip/select + specific nodes to load. + + The default behavior of the input archive is to read sequentially starting + with the first node and exploring its children. When a given NVP does + not match the read in name for a node, the archive will search for that + node at the current level and load it if it exists. After loading an out of + order node, the archive will then proceed back to loading sequentially from + its new position. + + Consider this simple example where loading of some data is skipped: + + @code{cpp} + // imagine the input file has someData(1-9) saved in order at the top level node + ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file + ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not + // match expected NVP name, so we search + // for the given NVP and load that value + ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its + // current location, proceeding sequentially + @endcode + + \ingroup Archives */ + class JSONInputArchive : public InputArchive, public traits::TextArchive + { + private: + using ReadStream = CEREAL_RAPIDJSON_NAMESPACE::IStreamWrapper; + typedef CEREAL_RAPIDJSON_NAMESPACE::GenericValue> JSONValue; + typedef JSONValue::ConstMemberIterator MemberIterator; + typedef JSONValue::ConstValueIterator ValueIterator; + typedef CEREAL_RAPIDJSON_NAMESPACE::Document::GenericValue GenericValue; + + public: + /*! @name Common Functionality + Common use cases for directly interacting with an JSONInputArchive */ + //! @{ + + //! Construct, reading from the provided stream + /*! @param stream The stream to read from */ + JSONInputArchive(std::istream & stream) : + InputArchive(this), + itsNextName( nullptr ), + itsReadStream(stream) + { + itsDocument.ParseStream<>(itsReadStream); + if (itsDocument.IsArray()) + itsIteratorStack.emplace_back(itsDocument.Begin(), itsDocument.End()); + else + itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd()); + } + + ~JSONInputArchive() CEREAL_NOEXCEPT = default; + + //! Loads some binary data, encoded as a base64 string + /*! This will automatically start and finish a node to load the data, and can be called directly by + users. + + Note that this follows the same ordering rules specified in the class description in regards + to loading in/out of order */ + void loadBinaryValue( void * data, size_t size, const char * name = nullptr ) + { + itsNextName = name; + + std::string encoded; + loadValue( encoded ); + auto decoded = base64::decode( encoded ); + + if( size != decoded.size() ) + throw Exception("Decoded binary data size does not match specified size"); + + std::memcpy( data, decoded.data(), decoded.size() ); + itsNextName = nullptr; + }; + + private: + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the JSONInputArchive */ + //! @{ + + //! An internal iterator that handles both array and object types + /*! This class is a variant and holds both types of iterators that + rapidJSON supports - one for arrays and one for objects. */ + class Iterator + { + public: + Iterator() : itsIndex( 0 ), itsType(Null_) {} + + Iterator(MemberIterator begin, MemberIterator end) : + itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member) + { + if( std::distance( begin, end ) == 0 ) + itsType = Null_; + } + + Iterator(ValueIterator begin, ValueIterator end) : + itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value) + { + if( std::distance( begin, end ) == 0 ) + itsType = Null_; + } + + //! Advance to the next node + Iterator & operator++() + { + ++itsIndex; + return *this; + } + + //! Get the value of the current node + GenericValue const & value() + { + switch(itsType) + { + case Value : return itsValueItBegin[itsIndex]; + case Member: return itsMemberItBegin[itsIndex].value; + default: throw cereal::Exception("JSONInputArchive internal error: null or empty iterator to object or array!"); + } + } + + //! Get the name of the current node, or nullptr if it has no name + const char * name() const + { + if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd ) + return itsMemberItBegin[itsIndex].name.GetString(); + else + return nullptr; + } + + //! Adjust our position such that we are at the node with the given name + /*! @throws Exception if no such named node exists */ + inline void search( const char * searchName ) + { + const auto len = std::strlen( searchName ); + size_t index = 0; + for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index ) + { + const auto currentName = it->name.GetString(); + if( ( std::strncmp( searchName, currentName, len ) == 0 ) && + ( std::strlen( currentName ) == len ) ) + { + itsIndex = index; + return; + } + } + + throw Exception("JSON Parsing failed - provided NVP (" + std::string(searchName) + ") not found"); + } + + private: + MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object) + ValueIterator itsValueItBegin, itsValueItEnd; //!< The value iterator (array) + size_t itsIndex; //!< The current index of this iterator + enum Type {Value, Member, Null_} itsType; //!< Whether this holds values (array) or members (objects) or nothing + }; + + //! Searches for the expectedName node if it doesn't match the actualName + /*! This needs to be called before every load or node start occurs. This function will + check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual + next name given. If the names do not match, it will search in the current level of the JSON for that name. + If the name is not found, an exception will be thrown. + + Resets the NVP name after called. + + @throws Exception if an expectedName is given and not found */ + inline void search() + { + // The name an NVP provided with setNextName() + if( itsNextName ) + { + // The actual name of the current node + auto const actualName = itsIteratorStack.back().name(); + + // Do a search if we don't see a name coming up, or if the names don't match + if( !actualName || std::strcmp( itsNextName, actualName ) != 0 ) + itsIteratorStack.back().search( itsNextName ); + } + + itsNextName = nullptr; + } + + public: + //! Starts a new node, going into its proper iterator + /*! This places an iterator for the next node to be parsed onto the iterator stack. If the next + node is an array, this will be a value iterator, otherwise it will be a member iterator. + + By default our strategy is to start with the document root node and then recursively iterate through + all children in the order they show up in the document. + We don't need to know NVPs to do this; we'll just blindly load in the order things appear in. + + If we were given an NVP, we will search for it if it does not match our the name of the next node + that would normally be loaded. This functionality is provided by search(). */ + void startNode() + { + search(); + + if(itsIteratorStack.back().value().IsArray()) + itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End()); + else + itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd()); + } + + //! Finishes the most recently started node + void finishNode() + { + itsIteratorStack.pop_back(); + ++itsIteratorStack.back(); + } + + //! Retrieves the current node name + /*! @return nullptr if no name exists */ + const char * getNodeName() const + { + return itsIteratorStack.back().name(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNextName = name; + } + + //! Loads a value from the current node - small signed overload + template ::value, + sizeof(T) < sizeof(int64_t)> = traits::sfinae> inline + void loadValue(T & val) + { + search(); + + val = static_cast( itsIteratorStack.back().value().GetInt() ); + ++itsIteratorStack.back(); + } + + //! Loads a value from the current node - small unsigned overload + template ::value, + sizeof(T) < sizeof(uint64_t), + !std::is_same::value> = traits::sfinae> inline + void loadValue(T & val) + { + search(); + + val = static_cast( itsIteratorStack.back().value().GetUint() ); + ++itsIteratorStack.back(); + } + + //! Loads a value from the current node - bool overload + void loadValue(bool & val) { search(); val = itsIteratorStack.back().value().GetBool(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - int64 overload + void loadValue(int64_t & val) { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - uint64 overload + void loadValue(uint64_t & val) { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - float overload + void loadValue(float & val) { search(); val = static_cast(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); } + //! Loads a value from the current node - double overload + void loadValue(double & val) { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - string overload + void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); } + //! Loads a nullptr from the current node + void loadValue(std::nullptr_t&) { search(); CEREAL_RAPIDJSON_ASSERT(itsIteratorStack.back().value().IsNull()); ++itsIteratorStack.back(); } + + // Special cases to handle various flavors of long, which tend to conflict with + // the int32_t or int64_t on various compiler/OS combinations. MSVC doesn't need any of this. + #ifndef _MSC_VER + private: + //! 32 bit signed long loading from current node + template inline + typename std::enable_if::value, void>::type + loadLong(T & l){ loadValue( reinterpret_cast( l ) ); } + + //! non 32 bit signed long loading from current node + template inline + typename std::enable_if::value, void>::type + loadLong(T & l){ loadValue( reinterpret_cast( l ) ); } + + //! 32 bit unsigned long loading from current node + template inline + typename std::enable_if::value, void>::type + loadLong(T & lu){ loadValue( reinterpret_cast( lu ) ); } + + //! non 32 bit unsigned long loading from current node + template inline + typename std::enable_if::value, void>::type + loadLong(T & lu){ loadValue( reinterpret_cast( lu ) ); } + + public: + //! Serialize a long if it would not be caught otherwise + template inline + typename std::enable_if::value && + sizeof(T) >= sizeof(std::int64_t) && + !std::is_same::value, void>::type + loadValue( T & t ){ loadLong(t); } + + //! Serialize an unsigned long if it would not be caught otherwise + template inline + typename std::enable_if::value && + sizeof(T) >= sizeof(std::uint64_t) && + !std::is_same::value, void>::type + loadValue( T & t ){ loadLong(t); } + #endif // _MSC_VER + + private: + //! Convert a string to a long long + void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); } + //! Convert a string to an unsigned long long + void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); } + //! Convert a string to a long double + void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); } + + public: + //! Loads a value from the current node - long double and long long overloads + template ::value, + !std::is_same::value, + !std::is_same::value, + !std::is_same::value, + !std::is_same::value, + (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> + inline void loadValue(T & val) + { + std::string encoded; + loadValue( encoded ); + stringToNumber( encoded, val ); + } + + //! Loads the size for a SizeTag + void loadSize(size_type & size) + { + if (itsIteratorStack.size() == 1) + size = itsDocument.Size(); + else + size = (itsIteratorStack.rbegin() + 1)->value().Size(); + } + + //! @} + + private: + const char * itsNextName; //!< Next name set by NVP + ReadStream itsReadStream; //!< Rapidjson write stream + std::vector itsIteratorStack; //!< 'Stack' of rapidJSON iterators + CEREAL_RAPIDJSON_NAMESPACE::Document itsDocument; //!< Rapidjson document + }; + + // ###################################################################### + // JSONArchive prologue and epilogue functions + // ###################################################################### + + // ###################################################################### + //! Prologue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void prologue( JSONOutputArchive &, NameValuePair const & ) + { } + + //! Prologue for NVPs for JSON archives + template inline + void prologue( JSONInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Epilogue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void epilogue( JSONOutputArchive &, NameValuePair const & ) + { } + + //! Epilogue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void epilogue( JSONInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Prologue for SizeTags for JSON archives + /*! SizeTags are strictly ignored for JSON, they just indicate + that the current node should be made into an array */ + template inline + void prologue( JSONOutputArchive & ar, SizeTag const & ) + { + ar.makeArray(); + } + + //! Prologue for SizeTags for JSON archives + template inline + void prologue( JSONInputArchive &, SizeTag const & ) + { } + + // ###################################################################### + //! Epilogue for SizeTags for JSON archives + /*! SizeTags are strictly ignored for JSON */ + template inline + void epilogue( JSONOutputArchive &, SizeTag const & ) + { } + + //! Epilogue for SizeTags for JSON archives + template inline + void epilogue( JSONInputArchive &, SizeTag const & ) + { } + + // ###################################################################### + //! Prologue for all other types for JSON archives (except minimal types) + /*! Starts a new node, named either automatically or by some NVP, + that may be given data by the type about to be archived + + Minimal types do not start or finish nodes */ + template ::value, + !traits::has_minimal_base_class_serialization::value, + !traits::has_minimal_output_serialization::value> = traits::sfinae> + inline void prologue( JSONOutputArchive & ar, T const & ) + { + ar.startNode(); + } + + //! Prologue for all other types for JSON archives + template ::value, + !traits::has_minimal_base_class_serialization::value, + !traits::has_minimal_input_serialization::value> = traits::sfinae> + inline void prologue( JSONInputArchive & ar, T const & ) + { + ar.startNode(); + } + + // ###################################################################### + //! Epilogue for all other types other for JSON archives (except minimal types) + /*! Finishes the node created in the prologue + + Minimal types do not start or finish nodes */ + template ::value, + !traits::has_minimal_base_class_serialization::value, + !traits::has_minimal_output_serialization::value> = traits::sfinae> + inline void epilogue( JSONOutputArchive & ar, T const & ) + { + ar.finishNode(); + } + + //! Epilogue for all other types other for JSON archives + template ::value, + !traits::has_minimal_base_class_serialization::value, + !traits::has_minimal_input_serialization::value> = traits::sfinae> + inline void epilogue( JSONInputArchive & ar, T const & ) + { + ar.finishNode(); + } + + // ###################################################################### + //! Prologue for arithmetic types for JSON archives + inline + void prologue( JSONOutputArchive & ar, std::nullptr_t const & ) + { + ar.writeName(); + } + + //! Prologue for arithmetic types for JSON archives + inline + void prologue( JSONInputArchive &, std::nullptr_t const & ) + { } + + // ###################################################################### + //! Epilogue for arithmetic types for JSON archives + inline + void epilogue( JSONOutputArchive &, std::nullptr_t const & ) + { } + + //! Epilogue for arithmetic types for JSON archives + inline + void epilogue( JSONInputArchive &, std::nullptr_t const & ) + { } + + // ###################################################################### + //! Prologue for arithmetic types for JSON archives + template ::value> = traits::sfinae> inline + void prologue( JSONOutputArchive & ar, T const & ) + { + ar.writeName(); + } + + //! Prologue for arithmetic types for JSON archives + template ::value> = traits::sfinae> inline + void prologue( JSONInputArchive &, T const & ) + { } + + // ###################################################################### + //! Epilogue for arithmetic types for JSON archives + template ::value> = traits::sfinae> inline + void epilogue( JSONOutputArchive &, T const & ) + { } + + //! Epilogue for arithmetic types for JSON archives + template ::value> = traits::sfinae> inline + void epilogue( JSONInputArchive &, T const & ) + { } + + // ###################################################################### + //! Prologue for strings for JSON archives + template inline + void prologue(JSONOutputArchive & ar, std::basic_string const &) + { + ar.writeName(); + } + + //! Prologue for strings for JSON archives + template inline + void prologue(JSONInputArchive &, std::basic_string const &) + { } + + // ###################################################################### + //! Epilogue for strings for JSON archives + template inline + void epilogue(JSONOutputArchive &, std::basic_string const &) + { } + + //! Epilogue for strings for JSON archives + template inline + void epilogue(JSONInputArchive &, std::basic_string const &) + { } + + // ###################################################################### + // Common JSONArchive serialization functions + // ###################################################################### + //! Serializing NVP types to JSON + template inline + void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive & ar, NameValuePair const & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + template inline + void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, NameValuePair & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + //! Saving for nullptr to JSON + inline + void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::nullptr_t const & t) + { + ar.saveValue( t ); + } + + //! Loading arithmetic from JSON + inline + void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::nullptr_t & t) + { + ar.loadValue( t ); + } + + //! Saving for arithmetic to JSON + template ::value> = traits::sfinae> inline + void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, T const & t) + { + ar.saveValue( t ); + } + + //! Loading arithmetic from JSON + template ::value> = traits::sfinae> inline + void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, T & t) + { + ar.loadValue( t ); + } + + //! saving string to JSON + template inline + void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::basic_string const & str) + { + ar.saveValue( str ); + } + + //! loading string from JSON + template inline + void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::basic_string & str) + { + ar.loadValue( str ); + } + + // ###################################################################### + //! Saving SizeTags to JSON + template inline + void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive &, SizeTag const & ) + { + // nothing to do here, we don't explicitly save the size + } + + //! Loading SizeTags from JSON + template inline + void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, SizeTag & st ) + { + ar.loadSize( st.size ); + } +} // namespace cereal + +// register archives for polymorphic support +CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive) +CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive) + +// tie input and output archives together +CEREAL_SETUP_ARCHIVE_TRAITS(cereal::JSONInputArchive, cereal::JSONOutputArchive) + +#endif // CEREAL_ARCHIVES_JSON_HPP_ diff --git a/tpl/cereal/include/cereal/archives/portable_binary.hpp b/tpl/cereal/include/cereal/archives/portable_binary.hpp new file mode 100644 index 0000000..76e634b --- /dev/null +++ b/tpl/cereal/include/cereal/archives/portable_binary.hpp @@ -0,0 +1,334 @@ +/*! \file binary.hpp + \brief Binary input and output archives */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_ +#define CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_ + +#include "cereal/cereal.hpp" +#include +#include + +namespace cereal +{ + namespace portable_binary_detail + { + //! Returns true if the current machine is little endian + /*! @ingroup Internal */ + inline std::uint8_t is_little_endian() + { + static std::int32_t test = 1; + return *reinterpret_cast( &test ) == 1; + } + + //! Swaps the order of bytes for some chunk of memory + /*! @param data The data as a uint8_t pointer + @tparam DataSize The true size of the data + @ingroup Internal */ + template + inline void swap_bytes( std::uint8_t * data ) + { + for( std::size_t i = 0, end = DataSize / 2; i < end; ++i ) + std::swap( data[i], data[DataSize - i - 1] ); + } + } // end namespace portable_binary_detail + + // ###################################################################### + //! An output archive designed to save data in a compact binary representation portable over different architectures + /*! This archive outputs data to a stream in an extremely compact binary + representation with as little extra metadata as possible. + + This archive will record the endianness of the data as well as the desired in/out endianness + and assuming that the user takes care of ensuring serialized types are the same size + across machines, is portable over different architectures. + + When using a binary archive and a file stream, you must use the + std::ios::binary format flag to avoid having your data altered + inadvertently. + + \warning This archive has not been thoroughly tested across different architectures. + Please report any issues, optimizations, or feature requests at + the project github. + + \ingroup Archives */ + class PortableBinaryOutputArchive : public OutputArchive + { + public: + //! A class containing various advanced options for the PortableBinaryOutput archive + class Options + { + public: + //! Represents desired endianness + enum class Endianness : std::uint8_t + { big, little }; + + //! Default options, preserve system endianness + static Options Default(){ return Options(); } + + //! Save as little endian + static Options LittleEndian(){ return Options( Endianness::little ); } + + //! Save as big endian + static Options BigEndian(){ return Options( Endianness::big ); } + + //! Specify specific options for the PortableBinaryOutputArchive + /*! @param outputEndian The desired endianness of saved (output) data */ + explicit Options( Endianness outputEndian = getEndianness() ) : + itsOutputEndianness( outputEndian ) { } + + private: + //! Gets the endianness of the system + inline static Endianness getEndianness() + { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; } + + //! Checks if Options is set for little endian + inline std::uint8_t is_little_endian() const + { return itsOutputEndianness == Endianness::little; } + + friend class PortableBinaryOutputArchive; + Endianness itsOutputEndianness; + }; + + //! Construct, outputting to the provided stream + /*! @param stream The stream to output to. Should be opened with std::ios::binary flag. + @param options The PortableBinary specific options to use. See the Options struct + for the values of default parameters */ + PortableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) : + OutputArchive(this), + itsStream(stream), + itsConvertEndianness( portable_binary_detail::is_little_endian() ^ options.is_little_endian() ) + { + this->operator()( options.is_little_endian() ); + } + + ~PortableBinaryOutputArchive() CEREAL_NOEXCEPT = default; + + //! Writes size bytes of data to the output stream + template inline + void saveBinary( const void * data, std::size_t size ) + { + std::size_t writtenSize = 0; + + if( itsConvertEndianness ) + { + for( std::size_t i = 0; i < size; i += DataSize ) + for( std::size_t j = 0; j < DataSize; ++j ) + writtenSize += static_cast( itsStream.rdbuf()->sputn( reinterpret_cast( data ) + DataSize - j - 1 + i, 1 ) ); + } + else + writtenSize = static_cast( itsStream.rdbuf()->sputn( reinterpret_cast( data ), size ) ); + + if(writtenSize != size) + throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize)); + } + + private: + std::ostream & itsStream; + const uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon saving + }; + + // ###################################################################### + //! An input archive designed to load data saved using PortableBinaryOutputArchive + /*! This archive outputs data to a stream in an extremely compact binary + representation with as little extra metadata as possible. + + This archive will load the endianness of the serialized data and + if necessary transform it to match that of the local machine. This comes + at a significant performance cost compared to non portable archives if + the transformation is necessary, and also causes a small performance hit + even if it is not necessary. + + It is recommended to use portable archives only if you know that you will + be sending binary data to machines with different endianness. + + The archive will do nothing to ensure types are the same size - that is + the responsibility of the user. + + When using a binary archive and a file stream, you must use the + std::ios::binary format flag to avoid having your data altered + inadvertently. + + \warning This archive has not been thoroughly tested across different architectures. + Please report any issues, optimizations, or feature requests at + the project github. + + \ingroup Archives */ + class PortableBinaryInputArchive : public InputArchive + { + public: + //! A class containing various advanced options for the PortableBinaryInput archive + class Options + { + public: + //! Represents desired endianness + enum class Endianness : std::uint8_t + { big, little }; + + //! Default options, preserve system endianness + static Options Default(){ return Options(); } + + //! Load into little endian + static Options LittleEndian(){ return Options( Endianness::little ); } + + //! Load into big endian + static Options BigEndian(){ return Options( Endianness::big ); } + + //! Specify specific options for the PortableBinaryInputArchive + /*! @param inputEndian The desired endianness of loaded (input) data */ + explicit Options( Endianness inputEndian = getEndianness() ) : + itsInputEndianness( inputEndian ) { } + + private: + //! Gets the endianness of the system + inline static Endianness getEndianness() + { return portable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; } + + //! Checks if Options is set for little endian + inline std::uint8_t is_little_endian() const + { return itsInputEndianness == Endianness::little; } + + friend class PortableBinaryInputArchive; + Endianness itsInputEndianness; + }; + + //! Construct, loading from the provided stream + /*! @param stream The stream to read from. Should be opened with std::ios::binary flag. + @param options The PortableBinary specific options to use. See the Options struct + for the values of default parameters */ + PortableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) : + InputArchive(this), + itsStream(stream), + itsConvertEndianness( false ) + { + uint8_t streamLittleEndian; + this->operator()( streamLittleEndian ); + itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian; + } + + ~PortableBinaryInputArchive() CEREAL_NOEXCEPT = default; + + //! Reads size bytes of data from the input stream + /*! @param data The data to save + @param size The number of bytes in the data + @tparam DataSize T The size of the actual type of the data elements being loaded */ + template inline + void loadBinary( void * const data, std::size_t size ) + { + // load data + auto const readSize = static_cast( itsStream.rdbuf()->sgetn( reinterpret_cast( data ), size ) ); + + if(readSize != size) + throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize)); + + // flip bits if needed + if( itsConvertEndianness ) + { + std::uint8_t * ptr = reinterpret_cast( data ); + for( std::size_t i = 0; i < size; i += DataSize ) + portable_binary_detail::swap_bytes( ptr + i ); + } + } + + private: + std::istream & itsStream; + uint8_t itsConvertEndianness; //!< If set to true, we will need to swap bytes upon loading + }; + + // ###################################################################### + // Common BinaryArchive serialization functions + + //! Saving for POD types to portable binary + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, T const & t) + { + static_assert( !std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point" ); + ar.template saveBinary(std::addressof(t), sizeof(t)); + } + + //! Loading for POD types from portable binary + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, T & t) + { + static_assert( !std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point" ); + ar.template loadBinary(std::addressof(t), sizeof(t)); + } + + //! Serializing NVP types to portable binary + template inline + CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair & t ) + { + ar( t.value ); + } + + //! Serializing SizeTags to portable binary + template inline + CEREAL_ARCHIVE_RESTRICT(PortableBinaryInputArchive, PortableBinaryOutputArchive) + CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag & t ) + { + ar( t.size ); + } + + //! Saving binary data to portable binary + template inline + void CEREAL_SAVE_FUNCTION_NAME(PortableBinaryOutputArchive & ar, BinaryData const & bd) + { + typedef typename std::remove_pointer::type TT; + static_assert( !std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point" ); + + ar.template saveBinary( bd.data, static_cast( bd.size ) ); + } + + //! Loading binary data from portable binary + template inline + void CEREAL_LOAD_FUNCTION_NAME(PortableBinaryInputArchive & ar, BinaryData & bd) + { + typedef typename std::remove_pointer::type TT; + static_assert( !std::is_floating_point::value || + (std::is_floating_point::value && std::numeric_limits::is_iec559), + "Portable binary only supports IEEE 754 standardized floating point" ); + + ar.template loadBinary( bd.data, static_cast( bd.size ) ); + } +} // namespace cereal + +// register archives for polymorphic support +CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryOutputArchive) +CEREAL_REGISTER_ARCHIVE(cereal::PortableBinaryInputArchive) + +// tie input and output archives together +CEREAL_SETUP_ARCHIVE_TRAITS(cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive) + +#endif // CEREAL_ARCHIVES_PORTABLE_BINARY_HPP_ diff --git a/tpl/cereal/include/cereal/archives/xml.hpp b/tpl/cereal/include/cereal/archives/xml.hpp new file mode 100644 index 0000000..c520f02 --- /dev/null +++ b/tpl/cereal/include/cereal/archives/xml.hpp @@ -0,0 +1,897 @@ +/*! \file xml.hpp + \brief XML input and output archives */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_ARCHIVES_XML_HPP_ +#define CEREAL_ARCHIVES_XML_HPP_ +#include "cereal/cereal.hpp" +#include "cereal/details/util.hpp" + +#include "cereal/external/rapidxml/rapidxml.hpp" +#include "cereal/external/rapidxml/rapidxml_print.hpp" +#include "cereal/external/base64.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace cereal +{ + namespace xml_detail + { + #ifndef CEREAL_XML_STRING_VALUE + //! The default name for the root node in a cereal xml archive. + /*! You can define CEREAL_XML_STRING_VALUE to be different assuming you do so + before this file is included. */ + #define CEREAL_XML_STRING_VALUE "cereal" + #endif // CEREAL_XML_STRING_VALUE + + //! The name given to the root node in a cereal xml archive + static const char * CEREAL_XML_STRING = CEREAL_XML_STRING_VALUE; + + //! Returns true if the character is whitespace + inline bool isWhitespace( char c ) + { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + } + + // ###################################################################### + //! An output archive designed to save data to XML + /*! This archive uses RapidXML to build an in memory XML tree of the + data it serializes before outputting it to its stream upon destruction. + This archive should be used in an RAII fashion, letting + the automatic destruction of the object cause the flush to its stream. + + XML archives provides a human readable output but at decreased + performance (both in time and space) compared to binary archives. + + XML benefits greatly from name-value pairs, which if present, will + name the nodes in the output. If these are not present, each level + of the output tree will be given an automatically generated delimited name. + + The precision of the output archive controls the number of decimals output + for floating point numbers and should be sufficiently large (i.e. at least 20) + if there is a desire to have binary equality between the numbers output and + those read in. In general you should expect a loss of precision when going + from floating point to text and back. + + XML archives can optionally print the type of everything they serialize, which + adds an attribute to each node. + + XML archives do not output the size information for any dynamically sized structure + and instead infer it from the number of children for a node. This means that data + can be hand edited for dynamic sized structures and will still be readable. This + is accomplished through the cereal::SizeTag object, which will also add an attribute + to its parent field. + \ingroup Archives */ + class XMLOutputArchive : public OutputArchive, public traits::TextArchive + { + public: + /*! @name Common Functionality + Common use cases for directly interacting with an XMLOutputArchive */ + //! @{ + + //! A class containing various advanced options for the XML archive + class Options + { + public: + //! Default options + static Options Default(){ return Options(); } + + //! Default options with no indentation + static Options NoIndent(){ return Options( std::numeric_limits::max_digits10, false ); } + + //! Specify specific options for the XMLOutputArchive + /*! @param precision The precision used for floating point numbers + @param indent Whether to indent each line of XML + @param outputType Whether to output the type of each serialized object as an attribute */ + explicit Options( int precision = std::numeric_limits::max_digits10, + bool indent = true, + bool outputType = false ) : + itsPrecision( precision ), + itsIndent( indent ), + itsOutputType( outputType ) { } + + private: + friend class XMLOutputArchive; + int itsPrecision; + bool itsIndent; + bool itsOutputType; + }; + + //! Construct, outputting to the provided stream upon destruction + /*! @param stream The stream to output to. Note that XML is only guaranteed to flush + its output to the stream upon destruction. + @param options The XML specific options to use. See the Options struct + for the values of default parameters */ + XMLOutputArchive( std::ostream & stream, Options const & options = Options::Default() ) : + OutputArchive(this), + itsStream(stream), + itsOutputType( options.itsOutputType ), + itsIndent( options.itsIndent ) + { + // rapidxml will delete all allocations when xml_document is cleared + auto node = itsXML.allocate_node( rapidxml::node_declaration ); + node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) ); + node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) ); + itsXML.append_node( node ); + + // allocate root node + auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING ); + itsXML.append_node( root ); + itsNodes.emplace( root ); + + // set attributes on the streams + itsStream << std::boolalpha; + itsStream.precision( options.itsPrecision ); + itsOS << std::boolalpha; + itsOS.precision( options.itsPrecision ); + } + + //! Destructor, flushes the XML + ~XMLOutputArchive() CEREAL_NOEXCEPT + { + const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting; + rapidxml::print( itsStream, itsXML, flags ); + itsXML.clear(); + } + + //! Saves some binary data, encoded as a base64 string, with an optional name + /*! This can be called directly by users and it will automatically create a child node for + the current XML node, populate it with a base64 encoded string, and optionally name + it. The node will be finished after it has been populated. */ + void saveBinaryValue( const void * data, size_t size, const char * name = nullptr ) + { + itsNodes.top().name = name; + + startNode(); + + auto base64string = base64::encode( reinterpret_cast( data ), size ); + saveValue( base64string ); + + if( itsOutputType ) + itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) ); + + finishNode(); + }; + + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the XMLOutputArchive */ + //! @{ + + //! Creates a new node that is a child of the node at the top of the stack + /*! Nodes will be given a name that has either been pre-set by a name value pair, + or generated based upon a counter unique to the parent node. If you want to + give a node a specific name, use setNextName prior to calling startNode. + + The node will then be pushed onto the node stack. */ + void startNode() + { + // generate a name for this new node + const auto nameString = itsNodes.top().getValueName(); + + // allocate strings for all of the data in the XML object + auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 ); + + // insert into the XML + auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() ); + itsNodes.top().node->append_node( node ); + itsNodes.emplace( node ); + } + + //! Designates the most recently added node as finished + void finishNode() + { + itsNodes.pop(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNodes.top().name = name; + } + + //! Saves some data, encoded as a string, into the current top level node + /*! The data will be be named with the most recent name if one exists, + otherwise it will be given some default delimited value that depends upon + the parent node */ + template inline + void saveValue( T const & value ) + { + itsOS.clear(); itsOS.seekp( 0, std::ios::beg ); + itsOS << value << std::ends; + + auto strValue = itsOS.str(); + + // itsOS.str() may contain data from previous calls after the first '\0' that was just inserted + // and this data is counted in the length call. We make sure to remove that section so that the + // whitespace validation is done properly + strValue.resize(std::strlen(strValue.c_str())); + + // If the first or last character is a whitespace, add xml:space attribute + const auto len = strValue.length(); + if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) ) + { + itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "xml:space", "preserve" ) ); + } + + // allocate strings for all of the data in the XML object + auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 ); + + // insert into the XML + itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) ); + } + + //! Overload for uint8_t prevents them from being serialized as characters + void saveValue( uint8_t const & value ) + { + saveValue( static_cast( value ) ); + } + + //! Overload for int8_t prevents them from being serialized as characters + void saveValue( int8_t const & value ) + { + saveValue( static_cast( value ) ); + } + + //! Causes the type to be appended as an attribute to the most recently made node if output type is set to true + template inline + void insertType() + { + if( !itsOutputType ) + return; + + // generate a name for this new node + const auto nameString = util::demangledName(); + + // allocate strings for all of the data in the XML object + auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 ); + + itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) ); + } + + //! Appends an attribute to the current top level node + void appendAttribute( const char * name, const char * value ) + { + auto namePtr = itsXML.allocate_string( name ); + auto valuePtr = itsXML.allocate_string( value ); + itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) ); + } + + protected: + //! A struct that contains metadata about a node + struct NodeInfo + { + NodeInfo( rapidxml::xml_node<> * n = nullptr, + const char * nm = nullptr ) : + node( n ), + counter( 0 ), + name( nm ) + { } + + rapidxml::xml_node<> * node; //!< A pointer to this node + size_t counter; //!< The counter for naming child nodes + const char * name; //!< The name for the next child node + + //! Gets the name for the next child node created from this node + /*! The name will be automatically generated using the counter if + a name has not been previously set. If a name has been previously + set, that name will be returned only once */ + std::string getValueName() + { + if( name ) + { + auto n = name; + name = nullptr; + return {n}; + } + else + return "value" + std::to_string( counter++ ) + "\0"; + } + }; // NodeInfo + + //! @} + + private: + std::ostream & itsStream; //!< The output stream + rapidxml::xml_document<> itsXML; //!< The XML document + std::stack itsNodes; //!< A stack of nodes added to the document + std::ostringstream itsOS; //!< Used to format strings internally + bool itsOutputType; //!< Controls whether type information is printed + bool itsIndent; //!< Controls whether indenting is used + }; // XMLOutputArchive + + // ###################################################################### + //! An output archive designed to load data from XML + /*! This archive uses RapidXML to build an in memory XML tree of the + data in the stream it is given before loading any types serialized. + + As with the output XML archive, the preferred way to use this archive is in + an RAII fashion, ensuring its destruction after all data has been read. + + Input XML should have been produced by the XMLOutputArchive. Data can + only be added to dynamically sized containers - the input archive will + determine their size by looking at the number of child nodes. Data that + did not originate from an XMLOutputArchive is not officially supported, + but may be possible to use if properly formatted. + + The XMLInputArchive does not require that nodes are loaded in the same + order they were saved by XMLOutputArchive. Using name value pairs (NVPs), + it is possible to load in an out of order fashion or otherwise skip/select + specific nodes to load. + + The default behavior of the input archive is to read sequentially starting + with the first node and exploring its children. When a given NVP does + not match the read in name for a node, the archive will search for that + node at the current level and load it if it exists. After loading an out of + order node, the archive will then proceed back to loading sequentially from + its new position. + + Consider this simple example where loading of some data is skipped: + + @code{cpp} + // imagine the input file has someData(1-9) saved in order at the top level node + ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file + ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not + // match expected NVP name, so we search + // for the given NVP and load that value + ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its + // current location, proceeding sequentially + @endcode + + \ingroup Archives */ + class XMLInputArchive : public InputArchive, public traits::TextArchive + { + public: + /*! @name Common Functionality + Common use cases for directly interacting with an XMLInputArchive */ + //! @{ + + //! Construct, reading in from the provided stream + /*! Reads in an entire XML document from some stream and parses it as soon + as serialization starts + + @param stream The stream to read from. Can be a stringstream or a file. */ + XMLInputArchive( std::istream & stream ) : + InputArchive( this ), + itsData( std::istreambuf_iterator( stream ), std::istreambuf_iterator() ) + { + try + { + itsData.push_back('\0'); // rapidxml will do terrible things without the data being null terminated + itsXML.parse( reinterpret_cast( itsData.data() ) ); + } + catch( rapidxml::parse_error const & ) + { + //std::cerr << "-----Original-----" << std::endl; + //stream.seekg(0); + //std::cout << std::string( std::istreambuf_iterator( stream ), std::istreambuf_iterator() ) << std::endl; + + //std::cerr << "-----Error-----" << std::endl; + //std::cerr << e.what() << std::endl; + //std::cerr << e.where() << std::endl; + throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming"); + } + + // Parse the root + auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING ); + if( root == nullptr ) + throw Exception("Could not detect cereal root node - likely due to empty or invalid input"); + else + itsNodes.emplace( root ); + } + + ~XMLInputArchive() CEREAL_NOEXCEPT = default; + + //! Loads some binary data, encoded as a base64 string, optionally specified by some name + /*! This will automatically start and finish a node to load the data, and can be called directly by + users. + + Note that this follows the same ordering rules specified in the class description in regards + to loading in/out of order */ + void loadBinaryValue( void * data, size_t size, const char * name = nullptr ) + { + setNextName( name ); + startNode(); + + std::string encoded; + loadValue( encoded ); + + auto decoded = base64::decode( encoded ); + + if( size != decoded.size() ) + throw Exception("Decoded binary data size does not match specified size"); + + std::memcpy( data, decoded.data(), decoded.size() ); + + finishNode(); + }; + + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the XMLInputArchive */ + //! @{ + + //! Prepares to start reading the next node + /*! This places the next node to be parsed onto the nodes stack. + + By default our strategy is to start with the document root node and then + recursively iterate through all children in the order they show up in the document. + We don't need to know NVPs do to this; we'll just blindly load in the order things appear in. + + We check to see if the specified NVP matches what the next automatically loaded node is. If they + match, we just continue as normal, going in order. If they don't match, we attempt to find a node + named after the NVP that is being loaded. If that NVP does not exist, we throw an exception. */ + void startNode() + { + auto next = itsNodes.top().child; // By default we would move to the next child node + auto const expectedName = itsNodes.top().name; // this is the expected name from the NVP, if provided + + // If we were given an NVP name, look for it in the current level of the document. + // We only need to do this if either we have exhausted the siblings of the current level or + // the NVP name does not match the name of the node we would normally read next + if( expectedName && ( next == nullptr || std::strcmp( next->name(), expectedName ) != 0 ) ) + { + next = itsNodes.top().search( expectedName ); + + if( next == nullptr ) + throw Exception("XML Parsing failed - provided NVP (" + std::string(expectedName) + ") not found"); + } + + itsNodes.emplace( next ); + } + + //! Finishes reading the current node + void finishNode() + { + // remove current + itsNodes.pop(); + + // advance parent + itsNodes.top().advance(); + + // Reset name + itsNodes.top().name = nullptr; + } + + //! Retrieves the current node name + //! will return @c nullptr if the node does not have a name + const char * getNodeName() const + { + return itsNodes.top().getChildName(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNodes.top().name = name; + } + + //! Loads a bool from the current top node + template ::value, + std::is_same::value> = traits::sfinae> inline + void loadValue( T & value ) + { + std::istringstream is( itsNodes.top().node->value() ); + is.setf( std::ios::boolalpha ); + is >> value; + } + + //! Loads a char (signed or unsigned) from the current top node + template ::value, + !std::is_same::value, + sizeof(T) == sizeof(char)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = *reinterpret_cast( itsNodes.top().node->value() ); + } + + //! Load an int8_t from the current top node (ensures we parse entire number) + void loadValue( int8_t & value ) + { + int32_t val; loadValue( val ); value = static_cast( val ); + } + + //! Load a uint8_t from the current top node (ensures we parse entire number) + void loadValue( uint8_t & value ) + { + uint32_t val; loadValue( val ); value = static_cast( val ); + } + + //! Loads a type best represented as an unsigned long from the current top node + template ::value, + !std::is_same::value, + !std::is_same::value, + !std::is_same::value, + sizeof(T) < sizeof(long long)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = static_cast( std::stoul( itsNodes.top().node->value() ) ); + } + + //! Loads a type best represented as an unsigned long long from the current top node + template ::value, + !std::is_same::value, + sizeof(T) >= sizeof(long long)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = static_cast( std::stoull( itsNodes.top().node->value() ) ); + } + + //! Loads a type best represented as an int from the current top node + template ::value, + !std::is_same::value, + sizeof(T) <= sizeof(int)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = static_cast( std::stoi( itsNodes.top().node->value() ) ); + } + + //! Loads a type best represented as a long from the current top node + template ::value, + (sizeof(T) > sizeof(int)), + sizeof(T) <= sizeof(long)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = static_cast( std::stol( itsNodes.top().node->value() ) ); + } + + //! Loads a type best represented as a long long from the current top node + template ::value, + (sizeof(T) > sizeof(long)), + sizeof(T) <= sizeof(long long)> = traits::sfinae> inline + void loadValue( T & value ) + { + value = static_cast( std::stoll( itsNodes.top().node->value() ) ); + } + + //! Loads a type best represented as a float from the current top node + void loadValue( float & value ) + { + try + { + value = std::stof( itsNodes.top().node->value() ); + } + catch( std::out_of_range const & ) + { + // special case for denormalized values + std::istringstream is( itsNodes.top().node->value() ); + is >> value; + if( std::fpclassify( value ) != FP_SUBNORMAL ) + throw; + } + } + + //! Loads a type best represented as a double from the current top node + void loadValue( double & value ) + { + try + { + value = std::stod( itsNodes.top().node->value() ); + } + catch( std::out_of_range const & ) + { + // special case for denormalized values + std::istringstream is( itsNodes.top().node->value() ); + is >> value; + if( std::fpclassify( value ) != FP_SUBNORMAL ) + throw; + } + } + + //! Loads a type best represented as a long double from the current top node + void loadValue( long double & value ) + { + try + { + value = std::stold( itsNodes.top().node->value() ); + } + catch( std::out_of_range const & ) + { + // special case for denormalized values + std::istringstream is( itsNodes.top().node->value() ); + is >> value; + if( std::fpclassify( value ) != FP_SUBNORMAL ) + throw; + } + } + + //! Loads a string from the current node from the current top node + template inline + void loadValue( std::basic_string & str ) + { + std::basic_istringstream is( itsNodes.top().node->value() ); + + str.assign( std::istreambuf_iterator( is ), + std::istreambuf_iterator() ); + } + + //! Loads the size of the current top node + template inline + void loadSize( T & value ) + { + value = getNumChildren( itsNodes.top().node ); + } + + protected: + //! Gets the number of children (usually interpreted as size) for the specified node + static size_t getNumChildren( rapidxml::xml_node<> * node ) + { + size_t size = 0; + node = node->first_node(); // get first child + + while( node != nullptr ) + { + ++size; + node = node->next_sibling(); + } + + return size; + } + + //! A struct that contains metadata about a node + /*! Keeps track of some top level node, its number of + remaining children, and the current active child node */ + struct NodeInfo + { + NodeInfo( rapidxml::xml_node<> * n = nullptr ) : + node( n ), + child( n->first_node() ), + size( XMLInputArchive::getNumChildren( n ) ), + name( nullptr ) + { } + + //! Advances to the next sibling node of the child + /*! If this is the last sibling child will be null after calling */ + void advance() + { + if( size > 0 ) + { + --size; + child = child->next_sibling(); + } + } + + //! Searches for a child with the given name in this node + /*! @param searchName The name to search for (must be null terminated) + @return The node if found, nullptr otherwise */ + rapidxml::xml_node<> * search( const char * searchName ) + { + if( searchName ) + { + size_t new_size = XMLInputArchive::getNumChildren( node ); + const size_t name_size = rapidxml::internal::measure( searchName ); + + for( auto new_child = node->first_node(); new_child != nullptr; new_child = new_child->next_sibling() ) + { + if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) ) + { + size = new_size; + child = new_child; + + return new_child; + } + --new_size; + } + } + + return nullptr; + } + + //! Returns the actual name of the next child node, if it exists + const char * getChildName() const + { + return child ? child->name() : nullptr; + } + + rapidxml::xml_node<> * node; //!< A pointer to this node + rapidxml::xml_node<> * child; //!< A pointer to its current child + size_t size; //!< The remaining number of children for this node + const char * name; //!< The NVP name for next child node + }; // NodeInfo + + //! @} + + private: + std::vector itsData; //!< The raw data loaded + rapidxml::xml_document<> itsXML; //!< The XML document + std::stack itsNodes; //!< A stack of nodes read from the document + }; + + // ###################################################################### + // XMLArchive prologue and epilogue functions + // ###################################################################### + + // ###################################################################### + //! Prologue for NVPs for XML output archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void prologue( XMLOutputArchive &, NameValuePair const & ) + { } + + //! Prologue for NVPs for XML input archives + template inline + void prologue( XMLInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Epilogue for NVPs for XML output archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void epilogue( XMLOutputArchive &, NameValuePair const & ) + { } + + //! Epilogue for NVPs for XML input archives + template inline + void epilogue( XMLInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Prologue for SizeTags for XML output archives + /*! SizeTags do not start or finish nodes */ + template inline + void prologue( XMLOutputArchive & ar, SizeTag const & ) + { + ar.appendAttribute( "size", "dynamic" ); + } + + template inline + void prologue( XMLInputArchive &, SizeTag const & ) + { } + + //! Epilogue for SizeTags for XML output archives + /*! SizeTags do not start or finish nodes */ + template inline + void epilogue( XMLOutputArchive &, SizeTag const & ) + { } + + template inline + void epilogue( XMLInputArchive &, SizeTag const & ) + { } + + // ###################################################################### + //! Prologue for all other types for XML output archives (except minimal types) + /*! Starts a new node, named either automatically or by some NVP, + that may be given data by the type about to be archived + + Minimal types do not start or end nodes */ + template ::value || + traits::has_minimal_output_serialization::value> = traits::sfinae> inline + void prologue( XMLOutputArchive & ar, T const & ) + { + ar.startNode(); + ar.insertType(); + } + + //! Prologue for all other types for XML input archives (except minimal types) + template ::value || + traits::has_minimal_input_serialization::value> = traits::sfinae> inline + void prologue( XMLInputArchive & ar, T const & ) + { + ar.startNode(); + } + + // ###################################################################### + //! Epilogue for all other types other for XML output archives (except minimal types) + /*! Finishes the node created in the prologue + + Minimal types do not start or end nodes */ + template ::value || + traits::has_minimal_output_serialization::value> = traits::sfinae> inline + void epilogue( XMLOutputArchive & ar, T const & ) + { + ar.finishNode(); + } + + //! Epilogue for all other types other for XML output archives (except minimal types) + template ::value || + traits::has_minimal_input_serialization::value> = traits::sfinae> inline + void epilogue( XMLInputArchive & ar, T const & ) + { + ar.finishNode(); + } + + // ###################################################################### + // Common XMLArchive serialization functions + // ###################################################################### + + //! Saving NVP types to XML + template inline + void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive & ar, NameValuePair const & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + //! Loading NVP types from XML + template inline + void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, NameValuePair & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + // ###################################################################### + //! Saving SizeTags to XML + template inline + void CEREAL_SAVE_FUNCTION_NAME( XMLOutputArchive &, SizeTag const & ) + { } + + //! Loading SizeTags from XML + template inline + void CEREAL_LOAD_FUNCTION_NAME( XMLInputArchive & ar, SizeTag & st ) + { + ar.loadSize( st.size ); + } + + // ###################################################################### + //! Saving for POD types to xml + template ::value> = traits::sfinae> inline + void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, T const & t) + { + ar.saveValue( t ); + } + + //! Loading for POD types from xml + template ::value> = traits::sfinae> inline + void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, T & t) + { + ar.loadValue( t ); + } + + // ###################################################################### + //! saving string to xml + template inline + void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive & ar, std::basic_string const & str) + { + ar.saveValue( str ); + } + + //! loading string from xml + template inline + void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive & ar, std::basic_string & str) + { + ar.loadValue( str ); + } +} // namespace cereal + +// register archives for polymorphic support +CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive) +CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive) + +// tie input and output archives together +CEREAL_SETUP_ARCHIVE_TRAITS(cereal::XMLInputArchive, cereal::XMLOutputArchive) + +#endif // CEREAL_ARCHIVES_XML_HPP_ diff --git a/tpl/cereal/include/cereal/cereal.hpp b/tpl/cereal/include/cereal/cereal.hpp new file mode 100644 index 0000000..a777010 --- /dev/null +++ b/tpl/cereal/include/cereal/cereal.hpp @@ -0,0 +1,987 @@ +/*! \file cereal.hpp + \brief Main cereal functionality */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_CEREAL_HPP_ +#define CEREAL_CEREAL_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cereal/macros.hpp" +#include "cereal/details/traits.hpp" +#include "cereal/details/helpers.hpp" +#include "cereal/types/base_class.hpp" + +namespace cereal +{ + // ###################################################################### + //! Creates a name value pair + /*! @relates NameValuePair + @ingroup Utility */ + template inline + NameValuePair make_nvp( std::string const & name, T && value ) + { + return {name.c_str(), std::forward(value)}; + } + + //! Creates a name value pair + /*! @relates NameValuePair + @ingroup Utility */ + template inline + NameValuePair make_nvp( const char * name, T && value ) + { + return {name, std::forward(value)}; + } + + //! Creates a name value pair for the variable T with the same name as the variable + /*! @relates NameValuePair + @ingroup Utility */ + #define CEREAL_NVP(T) ::cereal::make_nvp(#T, T) + + // ###################################################################### + //! Convenience function to create binary data for both const and non const pointers + /*! @param data Pointer to beginning of the data + @param size The size in bytes of the data + @relates BinaryData + @ingroup Utility */ + template inline + BinaryData binary_data( T && data, size_t size ) + { + return {std::forward(data), size}; + } + + // ###################################################################### + //! Creates a size tag from some variable. + /*! Will normally be used to serialize size (e.g. size()) information for + variable size containers. If you have a variable sized container, + the very first thing it serializes should be its size, wrapped in + a SizeTag. + + @relates SizeTag + @ingroup Utility */ + template inline + SizeTag make_size_tag( T && sz ) + { + return {std::forward(sz)}; + } + + // ###################################################################### + //! Called before a type is serialized to set up any special archive state + //! for processing some type + /*! If designing a serializer that needs to set up any kind of special + state or output extra information for a type, specialize this function + for the archive type and the types that require the extra information. + @ingroup Internal */ + template inline + void prologue( Archive & /* archive */, T const & /* data */) + { } + + //! Called after a type is serialized to tear down any special archive state + //! for processing some type + /*! @ingroup Internal */ + template inline + void epilogue( Archive & /* archive */, T const & /* data */) + { } + + // ###################################################################### + //! Special flags for archives + /*! AllowEmptyClassElision + This allows for empty classes to be serialized even if they do not provide + a serialization function. Classes with no data members are considered to be + empty. Be warned that if this is enabled and you attempt to serialize an + empty class with improperly formed serialize or load/save functions, no + static error will occur - the error will propogate silently and your + intended serialization functions may not be called. You can manually + ensure that your classes that have custom serialization are correct + by using the traits is_output_serializable and is_input_serializable + in cereal/details/traits.hpp. + @ingroup Internal */ + enum Flags { AllowEmptyClassElision = 1 }; + + // ###################################################################### + //! Registers a specific Archive type with cereal + /*! This registration should be done once per archive. A good place to + put this is immediately following the definition of your archive. + Archive registration is only strictly necessary if you wish to + support pointers to polymorphic data types. All archives that + come with cereal are already registered. + @ingroup Internal */ + #define CEREAL_REGISTER_ARCHIVE(Archive) \ + namespace cereal { namespace detail { \ + template \ + typename polymorphic_serialization_support::type \ + instantiate_polymorphic_binding( T*, Archive*, BindingTag, adl_tag ); \ + } } /* end namespaces */ + + // ###################################################################### + //! Defines a class version for some type + /*! Versioning information is optional and adds some small amount of + overhead to serialization. This overhead will occur both in terms of + space in the archive (the version information for each class will be + stored exactly once) as well as runtime (versioned serialization functions + must check to see if they need to load or store version information). + + Versioning is useful if you plan on fundamentally changing the way some + type is serialized in the future. Versioned serialization functions + cannot be used to load non-versioned data. + + By default, all types have an assumed version value of zero. By + using this macro, you may change the version number associated with + some type. cereal will then use this value as a second parameter + to your serialization functions. + + The interface for the serialization functions is nearly identical + to non-versioned serialization with the addition of a second parameter, + const std::uint32_t version, which will be supplied with the correct + version number. Serializing the version number on a save happens + automatically. + + Versioning cannot be mixed with non-versioned serialization functions. + Having both types will result result in a compile time error. Data + serialized without versioning cannot be loaded by a serialization + function with added versioning support. + + Example interface for versioning on a non-member serialize function: + + @code{cpp} + CEREAL_CLASS_VERSION( Mytype, 77 ); // register class version + + template + void serialize( Archive & ar, Mytype & t, const std::uint32_t version ) + { + // When performing a load, the version associated with the class + // is whatever it was when that data was originally serialized + // + // When we save, we'll use the version that is defined in the macro + + if( version >= some_number ) + // do this + else + // do that + } + @endcode + + Interfaces for other forms of serialization functions is similar. This + macro should be placed at global scope. + @ingroup Utility */ + #define CEREAL_CLASS_VERSION(TYPE, VERSION_NUMBER) \ + namespace cereal { namespace detail { \ + template <> struct Version \ + { \ + static const std::uint32_t version; \ + static std::uint32_t registerVersion() \ + { \ + ::cereal::detail::StaticObject::getInstance().mapping.emplace( \ + std::type_index(typeid(TYPE)).hash_code(), VERSION_NUMBER ); \ + return VERSION_NUMBER; \ + } \ + static void unused() { (void)version; } \ + }; /* end Version */ \ + const std::uint32_t Version::version = \ + Version::registerVersion(); \ + } } // end namespaces + + // ###################################################################### + //! The base output archive class + /*! This is the base output archive for all output archives. If you create + a custom archive class, it should derive from this, passing itself as + a template parameter for the ArchiveType. + + The base class provides all of the functionality necessary to + properly forward data to the correct serialization functions. + + Individual archives should use a combination of prologue and + epilogue functions together with specializations of serialize, save, + and load to alter the functionality of their serialization. + + @tparam ArchiveType The archive type that derives from OutputArchive + @tparam Flags Flags to control advanced functionality. See the Flags + enum for more information. + @ingroup Internal */ + template + class OutputArchive : public detail::OutputArchiveBase + { + public: + //! Construct the output archive + /*! @param derived A pointer to the derived ArchiveType (pass this from the derived archive) */ + OutputArchive(ArchiveType * const derived) : self(derived), itsCurrentPointerId(1), itsCurrentPolymorphicTypeId(1) + { } + + OutputArchive & operator=( OutputArchive const & ) = delete; + + //! Serializes all passed in data + /*! This is the primary interface for serializing data with an archive */ + template inline + ArchiveType & operator()( Types && ... args ) + { + self->process( std::forward( args )... ); + return *self; + } + + /*! @name Boost Transition Layer + Functionality that mirrors the syntax for Boost. This is useful if you are transitioning + a large project from Boost to cereal. The preferred interface for cereal is using operator(). */ + //! @{ + + //! Indicates this archive is not intended for loading + /*! This ensures compatibility with boost archive types. If you are transitioning + from boost, you can check this value within a member or external serialize function + (i.e., Archive::is_loading::value) to disable behavior specific to loading, until + you can transition to split save/load or save_minimal/load_minimal functions */ + using is_loading = std::false_type; + + //! Indicates this archive is intended for saving + /*! This ensures compatibility with boost archive types. If you are transitioning + from boost, you can check this value within a member or external serialize function + (i.e., Archive::is_saving::value) to enable behavior specific to loading, until + you can transition to split save/load or save_minimal/load_minimal functions */ + using is_saving = std::true_type; + + //! Serializes passed in data + /*! This is a boost compatability layer and is not the preferred way of using + cereal. If you are transitioning from boost, use this until you can + transition to the operator() overload */ + template inline + ArchiveType & operator&( T && arg ) + { + self->process( std::forward( arg ) ); + return *self; + } + + //! Serializes passed in data + /*! This is a boost compatability layer and is not the preferred way of using + cereal. If you are transitioning from boost, use this until you can + transition to the operator() overload */ + template inline + ArchiveType & operator<<( T && arg ) + { + self->process( std::forward( arg ) ); + return *self; + } + + //! @} + + //! Registers a shared pointer with the archive + /*! This function is used to track shared pointer targets to prevent + unnecessary saves from taking place if multiple shared pointers + point to the same data. + + @internal + @param addr The address (see shared_ptr get()) pointed to by the shared pointer + @return A key that uniquely identifies the pointer */ + inline std::uint32_t registerSharedPointer( void const * addr ) + { + // Handle null pointers by just returning 0 + if(addr == 0) return 0; + + auto id = itsSharedPointerMap.find( addr ); + if( id == itsSharedPointerMap.end() ) + { + auto ptrId = itsCurrentPointerId++; + itsSharedPointerMap.insert( {addr, ptrId} ); + return ptrId | detail::msb_32bit; // mask MSB to be 1 + } + else + return id->second; + } + + //! Registers a polymorphic type name with the archive + /*! This function is used to track polymorphic types to prevent + unnecessary saves of identifying strings used by the polymorphic + support functionality. + + @internal + @param name The name to associate with a polymorphic type + @return A key that uniquely identifies the polymorphic type name */ + inline std::uint32_t registerPolymorphicType( char const * name ) + { + auto id = itsPolymorphicTypeMap.find( name ); + if( id == itsPolymorphicTypeMap.end() ) + { + auto polyId = itsCurrentPolymorphicTypeId++; + itsPolymorphicTypeMap.insert( {name, polyId} ); + return polyId | detail::msb_32bit; // mask MSB to be 1 + } + else + return id->second; + } + + private: + //! Serializes data after calling prologue, then calls epilogue + template inline + void process( T && head ) + { + prologue( *self, head ); + self->processImpl( head ); + epilogue( *self, head ); + } + + //! Unwinds to process all data + template inline + void process( T && head, Other && ... tail ) + { + self->process( std::forward( head ) ); + self->process( std::forward( tail )... ); + } + + //! Serialization of a virtual_base_class wrapper + /*! \sa virtual_base_class */ + template inline + ArchiveType & processImpl(virtual_base_class const & b) + { + traits::detail::base_class_id id(b.base_ptr); + if(itsBaseClassSet.count(id) == 0) + { + itsBaseClassSet.insert(id); + self->processImpl( *b.base_ptr ); + } + return *self; + } + + //! Serialization of a base_class wrapper + /*! \sa base_class */ + template inline + ArchiveType & processImpl(base_class const & b) + { + self->processImpl( *b.base_ptr ); + return *self; + } + + //! Helper macro that expands the requirements for activating an overload + /*! Requirements: + Has the requested serialization function + Does not have version and unversioned at the same time + Is output serializable AND + is specialized for this type of function OR + has no specialization at all */ + #define PROCESS_IF(name) \ + traits::EnableIf::value, \ + !traits::has_invalid_output_versioning::value, \ + (traits::is_output_serializable::value && \ + (traits::is_specialized_##name::value || \ + !traits::is_specialized::value))> = traits::sfinae + + //! Member serialization + template inline + ArchiveType & processImpl(T const & t) + { + access::member_serialize(*self, const_cast(t)); + return *self; + } + + //! Non member serialization + template inline + ArchiveType & processImpl(T const & t) + { + CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast(t)); + return *self; + } + + //! Member split (save) + template inline + ArchiveType & processImpl(T const & t) + { + access::member_save(*self, t); + return *self; + } + + //! Non member split (save) + template inline + ArchiveType & processImpl(T const & t) + { + CEREAL_SAVE_FUNCTION_NAME(*self, t); + return *self; + } + + //! Member split (save_minimal) + template inline + ArchiveType & processImpl(T const & t) + { + self->process( access::member_save_minimal(*self, t) ); + return *self; + } + + //! Non member split (save_minimal) + template inline + ArchiveType & processImpl(T const & t) + { + self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t) ); + return *self; + } + + //! Empty class specialization + template ::value, + std::is_empty::value> = traits::sfinae> inline + ArchiveType & processImpl(T const &) + { + return *self; + } + + //! No matching serialization + /*! Invalid if we have invalid output versioning or + we are not output serializable, and either + don't allow empty class ellision or allow it but are not serializing an empty class */ + template ::value || + (!traits::is_output_serializable::value && + (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline + ArchiveType & processImpl(T const &) + { + static_assert(traits::detail::count_output_serializers::value != 0, + "cereal could not find any output serialization functions for the provided type and archive combination. \n\n " + "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " + "Serialize functions generally have the following signature: \n\n " + "template \n " + " void serialize(Archive & ar) \n " + " { \n " + " ar( member1, member2, member3 ); \n " + " } \n\n " ); + + static_assert(traits::detail::count_output_serializers::value < 2, + "cereal found more than one compatible output serialization function for the provided type and archive combination. \n\n " + "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " + "Use specialization (see access.hpp) if you need to disambiguate between serialize vs load/save functions. \n " + "Note that serialization functions can be inherited which may lead to the aforementioned ambiguities. \n " + "In addition, you may not mix versioned with non-versioned serialization functions. \n\n "); + + return *self; + } + + //! Registers a class version with the archive and serializes it if necessary + /*! If this is the first time this class has been serialized, we will record its + version number and serialize that. + + @tparam T The type of the class being serialized + @param version The version number associated with it */ + template inline + std::uint32_t registerClassVersion() + { + static const auto hash = std::type_index(typeid(T)).hash_code(); + const auto insertResult = itsVersionedTypes.insert( hash ); + const auto lock = detail::StaticObject::lock(); + const auto version = + detail::StaticObject::getInstance().find( hash, detail::Version::version ); + + if( insertResult.second ) // insertion took place, serialize the version number + process( make_nvp("cereal_class_version", version) ); + + return version; + } + + //! Member serialization + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + access::member_serialize(*self, const_cast(t), registerClassVersion()); + return *self; + } + + //! Non member serialization + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + CEREAL_SERIALIZE_FUNCTION_NAME(*self, const_cast(t), registerClassVersion()); + return *self; + } + + //! Member split (save) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + access::member_save(*self, t, registerClassVersion()); + return *self; + } + + //! Non member split (save) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + CEREAL_SAVE_FUNCTION_NAME(*self, t, registerClassVersion()); + return *self; + } + + //! Member split (save_minimal) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + self->process( access::member_save_minimal(*self, t, registerClassVersion()) ); + return *self; + } + + //! Non member split (save_minimal) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T const & t) + { + self->process( CEREAL_SAVE_MINIMAL_FUNCTION_NAME(*self, t, registerClassVersion()) ); + return *self; + } + + #undef PROCESS_IF + + private: + ArchiveType * const self; + + //! A set of all base classes that have been serialized + std::unordered_set itsBaseClassSet; + + //! Maps from addresses to pointer ids + std::unordered_map itsSharedPointerMap; + + //! The id to be given to the next pointer + std::uint32_t itsCurrentPointerId; + + //! Maps from polymorphic type name strings to ids + std::unordered_map itsPolymorphicTypeMap; + + //! The id to be given to the next polymorphic type name + std::uint32_t itsCurrentPolymorphicTypeId; + + //! Keeps track of classes that have versioning information associated with them + std::unordered_set itsVersionedTypes; + }; // class OutputArchive + + // ###################################################################### + //! The base input archive class + /*! This is the base input archive for all input archives. If you create + a custom archive class, it should derive from this, passing itself as + a template parameter for the ArchiveType. + + The base class provides all of the functionality necessary to + properly forward data to the correct serialization functions. + + Individual archives should use a combination of prologue and + epilogue functions together with specializations of serialize, save, + and load to alter the functionality of their serialization. + + @tparam ArchiveType The archive type that derives from InputArchive + @tparam Flags Flags to control advanced functionality. See the Flags + enum for more information. + @ingroup Internal */ + template + class InputArchive : public detail::InputArchiveBase + { + public: + //! Construct the output archive + /*! @param derived A pointer to the derived ArchiveType (pass this from the derived archive) */ + InputArchive(ArchiveType * const derived) : + self(derived), + itsBaseClassSet(), + itsSharedPointerMap(), + itsPolymorphicTypeMap(), + itsVersionedTypes() + { } + + InputArchive & operator=( InputArchive const & ) = delete; + + //! Serializes all passed in data + /*! This is the primary interface for serializing data with an archive */ + template inline + ArchiveType & operator()( Types && ... args ) + { + process( std::forward( args )... ); + return *self; + } + + /*! @name Boost Transition Layer + Functionality that mirrors the syntax for Boost. This is useful if you are transitioning + a large project from Boost to cereal. The preferred interface for cereal is using operator(). */ + //! @{ + + //! Indicates this archive is intended for loading + /*! This ensures compatibility with boost archive types. If you are transitioning + from boost, you can check this value within a member or external serialize function + (i.e., Archive::is_loading::value) to enable behavior specific to loading, until + you can transition to split save/load or save_minimal/load_minimal functions */ + using is_loading = std::true_type; + + //! Indicates this archive is not intended for saving + /*! This ensures compatibility with boost archive types. If you are transitioning + from boost, you can check this value within a member or external serialize function + (i.e., Archive::is_saving::value) to disable behavior specific to loading, until + you can transition to split save/load or save_minimal/load_minimal functions */ + using is_saving = std::false_type; + + //! Serializes passed in data + /*! This is a boost compatability layer and is not the preferred way of using + cereal. If you are transitioning from boost, use this until you can + transition to the operator() overload */ + template inline + ArchiveType & operator&( T && arg ) + { + self->process( std::forward( arg ) ); + return *self; + } + + //! Serializes passed in data + /*! This is a boost compatability layer and is not the preferred way of using + cereal. If you are transitioning from boost, use this until you can + transition to the operator() overload */ + template inline + ArchiveType & operator>>( T && arg ) + { + self->process( std::forward( arg ) ); + return *self; + } + + //! @} + + //! Retrieves a shared pointer given a unique key for it + /*! This is used to retrieve a previously registered shared_ptr + which has already been loaded. + + @param id The unique id that was serialized for the pointer + @return A shared pointer to the data + @throw Exception if the id does not exist */ + inline std::shared_ptr getSharedPointer(std::uint32_t const id) + { + if(id == 0) return std::shared_ptr(nullptr); + + auto iter = itsSharedPointerMap.find( id ); + if(iter == itsSharedPointerMap.end()) + throw Exception("Error while trying to deserialize a smart pointer. Could not find id " + std::to_string(id)); + + return iter->second; + } + + //! Registers a shared pointer to its unique identifier + /*! After a shared pointer has been allocated for the first time, it should + be registered with its loaded id for future references to it. + + @param id The unique identifier for the shared pointer + @param ptr The actual shared pointer */ + inline void registerSharedPointer(std::uint32_t const id, std::shared_ptr ptr) + { + std::uint32_t const stripped_id = id & ~detail::msb_32bit; + itsSharedPointerMap[stripped_id] = ptr; + } + + //! Retrieves the string for a polymorphic type given a unique key for it + /*! This is used to retrieve a string previously registered during + a polymorphic load. + + @param id The unique id that was serialized for the polymorphic type + @return The string identifier for the tyep */ + inline std::string getPolymorphicName(std::uint32_t const id) + { + auto name = itsPolymorphicTypeMap.find( id ); + if(name == itsPolymorphicTypeMap.end()) + { + throw Exception("Error while trying to deserialize a polymorphic pointer. Could not find type id " + std::to_string(id)); + } + return name->second; + } + + //! Registers a polymorphic name string to its unique identifier + /*! After a polymorphic type has been loaded for the first time, it should + be registered with its loaded id for future references to it. + + @param id The unique identifier for the polymorphic type + @param name The name associated with the tyep */ + inline void registerPolymorphicName(std::uint32_t const id, std::string const & name) + { + std::uint32_t const stripped_id = id & ~detail::msb_32bit; + itsPolymorphicTypeMap.insert( {stripped_id, name} ); + } + + private: + //! Serializes data after calling prologue, then calls epilogue + template inline + void process( T && head ) + { + prologue( *self, head ); + self->processImpl( head ); + epilogue( *self, head ); + } + + //! Unwinds to process all data + template inline + void process( T && head, Other && ... tail ) + { + process( std::forward( head ) ); + process( std::forward( tail )... ); + } + + //! Serialization of a virtual_base_class wrapper + /*! \sa virtual_base_class */ + template inline + ArchiveType & processImpl(virtual_base_class & b) + { + traits::detail::base_class_id id(b.base_ptr); + if(itsBaseClassSet.count(id) == 0) + { + itsBaseClassSet.insert(id); + self->processImpl( *b.base_ptr ); + } + return *self; + } + + //! Serialization of a base_class wrapper + /*! \sa base_class */ + template inline + ArchiveType & processImpl(base_class & b) + { + self->processImpl( *b.base_ptr ); + return *self; + } + + //! Helper macro that expands the requirements for activating an overload + /*! Requirements: + Has the requested serialization function + Does not have version and unversioned at the same time + Is input serializable AND + is specialized for this type of function OR + has no specialization at all */ + #define PROCESS_IF(name) \ + traits::EnableIf::value, \ + !traits::has_invalid_input_versioning::value, \ + (traits::is_input_serializable::value && \ + (traits::is_specialized_##name::value || \ + !traits::is_specialized::value))> = traits::sfinae + + //! Member serialization + template inline + ArchiveType & processImpl(T & t) + { + access::member_serialize(*self, t); + return *self; + } + + //! Non member serialization + template inline + ArchiveType & processImpl(T & t) + { + CEREAL_SERIALIZE_FUNCTION_NAME(*self, t); + return *self; + } + + //! Member split (load) + template inline + ArchiveType & processImpl(T & t) + { + access::member_load(*self, t); + return *self; + } + + //! Non member split (load) + template inline + ArchiveType & processImpl(T & t) + { + CEREAL_LOAD_FUNCTION_NAME(*self, t); + return *self; + } + + //! Member split (load_minimal) + template inline + ArchiveType & processImpl(T & t) + { + using OutArchiveType = typename traits::detail::get_output_from_input::type; + typename traits::has_member_save_minimal::type value; + self->process( value ); + access::member_load_minimal(*self, t, value); + return *self; + } + + //! Non member split (load_minimal) + template inline + ArchiveType & processImpl(T & t) + { + using OutArchiveType = typename traits::detail::get_output_from_input::type; + typename traits::has_non_member_save_minimal::type value; + self->process( value ); + CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value); + return *self; + } + + //! Empty class specialization + template ::value, + std::is_empty::value> = traits::sfinae> inline + ArchiveType & processImpl(T const &) + { + return *self; + } + + //! No matching serialization + /*! Invalid if we have invalid input versioning or + we are not input serializable, and either + don't allow empty class ellision or allow it but are not serializing an empty class */ + template ::value || + (!traits::is_input_serializable::value && + (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !std::is_empty::value)))> = traits::sfinae> inline + ArchiveType & processImpl(T const &) + { + static_assert(traits::detail::count_input_serializers::value != 0, + "cereal could not find any input serialization functions for the provided type and archive combination. \n\n " + "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " + "Serialize functions generally have the following signature: \n\n " + "template \n " + " void serialize(Archive & ar) \n " + " { \n " + " ar( member1, member2, member3 ); \n " + " } \n\n " ); + + static_assert(traits::detail::count_input_serializers::value < 2, + "cereal found more than one compatible input serialization function for the provided type and archive combination. \n\n " + "Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). \n " + "Use specialization (see access.hpp) if you need to disambiguate between serialize vs load/save functions. \n " + "Note that serialization functions can be inherited which may lead to the aforementioned ambiguities. \n " + "In addition, you may not mix versioned with non-versioned serialization functions. \n\n "); + + return *self; + } + + //! Befriend for versioning in load_and_construct + template friend struct detail::Construct; + + //! Registers a class version with the archive and serializes it if necessary + /*! If this is the first time this class has been serialized, we will record its + version number and serialize that. + + @tparam T The type of the class being serialized + @param version The version number associated with it */ + template inline + std::uint32_t loadClassVersion() + { + static const auto hash = std::type_index(typeid(T)).hash_code(); + auto lookupResult = itsVersionedTypes.find( hash ); + + if( lookupResult != itsVersionedTypes.end() ) // already exists + return lookupResult->second; + else // need to load + { + std::uint32_t version; + + process( make_nvp("cereal_class_version", version) ); + itsVersionedTypes.emplace_hint( lookupResult, hash, version ); + + return version; + } + } + + //! Member serialization + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + const auto version = loadClassVersion(); + access::member_serialize(*self, t, version); + return *self; + } + + //! Non member serialization + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + const auto version = loadClassVersion(); + CEREAL_SERIALIZE_FUNCTION_NAME(*self, t, version); + return *self; + } + + //! Member split (load) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + const auto version = loadClassVersion(); + access::member_load(*self, t, version); + return *self; + } + + //! Non member split (load) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + const auto version = loadClassVersion(); + CEREAL_LOAD_FUNCTION_NAME(*self, t, version); + return *self; + } + + //! Member split (load_minimal) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + using OutArchiveType = typename traits::detail::get_output_from_input::type; + const auto version = loadClassVersion(); + typename traits::has_member_versioned_save_minimal::type value; + self->process(value); + access::member_load_minimal(*self, t, value, version); + return *self; + } + + //! Non member split (load_minimal) + /*! Versioning implementation */ + template inline + ArchiveType & processImpl(T & t) + { + using OutArchiveType = typename traits::detail::get_output_from_input::type; + const auto version = loadClassVersion(); + typename traits::has_non_member_versioned_save_minimal::type value; + self->process(value); + CEREAL_LOAD_MINIMAL_FUNCTION_NAME(*self, t, value, version); + return *self; + } + + #undef PROCESS_IF + + private: + ArchiveType * const self; + + //! A set of all base classes that have been serialized + std::unordered_set itsBaseClassSet; + + //! Maps from pointer ids to metadata + std::unordered_map> itsSharedPointerMap; + + //! Maps from name ids to names + std::unordered_map itsPolymorphicTypeMap; + + //! Maps from type hash codes to version numbers + std::unordered_map itsVersionedTypes; + }; // class InputArchive +} // namespace cereal + +// This include needs to come after things such as binary_data, make_nvp, etc +#include "cereal/types/common.hpp" + +#endif // CEREAL_CEREAL_HPP_ diff --git a/tpl/cereal/include/cereal/details/helpers.hpp b/tpl/cereal/include/cereal/details/helpers.hpp new file mode 100644 index 0000000..9092688 --- /dev/null +++ b/tpl/cereal/include/cereal/details/helpers.hpp @@ -0,0 +1,383 @@ +/*! \file helpers.hpp + \brief Internal helper functionality + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_DETAILS_HELPERS_HPP_ +#define CEREAL_DETAILS_HELPERS_HPP_ + +#include +#include +#include +#include +#include +#include + +#include "cereal/macros.hpp" +#include "cereal/details/static_object.hpp" + +namespace cereal +{ + // ###################################################################### + //! An exception class thrown when things go wrong at runtime + /*! @ingroup Utility */ + struct Exception : public std::runtime_error + { + explicit Exception( const std::string & what_ ) : std::runtime_error(what_) {} + explicit Exception( const char * what_ ) : std::runtime_error(what_) {} + }; + + // ###################################################################### + //! The size type used by cereal + /*! To ensure compatability between 32, 64, etc bit machines, we need to use + a fixed size type instead of size_t, which may vary from machine to + machine. + + The default value for CEREAL_SIZE_TYPE is specified in cereal/macros.hpp */ + using size_type = CEREAL_SIZE_TYPE; + + // forward decls + class BinaryOutputArchive; + class BinaryInputArchive; + + // ###################################################################### + namespace detail + { + struct NameValuePairCore {}; //!< Traits struct for NVPs + } + + //! For holding name value pairs + /*! This pairs a name (some string) with some value such that an archive + can potentially take advantage of the pairing. + + In serialization functions, NameValuePairs are usually created like so: + @code{.cpp} + struct MyStruct + { + int a, b, c, d, e; + + template + void serialize(Archive & archive) + { + archive( CEREAL_NVP(a), + CEREAL_NVP(b), + CEREAL_NVP(c), + CEREAL_NVP(d), + CEREAL_NVP(e) ); + } + }; + @endcode + + Alternatively, you can give you data members custom names like so: + @code{.cpp} + struct MyStruct + { + int a, b, my_embarrassing_variable_name, d, e; + + template + void serialize(Archive & archive) + { + archive( CEREAL_NVP(a), + CEREAL_NVP(b), + cereal::make_nvp("var", my_embarrassing_variable_name) ); + CEREAL_NVP(d), + CEREAL_NVP(e) ); + } + }; + @endcode + + There is a slight amount of overhead to creating NameValuePairs, so there + is a third method which will elide the names when they are not used by + the Archive: + + @code{.cpp} + struct MyStruct + { + int a, b; + + template + void serialize(Archive & archive) + { + archive( cereal::make_nvp(a), + cereal::make_nvp(b) ); + } + }; + @endcode + + This third method is generally only used when providing generic type + support. Users writing their own serialize functions will normally + explicitly control whether they want to use NVPs or not. + + @internal */ + template + class NameValuePair : detail::NameValuePairCore + { + private: + // If we get passed an array, keep the type as is, otherwise store + // a reference if we were passed an l value reference, else copy the value + using Type = typename std::conditional::type>::value, + typename std::remove_cv::type, + typename std::conditional::value, + T, + typename std::decay::type>::type>::type; + + // prevent nested nvps + static_assert( !std::is_base_of::value, + "Cannot pair a name to a NameValuePair" ); + + NameValuePair & operator=( NameValuePair const & ) = delete; + + public: + //! Constructs a new NameValuePair + /*! @param n The name of the pair + @param v The value to pair. Ideally this should be an l-value reference so that + the value can be both loaded and saved to. If you pass an r-value reference, + the NameValuePair will store a copy of it instead of a reference. Thus you should + only pass r-values in cases where this makes sense, such as the result of some + size() call. + @internal */ + NameValuePair( char const * n, T && v ) : name(n), value(std::forward(v)) {} + + char const * name; + Type value; + }; + + //! A specialization of make_nvp<> that simply forwards the value for binary archives + /*! @relates NameValuePair + @internal */ + template inline + typename + std::enable_if::value || + std::is_same::value, + T && >::type + make_nvp( const char *, T && value ) + { + return std::forward(value); + } + + //! A specialization of make_nvp<> that actually creates an nvp for non-binary archives + /*! @relates NameValuePair + @internal */ + template inline + typename + std::enable_if::value && + !std::is_same::value, + NameValuePair >::type + make_nvp( const char * name, T && value) + { + return {name, std::forward(value)}; + } + + //! Convenience for creating a templated NVP + /*! For use in internal generic typing functions which have an + Archive type declared + @internal */ + #define CEREAL_NVP_(name, value) ::cereal::make_nvp(name, value) + + // ###################################################################### + //! A wrapper around data that can be serialized in a binary fashion + /*! This class is used to demarcate data that can safely be serialized + as a binary chunk of data. Individual archives can then choose how + best represent this during serialization. + + @internal */ + template + struct BinaryData + { + //! Internally store the pointer as a void *, keeping const if created with + //! a const pointer + using PT = typename std::conditional::type>::value, + const void *, + void *>::type; + + BinaryData( T && d, uint64_t s ) : data(std::forward(d)), size(s) {} + + PT data; //!< pointer to beginning of data + uint64_t size; //!< size in bytes + }; + + // ###################################################################### + namespace detail + { + // base classes for type checking + /* The rtti virtual function only exists to enable an archive to + be used in a polymorphic fashion, if necessary. See the + archive adapters for an example of this */ + class OutputArchiveBase + { + public: + OutputArchiveBase() = default; + OutputArchiveBase( OutputArchiveBase && ) CEREAL_NOEXCEPT {} + OutputArchiveBase & operator=( OutputArchiveBase && ) CEREAL_NOEXCEPT { return *this; } + virtual ~OutputArchiveBase() CEREAL_NOEXCEPT = default; + + private: + virtual void rtti() {} + }; + + class InputArchiveBase + { + public: + InputArchiveBase() = default; + InputArchiveBase( InputArchiveBase && ) CEREAL_NOEXCEPT {} + InputArchiveBase & operator=( InputArchiveBase && ) CEREAL_NOEXCEPT { return *this; } + virtual ~InputArchiveBase() CEREAL_NOEXCEPT = default; + + private: + virtual void rtti() {} + }; + + // forward decls for polymorphic support + template struct polymorphic_serialization_support; + struct adl_tag; + + // used during saving pointers + static const int32_t msb_32bit = 0x80000000; + static const int32_t msb2_32bit = 0x40000000; + } + + // ###################################################################### + //! A wrapper around size metadata + /*! This class provides a way for archives to have more flexibility over how + they choose to serialize size metadata for containers. For some archive + types, the size may be implicitly encoded in the output (e.g. JSON) and + not need an explicit entry. Specializing serialize or load/save for + your archive and SizeTags allows you to choose what happens. + + @internal */ + template + class SizeTag + { + private: + // Store a reference if passed an lvalue reference, otherwise + // make a copy of the data + using Type = typename std::conditional::value, + T, + typename std::decay::type>::type; + + SizeTag & operator=( SizeTag const & ) = delete; + + public: + SizeTag( T && sz ) : size(std::forward(sz)) {} + + Type size; + }; + + // ###################################################################### + //! A wrapper around a key and value for serializing data into maps. + /*! This class just provides a grouping of keys and values into a struct for + human readable archives. For example, XML archives will use this wrapper + to write maps like so: + + @code{.xml} + + + MyFirstKey + MyFirstValue + + + MySecondKey + MySecondValue + + + @endcode + + \sa make_map_item + @internal */ + template + struct MapItem + { + using KeyType = typename std::conditional< + std::is_lvalue_reference::value, + Key, + typename std::decay::type>::type; + + using ValueType = typename std::conditional< + std::is_lvalue_reference::value, + Value, + typename std::decay::type>::type; + + //! Construct a MapItem from a key and a value + /*! @internal */ + MapItem( Key && key_, Value && value_ ) : key(std::forward(key_)), value(std::forward(value_)) {} + + MapItem & operator=( MapItem const & ) = delete; + + KeyType key; + ValueType value; + + //! Serialize the MapItem with the NVPs "key" and "value" + template inline + void CEREAL_SERIALIZE_FUNCTION_NAME(Archive & archive) + { + archive( make_nvp("key", key), + make_nvp("value", value) ); + } + }; + + //! Create a MapItem so that human readable archives will group keys and values together + /*! @internal + @relates MapItem */ + template inline + MapItem make_map_item(KeyType && key, ValueType && value) + { + return {std::forward(key), std::forward(value)}; + } + + namespace detail + { + //! Tag for Version, which due to its anonymous namespace, becomes a different + //! type in each translation unit + /*! This allows CEREAL_CLASS_VERSION to be safely called in a header file */ + namespace{ struct version_binding_tag {}; } + + // ###################################################################### + //! Version information class + /*! This is the base case for classes that have not been explicitly + registered */ + template struct Version + { + static const std::uint32_t version = 0; + // we don't need to explicitly register these types since they + // always get a version number of 0 + }; + + //! Holds all registered version information + struct Versions + { + std::unordered_map mapping; + + std::uint32_t find( std::size_t hash, std::uint32_t version ) + { + const auto result = mapping.emplace( hash, version ); + return result.first->second; + } + }; // struct Versions + } // namespace detail +} // namespace cereal + +#endif // CEREAL_DETAILS_HELPERS_HPP_ diff --git a/tpl/cereal/include/cereal/details/polymorphic_impl.hpp b/tpl/cereal/include/cereal/details/polymorphic_impl.hpp new file mode 100644 index 0000000..81128c8 --- /dev/null +++ b/tpl/cereal/include/cereal/details/polymorphic_impl.hpp @@ -0,0 +1,764 @@ +/*! \file polymorphic_impl.hpp + \brief Internal polymorphism support + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* This code is heavily inspired by the boost serialization implementation by the following authors + + (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt) + + See http://www.boost.org for updates, documentation, and revision history. + + (C) Copyright 2006 David Abrahams - http://www.boost.org. + + See /boost/serialization/export.hpp, /boost/archive/detail/register_archive.hpp, + and /boost/serialization/void_cast.hpp for their implementation. Additional details + found in other files split across serialization and archive. +*/ +#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_ +#define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_ + +#include "cereal/details/polymorphic_impl_fwd.hpp" +#include "cereal/details/static_object.hpp" +#include "cereal/types/memory.hpp" +#include "cereal/types/string.hpp" +#include +#include +#include +#include +#include +#include + +//! Binds a polymorhic type to all registered archives +/*! This binds a polymorphic type to all compatible registered archives that + have been registered with CEREAL_REGISTER_ARCHIVE. This must be called + after all archives are registered (usually after the archives themselves + have been included). */ +#define CEREAL_BIND_TO_ARCHIVES(...) \ + namespace cereal { \ + namespace detail { \ + template<> \ + struct init_binding<__VA_ARGS__> { \ + static bind_to_archives<__VA_ARGS__> const & b; \ + static void unused() { (void)b; } \ + }; \ + bind_to_archives<__VA_ARGS__> const & init_binding<__VA_ARGS__>::b = \ + ::cereal::detail::StaticObject< \ + bind_to_archives<__VA_ARGS__> \ + >::getInstance().bind(); \ + }} /* end namespaces */ + +namespace cereal +{ + /* Polymorphic casting support */ + namespace detail + { + //! Base type for polymorphic void casting + /*! Contains functions for casting between registered base and derived types. + + This is necessary so that cereal can properly cast between polymorphic types + even though void pointers are used, which normally have no type information. + Runtime type information is used instead to index a compile-time made mapping + that can perform the proper cast. In the case of multiple levels of inheritance, + cereal will attempt to find the shortest path by using registered relationships to + perform the cast. + + This class will be allocated as a StaticObject and only referenced by pointer, + allowing a templated derived version of it to define strongly typed functions + that cast between registered base and derived types. */ + struct PolymorphicCaster + { + PolymorphicCaster() = default; + PolymorphicCaster( const PolymorphicCaster & ) = default; + PolymorphicCaster & operator=( const PolymorphicCaster & ) = default; + PolymorphicCaster( PolymorphicCaster && ) CEREAL_NOEXCEPT {} + PolymorphicCaster & operator=( PolymorphicCaster && ) CEREAL_NOEXCEPT { return *this; } + virtual ~PolymorphicCaster() CEREAL_NOEXCEPT = default; + + //! Downcasts to the proper derived type + virtual void const * downcast( void const * const ptr ) const = 0; + //! Upcast to proper base type + virtual void * upcast( void * const ptr ) const = 0; + //! Upcast to proper base type, shared_ptr version + virtual std::shared_ptr upcast( std::shared_ptr const & ptr ) const = 0; + }; + + //! Holds registered mappings between base and derived types for casting + /*! This will be allocated as a StaticObject and holds a map containing + all registered mappings between base and derived types. */ + struct PolymorphicCasters + { + //! Maps from base type index to a map from derived type index to caster + std::map>> map; + + std::multimap reverseMap; + + //! Error message used for unregistered polymorphic casts + #define UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(LoadSave) \ + throw cereal::Exception("Trying to " #LoadSave " a registered polymorphic type with an unregistered polymorphic cast.\n" \ + "Could not find a path to a base class (" + util::demangle(baseInfo.name()) + ") for type: " + ::cereal::util::demangledName() + "\n" \ + "Make sure you either serialize the base class at some point via cereal::base_class or cereal::virtual_base_class.\n" \ + "Alternatively, manually register the association with CEREAL_REGISTER_POLYMORPHIC_RELATION."); + + //! Checks if the mapping object that can perform the upcast or downcast + /*! Uses the type index from the base and derived class to find the matching + registered caster. If no matching caster exists, returns false. */ + static bool exists( std::type_index const & baseIndex, std::type_index const & derivedIndex ) + { + // First phase of lookup - match base type index + auto const & baseMap = StaticObject::getInstance().map; + auto baseIter = baseMap.find( baseIndex ); + if (baseIter == baseMap.end()) + return false; + + // Second phase - find a match from base to derived + auto & derivedMap = baseIter->second; + auto derivedIter = derivedMap.find( derivedIndex ); + if (derivedIter == derivedMap.end()) + return false; + + return true; + } + + //! Gets the mapping object that can perform the upcast or downcast + /*! Uses the type index from the base and derived class to find the matching + registered caster. If no matching caster exists, calls the exception function. + + The returned PolymorphicCaster is capable of upcasting or downcasting between the two types. */ + template inline + static std::vector const & lookup( std::type_index const & baseIndex, std::type_index const & derivedIndex, F && exceptionFunc ) + { + // First phase of lookup - match base type index + auto const & baseMap = StaticObject::getInstance().map; + auto baseIter = baseMap.find( baseIndex ); + if( baseIter == baseMap.end() ) + exceptionFunc(); + + // Second phase - find a match from base to derived + auto & derivedMap = baseIter->second; + auto derivedIter = derivedMap.find( derivedIndex ); + if( derivedIter == derivedMap.end() ) + exceptionFunc(); + + return derivedIter->second; + } + + //! Performs a downcast to the derived type using a registered mapping + template inline + static const Derived * downcast( const void * dptr, std::type_info const & baseInfo ) + { + auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } ); + + for( auto const * map : mapping ) + dptr = map->downcast( dptr ); + + return static_cast( dptr ); + } + + //! Performs an upcast to the registered base type using the given a derived type + /*! The return is untyped because the final casting to the base type must happen in the polymorphic + serialization function, where the type is known at compile time */ + template inline + static void * upcast( Derived * const dptr, std::type_info const & baseInfo ) + { + auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } ); + + void * uptr = dptr; + for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter ) + uptr = (*mIter)->upcast( uptr ); + + return uptr; + } + + //! Upcasts for shared pointers + template inline + static std::shared_ptr upcast( std::shared_ptr const & dptr, std::type_info const & baseInfo ) + { + auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } ); + + std::shared_ptr uptr = dptr; + for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter ) + uptr = (*mIter)->upcast( uptr ); + + return uptr; + } + + #undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION + }; + + //! Strongly typed derivation of PolymorphicCaster + template + struct PolymorphicVirtualCaster : PolymorphicCaster + { + //! Inserts an entry in the polymorphic casting map for this pairing + /*! Creates an explicit mapping between Base and Derived in both upwards and + downwards directions, allowing void pointers to either to be properly cast + assuming dynamic type information is available */ + PolymorphicVirtualCaster() + { + const auto baseKey = std::type_index(typeid(Base)); + const auto derivedKey = std::type_index(typeid(Derived)); + + // First insert the relation Base->Derived + const auto lock = StaticObject::lock(); + auto & baseMap = StaticObject::getInstance().map; + auto lb = baseMap.lower_bound(baseKey); + + { + auto & derivedMap = baseMap.insert( lb, {baseKey, {}} )->second; + auto lbd = derivedMap.lower_bound(derivedKey); + auto & derivedVec = derivedMap.insert( lbd, { std::move(derivedKey), {}} )->second; + derivedVec.push_back( this ); + } + + // Insert reverse relation Derived->Base + auto & reverseMap = StaticObject::getInstance().reverseMap; + reverseMap.insert( {derivedKey, baseKey} ); + + // Find all chainable unregistered relations + /* The strategy here is to process only the nodes in the class hierarchy graph that have been + affected by the new insertion. The aglorithm iteratively processes a node an ensures that it + is updated with all new shortest length paths. It then rocesses the parents of the active node, + with the knowledge that all children have already been processed. + + Note that for the following, we'll use the nomenclature of parent and child to not confuse with + the inserted base derived relationship */ + { + // Checks whether there is a path from parent->child and returns a pair + // dist is set to MAX if the path does not exist + auto checkRelation = [](std::type_index const & parentInfo, std::type_index const & childInfo) -> + std::pair> + { + if( PolymorphicCasters::exists( parentInfo, childInfo ) ) + { + auto const & path = PolymorphicCasters::lookup( parentInfo, childInfo, [](){} ); + return {path.size(), path}; + } + else + return {std::numeric_limits::max(), {}}; + }; + + std::stack parentStack; // Holds the parent nodes to be processed + std::set dirtySet; // Marks child nodes that have been changed + std::set processedParents; // Marks parent nodes that have been processed + + // Begin processing the base key and mark derived as dirty + parentStack.push( baseKey ); + dirtySet.insert( derivedKey ); + + while( !parentStack.empty() ) + { + using Relations = std::multimap>>; + Relations unregisteredRelations; // Defer insertions until after main loop to prevent iterator invalidation + + const auto parent = parentStack.top(); + parentStack.pop(); + + // Update paths to all children marked dirty + for( auto const & childPair : baseMap[parent] ) + { + const auto child = childPair.first; + if( dirtySet.count( child ) && baseMap.count( child ) ) + { + auto parentChildPath = checkRelation( parent, child ); + + // Search all paths from the child to its own children (finalChild), + // looking for a shorter parth from parent to finalChild + for( auto const & finalChildPair : baseMap[child] ) + { + const auto finalChild = finalChildPair.first; + + auto parentFinalChildPath = checkRelation( parent, finalChild ); + auto childFinalChildPath = checkRelation( child, finalChild ); + + const size_t newLength = 1u + parentChildPath.first; + + if( newLength < parentFinalChildPath.first ) + { + std::vector path = parentChildPath.second; + path.insert( path.end(), childFinalChildPath.second.begin(), childFinalChildPath.second.end() ); + + // Check to see if we have a previous uncommitted path in unregisteredRelations + // that is shorter. If so, ignore this path + auto hintRange = unregisteredRelations.equal_range( parent ); + auto hint = hintRange.first; + for( ; hint != hintRange.second; ++hint ) + if( hint->second.first == finalChild ) + break; + + const bool uncommittedExists = hint != unregisteredRelations.end(); + if( uncommittedExists && (hint->second.second.size() <= newLength) ) + continue; + + auto newPath = std::pair>{finalChild, std::move(path)}; + + // Insert the new path if it doesn't exist, otherwise this will just lookup where to do the + // replacement + #ifdef CEREAL_OLDER_GCC + auto old = unregisteredRelations.insert( hint, std::make_pair(parent, newPath) ); + #else // NOT CEREAL_OLDER_GCC + auto old = unregisteredRelations.emplace_hint( hint, parent, newPath ); + #endif // NOT CEREAL_OLDER_GCC + + // If there was an uncommitted path, we need to perform a replacement + if( uncommittedExists ) + old->second = newPath; + } + } // end loop over child's children + } // end if dirty and child has children + } // end loop over children + + // Insert chained relations + for( auto const & it : unregisteredRelations ) + { + auto & derivedMap = baseMap.find( it.first )->second; + derivedMap[it.second.first] = it.second.second; + reverseMap.insert( {it.second.first, it.first} ); + } + + // Mark current parent as modified + dirtySet.insert( parent ); + + // Insert all parents of the current parent node that haven't yet been processed + auto parentRange = reverseMap.equal_range( parent ); + for( auto pIter = parentRange.first; pIter != parentRange.second; ++pIter ) + { + const auto pParent = pIter->second; + if( !processedParents.count( pParent ) ) + { + parentStack.push( pParent ); + processedParents.insert( pParent ); + } + } + } // end loop over parent stack + } // end chainable relations + } // end PolymorphicVirtualCaster() + + //! Performs the proper downcast with the templated types + void const * downcast( void const * const ptr ) const override + { + return dynamic_cast( static_cast( ptr ) ); + } + + //! Performs the proper upcast with the templated types + void * upcast( void * const ptr ) const override + { + return dynamic_cast( static_cast( ptr ) ); + } + + //! Performs the proper upcast with the templated types (shared_ptr version) + std::shared_ptr upcast( std::shared_ptr const & ptr ) const override + { + return std::dynamic_pointer_cast( std::static_pointer_cast( ptr ) ); + } + }; + + //! Registers a polymorphic casting relation between a Base and Derived type + /*! Registering a relation allows cereal to properly cast between the two types + given runtime type information and void pointers. + + Registration happens automatically via cereal::base_class and cereal::virtual_base_class + instantiations. For cases where neither is called, see the CEREAL_REGISTER_POLYMORPHIC_RELATION + macro */ + template + struct RegisterPolymorphicCaster + { + static PolymorphicCaster const * bind( std::true_type /* is_polymorphic */) + { + return &StaticObject>::getInstance(); + } + + static PolymorphicCaster const * bind( std::false_type /* is_polymorphic */ ) + { return nullptr; } + + //! Performs registration (binding) between Base and Derived + /*! If the type is not polymorphic, nothing will happen */ + static PolymorphicCaster const * bind() + { return bind( typename std::is_polymorphic::type() ); } + }; + } + + /* General polymorphism support */ + namespace detail + { + //! Binds a compile time type with a user defined string + template + struct binding_name {}; + + //! A structure holding a map from type_indices to output serializer functions + /*! A static object of this map should be created for each registered archive + type, containing entries for every registered type that describe how to + properly cast the type to its real type in polymorphic scenarios for + shared_ptr, weak_ptr, and unique_ptr. */ + template + struct OutputBindingMap + { + //! A serializer function + /*! Serializer functions return nothing and take an archive as + their first parameter (will be cast properly inside the function, + a pointer to actual data (contents of smart_ptr's get() function) + as their second parameter, and the type info of the owning smart_ptr + as their final parameter */ + typedef std::function Serializer; + + //! Struct containing the serializer functions for all pointer types + struct Serializers + { + Serializer shared_ptr, //!< Serializer function for shared/weak pointers + unique_ptr; //!< Serializer function for unique pointers + }; + + //! A map of serializers for pointers of all registered types + std::map map; + }; + + //! An empty noop deleter + template struct EmptyDeleter { void operator()(T *) const {} }; + + //! A structure holding a map from type name strings to input serializer functions + /*! A static object of this map should be created for each registered archive + type, containing entries for every registered type that describe how to + properly cast the type to its real type in polymorphic scenarios for + shared_ptr, weak_ptr, and unique_ptr. */ + template + struct InputBindingMap + { + //! Shared ptr serializer function + /*! Serializer functions return nothing and take an archive as + their first parameter (will be cast properly inside the function, + a shared_ptr (or unique_ptr for the unique case) of any base + type, and the type id of said base type as the third parameter. + Internally it will properly be loaded and cast to the correct type. */ + typedef std::function &, std::type_info const &)> SharedSerializer; + //! Unique ptr serializer function + typedef std::function> &, std::type_info const &)> UniqueSerializer; + + //! Struct containing the serializer functions for all pointer types + struct Serializers + { + SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers + UniqueSerializer unique_ptr; //!< Serializer function for unique pointers + }; + + //! A map of serializers for pointers of all registered types + std::map map; + }; + + // forward decls for archives from cereal.hpp + class InputArchiveBase; + class OutputArchiveBase; + + //! Creates a binding (map entry) between an input archive type and a polymorphic type + /*! Bindings are made when types are registered, assuming that at least one + archive has already been registered. When this struct is created, + it will insert (at run time) an entry into a map that properly handles + casting for serializing polymorphic objects */ + template struct InputBindingCreator + { + //! Initialize the binding + InputBindingCreator() + { + auto & map = StaticObject>::getInstance().map; + auto lock = StaticObject>::lock(); + auto key = std::string(binding_name::name()); + auto lb = map.lower_bound(key); + + if (lb != map.end() && lb->first == key) + return; + + typename InputBindingMap::Serializers serializers; + + serializers.shared_ptr = + [](void * arptr, std::shared_ptr & dptr, std::type_info const & baseInfo) + { + Archive & ar = *static_cast(arptr); + std::shared_ptr ptr; + + ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) ); + + dptr = PolymorphicCasters::template upcast( ptr, baseInfo ); + }; + + serializers.unique_ptr = + [](void * arptr, std::unique_ptr> & dptr, std::type_info const & baseInfo) + { + Archive & ar = *static_cast(arptr); + std::unique_ptr ptr; + + ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) ); + + dptr.reset( PolymorphicCasters::template upcast( ptr.release(), baseInfo )); + }; + + map.insert( lb, { std::move(key), std::move(serializers) } ); + } + }; + + //! Creates a binding (map entry) between an output archive type and a polymorphic type + /*! Bindings are made when types are registered, assuming that at least one + archive has already been registered. When this struct is created, + it will insert (at run time) an entry into a map that properly handles + casting for serializing polymorphic objects */ + template struct OutputBindingCreator + { + //! Writes appropriate metadata to the archive for this polymorphic type + static void writeMetadata(Archive & ar) + { + // Register the polymorphic type name with the archive, and get the id + char const * name = binding_name::name(); + std::uint32_t id = ar.registerPolymorphicType(name); + + // Serialize the id + ar( CEREAL_NVP_("polymorphic_id", id) ); + + // If the msb of the id is 1, then the type name is new, and we should serialize it + if( id & detail::msb_32bit ) + { + std::string namestring(name); + ar( CEREAL_NVP_("polymorphic_name", namestring) ); + } + } + + //! Holds a properly typed shared_ptr to the polymorphic type + class PolymorphicSharedPointerWrapper + { + public: + /*! Wrap a raw polymorphic pointer in a shared_ptr to its true type + + The wrapped pointer will not be responsible for ownership of the held pointer + so it will not attempt to destroy it; instead the refcount of the wrapped + pointer will be tied to a fake 'ownership pointer' that will do nothing + when it ultimately goes out of scope. + + The main reason for doing this, other than not to destroy the true object + with our wrapper pointer, is to avoid meddling with the internal reference + count in a polymorphic type that inherits from std::enable_shared_from_this. + + @param dptr A void pointer to the contents of the shared_ptr to serialize */ + PolymorphicSharedPointerWrapper( T const * dptr ) : refCount(), wrappedPtr( refCount, dptr ) + { } + + //! Get the wrapped shared_ptr */ + inline std::shared_ptr const & operator()() const { return wrappedPtr; } + + private: + std::shared_ptr refCount; //!< The ownership pointer + std::shared_ptr wrappedPtr; //!< The wrapped pointer + }; + + //! Does the actual work of saving a polymorphic shared_ptr + /*! This function will properly create a shared_ptr from the void * that is passed in + before passing it to the archive for serialization. + + In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms + + @param ar The archive to serialize to + @param dptr Pointer to the actual data held by the shared_ptr */ + static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::true_type /* has_shared_from_this */ ) + { + ::cereal::memory_detail::EnableSharedStateHelper state( const_cast(dptr) ); + PolymorphicSharedPointerWrapper psptr( dptr ); + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) ); + } + + //! Does the actual work of saving a polymorphic shared_ptr + /*! This function will properly create a shared_ptr from the void * that is passed in + before passing it to the archive for serialization. + + This version is for types that do not inherit from std::enable_shared_from_this. + + @param ar The archive to serialize to + @param dptr Pointer to the actual data held by the shared_ptr */ + static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::false_type /* has_shared_from_this */ ) + { + PolymorphicSharedPointerWrapper psptr( dptr ); + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) ); + } + + //! Initialize the binding + OutputBindingCreator() + { + auto & map = StaticObject>::getInstance().map; + auto key = std::type_index(typeid(T)); + auto lb = map.lower_bound(key); + + if (lb != map.end() && lb->first == key) + return; + + typename OutputBindingMap::Serializers serializers; + + serializers.shared_ptr = + [&](void * arptr, void const * dptr, std::type_info const & baseInfo) + { + Archive & ar = *static_cast(arptr); + writeMetadata(ar); + + auto ptr = PolymorphicCasters::template downcast( dptr, baseInfo ); + + #ifdef _MSC_VER + savePolymorphicSharedPtr( ar, ptr, ::cereal::traits::has_shared_from_this::type() ); // MSVC doesn't like typename here + #else // not _MSC_VER + savePolymorphicSharedPtr( ar, ptr, typename ::cereal::traits::has_shared_from_this::type() ); + #endif // _MSC_VER + }; + + serializers.unique_ptr = + [&](void * arptr, void const * dptr, std::type_info const & baseInfo) + { + Archive & ar = *static_cast(arptr); + writeMetadata(ar); + + std::unique_ptr> const ptr( PolymorphicCasters::template downcast( dptr, baseInfo ) ); + + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); + }; + + map.insert( { std::move(key), std::move(serializers) } ); + } + }; + + //! Used to help out argument dependent lookup for finding potential overloads + //! of instantiate_polymorphic_binding + struct adl_tag {}; + + //! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding. Due to the use of anonymous + //! namespace it becomes a different type in each translation unit. + namespace { struct polymorphic_binding_tag {}; } + + //! Causes the static object bindings between an archive type and a serializable type T + template + struct create_bindings + { + static const InputBindingCreator & + load(std::true_type) + { + return cereal::detail::StaticObject>::getInstance(); + } + + static const OutputBindingCreator & + save(std::true_type) + { + return cereal::detail::StaticObject>::getInstance(); + } + + inline static void load(std::false_type) {} + inline static void save(std::false_type) {} + }; + + //! When specialized, causes the compiler to instantiate its parameter + template + struct instantiate_function {}; + + /*! This struct is used as the return type of instantiate_polymorphic_binding + for specific Archive types. When the compiler looks for overloads of + instantiate_polymorphic_binding, it will be forced to instantiate this + struct during overload resolution, even though it will not be part of a valid + overload */ + template + struct polymorphic_serialization_support + { + #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + //! Creates the appropriate bindings depending on whether the archive supports + //! saving or loading + virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED; + #else // NOT _MSC_VER + //! Creates the appropriate bindings depending on whether the archive supports + //! saving or loading + static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED; + //! This typedef causes the compiler to instantiate this static function + typedef instantiate_function unused; + #endif // _MSC_VER + }; + + // instantiate implementation + template + CEREAL_DLL_EXPORT void polymorphic_serialization_support::instantiate() + { + create_bindings::save( std::integral_constant::value && + traits::is_output_serializable::value>{} ); + + create_bindings::load( std::integral_constant::value && + traits::is_input_serializable::value>{} ); + } + + //! Begins the binding process of a type to all registered archives + /*! Archives need to be registered prior to this struct being instantiated via + the CEREAL_REGISTER_ARCHIVE macro. Overload resolution will then force + several static objects to be made that allow us to bind together all + registered archive types with the parameter type T. */ + template + struct bind_to_archives + { + //! Binding for non abstract types + void bind(std::false_type) const + { + instantiate_polymorphic_binding(static_cast(nullptr), 0, Tag{}, adl_tag{}); + } + + //! Binding for abstract types + void bind(std::true_type) const + { } + + //! Binds the type T to all registered archives + /*! If T is abstract, we will not serialize it and thus + do not need to make a binding */ + bind_to_archives const & bind() const + { + static_assert( std::is_polymorphic::value, + "Attempting to register non polymorphic type" ); + bind( std::is_abstract() ); + return *this; + } + }; + + //! Used to hide the static object used to bind T to registered archives + template + struct init_binding; + + //! Base case overload for instantiation + /*! This will end up always being the best overload due to the second + parameter always being passed as an int. All other overloads will + accept pointers to archive types and have lower precedence than int. + + Since the compiler needs to check all possible overloads, the + other overloads created via CEREAL_REGISTER_ARCHIVE, which will have + lower precedence due to requring a conversion from int to (Archive*), + will cause their return types to be instantiated through the static object + mechanisms even though they are never called. + + See the documentation for the other functions to try and understand this */ + template + void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {} + } // namespace detail +} // namespace cereal + +#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_ diff --git a/tpl/cereal/include/cereal/details/polymorphic_impl_fwd.hpp b/tpl/cereal/include/cereal/details/polymorphic_impl_fwd.hpp new file mode 100644 index 0000000..c13ce23 --- /dev/null +++ b/tpl/cereal/include/cereal/details/polymorphic_impl_fwd.hpp @@ -0,0 +1,65 @@ +/*! \file polymorphic_impl_fwd.hpp + \brief Internal polymorphism support forward declarations + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* This code is heavily inspired by the boost serialization implementation by the following authors + + (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt) + + See http://www.boost.org for updates, documentation, and revision history. + + (C) Copyright 2006 David Abrahams - http://www.boost.org. + + See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their + implementation. +*/ + +#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_ +#define CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_ + +namespace cereal +{ + namespace detail + { + //! Forward declaration, see polymorphic_impl.hpp for more information + template + struct RegisterPolymorphicCaster; + + //! Forward declaration, see polymorphic_impl.hpp for more information + struct PolymorphicCasters; + + //! Forward declaration, see polymorphic_impl.hpp for more information + template + struct PolymorphicRelation; + } // namespace detail +} // namespace cereal + +#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_FWD_HPP_ diff --git a/tpl/cereal/include/cereal/details/static_object.hpp b/tpl/cereal/include/cereal/details/static_object.hpp new file mode 100644 index 0000000..c9c888d --- /dev/null +++ b/tpl/cereal/include/cereal/details/static_object.hpp @@ -0,0 +1,127 @@ +/*! \file static_object.hpp + \brief Internal polymorphism static object support + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_DETAILS_STATIC_OBJECT_HPP_ +#define CEREAL_DETAILS_STATIC_OBJECT_HPP_ + +#include "cereal/macros.hpp" + +#if CEREAL_THREAD_SAFE +#include +#endif + +//! Prevent link optimization from removing non-referenced static objects +/*! Especially for polymorphic support, we create static objects which + may not ever be explicitly referenced. Most linkers will detect this + and remove the code causing various unpleasant runtime errors. These + macros, adopted from Boost (see force_include.hpp) prevent this + (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) */ + +#ifdef _MSC_VER +# define CEREAL_DLL_EXPORT __declspec(dllexport) +# define CEREAL_USED +#else // clang or gcc +# define CEREAL_DLL_EXPORT +# define CEREAL_USED __attribute__ ((__used__)) +#endif + +namespace cereal +{ + namespace detail + { + //! A static, pre-execution object + /*! This class will create a single copy (singleton) of some + type and ensures that merely referencing this type will + cause it to be instantiated and initialized pre-execution. + For example, this is used heavily in the polymorphic pointer + serialization mechanisms to bind various archive types with + different polymorphic classes */ + template + class CEREAL_DLL_EXPORT StaticObject + { + private: + //! Forces instantiation at pre-execution time + static void instantiate( T const & ) {} + + static T & create() + { + static T t; + instantiate(instance); + return t; + } + + StaticObject( StaticObject const & /*other*/ ) {} + + public: + static T & getInstance() + { + return create(); + } + + //! A class that acts like std::lock_guard + class LockGuard + { + #if CEREAL_THREAD_SAFE + public: + LockGuard(std::mutex & m) : lock(m) {} + private: + std::unique_lock lock; + #else + public: + ~LockGuard() CEREAL_NOEXCEPT {} // prevents variable not used + #endif + }; + + //! Attempts to lock this static object for the current scope + /*! @note This function is a no-op if cereal is not compiled with + thread safety enabled (CEREAL_THREAD_SAFE = 1). + + This function returns an object that holds a lock for + this StaticObject that will release its lock upon destruction. This + call will block until the lock is available. */ + static LockGuard lock() + { + #if CEREAL_THREAD_SAFE + static std::mutex instanceMutex; + return LockGuard{instanceMutex}; + #else + return LockGuard{}; + #endif + } + + private: + static T & instance; + }; + + template T & StaticObject::instance = StaticObject::create(); + } // namespace detail +} // namespace cereal + +#endif // CEREAL_DETAILS_STATIC_OBJECT_HPP_ diff --git a/tpl/cereal/include/cereal/details/traits.hpp b/tpl/cereal/include/cereal/details/traits.hpp new file mode 100644 index 0000000..3390bbd --- /dev/null +++ b/tpl/cereal/include/cereal/details/traits.hpp @@ -0,0 +1,1389 @@ +/*! \file traits.hpp + \brief Internal type trait support + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_DETAILS_TRAITS_HPP_ +#define CEREAL_DETAILS_TRAITS_HPP_ + +#ifndef __clang__ +#if (__GNUC__ == 4 && __GNUC_MINOR__ <= 7) +#define CEREAL_OLDER_GCC +#endif // gcc 4.7 or earlier +#endif // __clang__ + +#include +#include + +#include "cereal/macros.hpp" +#include "cereal/access.hpp" + +namespace cereal +{ + namespace traits + { + using yes = std::true_type; + using no = std::false_type; + + namespace detail + { + // ###################################################################### + //! Used to delay a static_assert until template instantiation + template + struct delay_static_assert : std::false_type {}; + + // ###################################################################### + // SFINAE Helpers + #ifdef CEREAL_OLDER_GCC // when VS supports better SFINAE, we can use this as the default + template struct Void { typedef void type; }; + #endif // CEREAL_OLDER_GCC + + //! Return type for SFINAE Enablers + enum class sfinae {}; + + // ###################################################################### + // Helper functionality for boolean integral constants and Enable/DisableIf + template struct meta_bool_and : std::integral_constant::value> {}; + template struct meta_bool_and : std::integral_constant {}; + + template struct meta_bool_or : std::integral_constant::value> {}; + template struct meta_bool_or : std::integral_constant {}; + + // workaround needed due to bug in MSVC 2013, see + // http://connect.microsoft.com/VisualStudio/feedback/details/800231/c-11-alias-template-issue + template + struct EnableIfHelper : std::enable_if::value, sfinae> {}; + + template + struct DisableIfHelper : std::enable_if::value, sfinae> {}; + } // namespace detail + + //! Used as the default value for EnableIf and DisableIf template parameters + /*! @relates EnableIf + @relates DisableIf */ + static const detail::sfinae sfinae = {}; + + // ###################################################################### + //! Provides a way to enable a function if conditions are met + /*! This is intended to be used in a near identical fashion to std::enable_if + while being significantly easier to read at the cost of not allowing for as + complicated of a condition. + + This will compile (allow the function) if every condition evaluates to true. + at compile time. This should be used with SFINAE to ensure that at least + one other candidate function works when one fails due to an EnableIf. + + This should be used as the las template parameter to a function as + an unnamed parameter with a default value of cereal::traits::sfinae: + + @code{cpp} + // using by making the last template argument variadic + template ::value> = sfinae> + void func(T t ); + @endcode + + Note that this performs a logical AND of all conditions, so you will need + to construct more complicated requirements with this fact in mind. + + @relates DisableIf + @relates sfinae + @tparam Conditions The conditions which will be logically ANDed to enable the function. */ + template + using EnableIf = typename detail::EnableIfHelper::type; + + // ###################################################################### + //! Provides a way to disable a function if conditions are met + /*! This is intended to be used in a near identical fashion to std::enable_if + while being significantly easier to read at the cost of not allowing for as + complicated of a condition. + + This will compile (allow the function) if every condition evaluates to false. + This should be used with SFINAE to ensure that at least one other candidate + function works when one fails due to a DisableIf. + + This should be used as the las template parameter to a function as + an unnamed parameter with a default value of cereal::traits::sfinae: + + @code{cpp} + // using by making the last template argument variadic + template ::value> = sfinae> + void func(T t ); + @endcode + + This is often used in conjunction with EnableIf to form an enable/disable pair of + overloads. + + Note that this performs a logical AND of all conditions, so you will need + to construct more complicated requirements with this fact in mind. If all conditions + hold, the function will be disabled. + + @relates EnableIf + @relates sfinae + @tparam Conditions The conditions which will be logically ANDed to disable the function. */ + template + using DisableIf = typename detail::DisableIfHelper::type; + + // ###################################################################### + namespace detail + { + template + struct get_output_from_input : no + { + static_assert( detail::delay_static_assert::value, + "Could not find an associated output archive for input archive." ); + }; + + template + struct get_input_from_output : no + { + static_assert( detail::delay_static_assert::value, + "Could not find an associated input archive for output archive." ); + }; + } + + //! Sets up traits that relate an input archive to an output archive + #define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive) \ + namespace cereal { namespace traits { namespace detail { \ + template <> struct get_output_from_input \ + { using type = OutputArchive; }; \ + template <> struct get_input_from_output \ + { using type = InputArchive; }; } } } /* end namespaces */ + + // ###################################################################### + //! Used to convert a MAKE_HAS_XXX macro into a versioned variant + #define CEREAL_MAKE_VERSIONED_TEST ,0 + + // ###################################################################### + //! Creates a test for whether a non const member function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param name The name of the function to test for (e.g. serialize, load, save) + @param test_name The name to give the test for the function being tested for (e.g. serialize, versioned_serialize) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #ifdef CEREAL_OLDER_GCC + #define CEREAL_MAKE_HAS_MEMBER_TEST(name, test_name, versioned) \ + template \ + struct has_member_##test_name : no {}; \ + template \ + struct has_member_##test_name(), std::declval() versioned ) ) >::type> : yes {} + #else // NOT CEREAL_OLDER_GCC + #define CEREAL_MAKE_HAS_MEMBER_TEST(name, test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##name##_##versioned##_impl \ + { \ + template \ + static auto test(int) -> decltype( cereal::access::member_##name( std::declval(), std::declval() versioned ), yes()); \ + template \ + static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + }; \ + } /* end namespace detail */ \ + template \ + struct has_member_##test_name : std::integral_constant::value> {} + #endif // NOT CEREAL_OLDER_GCC + + // ###################################################################### + //! Creates a test for whether a non const non-member function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper non-member function for the given archive. */ + #define CEREAL_MAKE_HAS_NON_MEMBER_TEST(test_name, func, versioned) \ + namespace detail \ + { \ + template \ + struct has_non_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( func( std::declval(), std::declval() versioned ), yes()); \ + template \ + static no test( ... ); \ + static const bool value = std::is_same( 0 ) ), yes>::value; \ + }; \ + } /* end namespace detail */ \ + template \ + struct has_non_member_##test_name : std::integral_constant::value> {} + + // ###################################################################### + // Member Serialize + CEREAL_MAKE_HAS_MEMBER_TEST(serialize, serialize,); + + // ###################################################################### + // Member Serialize (versioned) + CEREAL_MAKE_HAS_MEMBER_TEST(serialize, versioned_serialize, CEREAL_MAKE_VERSIONED_TEST); + + // ###################################################################### + // Non Member Serialize + CEREAL_MAKE_HAS_NON_MEMBER_TEST(serialize, CEREAL_SERIALIZE_FUNCTION_NAME,); + + // ###################################################################### + // Non Member Serialize (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_TEST(versioned_serialize, CEREAL_SERIALIZE_FUNCTION_NAME, CEREAL_MAKE_VERSIONED_TEST); + + // ###################################################################### + // Member Load + CEREAL_MAKE_HAS_MEMBER_TEST(load, load,); + + // ###################################################################### + // Member Load (versioned) + CEREAL_MAKE_HAS_MEMBER_TEST(load, versioned_load, CEREAL_MAKE_VERSIONED_TEST); + + // ###################################################################### + // Non Member Load + CEREAL_MAKE_HAS_NON_MEMBER_TEST(load, CEREAL_LOAD_FUNCTION_NAME,); + + // ###################################################################### + // Non Member Load (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_TEST(versioned_load, CEREAL_LOAD_FUNCTION_NAME, CEREAL_MAKE_VERSIONED_TEST); + + // ###################################################################### + #undef CEREAL_MAKE_HAS_NON_MEMBER_TEST + #undef CEREAL_MAKE_HAS_MEMBER_TEST + + // ###################################################################### + //! Creates a test for whether a member save function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param test_name The name to give the test (e.g. save or versioned_save) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #ifdef CEREAL_OLDER_GCC + #define CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##test_name##_impl \ + { \ + template struct test : no {}; \ + template \ + struct test(), \ + std::declval() versioned ) ) >::type> : yes {}; \ + static const bool value = test(); \ + \ + template struct test2 : no {}; \ + template \ + struct test2(), \ + std::declval::type&>() versioned ) ) >::type> : yes {}; \ + static const bool not_const_type = test2(); \ + }; \ + } /* end namespace detail */ + #else /* NOT CEREAL_OLDER_GCC =================================== */ + #define CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( cereal::access::member_save( std::declval(), \ + std::declval() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + \ + template \ + static auto test2(int) -> decltype( cereal::access::member_save_non_const( \ + std::declval(), \ + std::declval::type&>() versioned ), yes()); \ + template static no test2(...); \ + static const bool not_const_type = std::is_same(0)), yes>::value; \ + }; \ + } /* end namespace detail */ + #endif /* NOT CEREAL_OLDER_GCC */ + + // ###################################################################### + // Member Save + CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(save, ) + + template + struct has_member_save : std::integral_constant::value> + { + typedef typename detail::has_member_save_impl check; + static_assert( check::value || !check::not_const_type, + "cereal detected a non-const save. \n " + "save member functions must always be const" ); + }; + + // ###################################################################### + // Member Save (versioned) + CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(versioned_save, CEREAL_MAKE_VERSIONED_TEST) + + template + struct has_member_versioned_save : std::integral_constant::value> + { + typedef typename detail::has_member_versioned_save_impl check; + static_assert( check::value || !check::not_const_type, + "cereal detected a versioned non-const save. \n " + "save member functions must always be const" ); + }; + + // ###################################################################### + #undef CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL + + // ###################################################################### + //! Creates a test for whether a non-member save function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper non-member function for the given archive. + + @param test_name The name to give the test (e.g. save or versioned_save) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #define CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_non_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( CEREAL_SAVE_FUNCTION_NAME( \ + std::declval(), \ + std::declval() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + \ + template \ + static auto test2(int) -> decltype( CEREAL_SAVE_FUNCTION_NAME( \ + std::declval(), \ + std::declval::type&>() versioned ), yes()); \ + template static no test2(...); \ + static const bool not_const_type = std::is_same(0)), yes>::value; \ + }; \ + } /* end namespace detail */ \ + \ + template \ + struct has_non_member_##test_name : std::integral_constant::value> \ + { \ + using check = typename detail::has_non_member_##test_name##_impl; \ + static_assert( check::value || !check::not_const_type, \ + "cereal detected a non-const type parameter in non-member " #test_name ". \n " \ + #test_name " non-member functions must always pass their types as const" ); \ + }; + + // ###################################################################### + // Non Member Save + CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(save, ) + + // ###################################################################### + // Non Member Save (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(versioned_save, CEREAL_MAKE_VERSIONED_TEST) + + // ###################################################################### + #undef CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST + + // ###################################################################### + // Minimal Utilities + namespace detail + { + // Determines if the provided type is an std::string + template struct is_string : std::false_type {}; + + template + struct is_string> : std::true_type {}; + } + + // Determines if the type is valid for use with a minimal serialize function + template + struct is_minimal_type : std::integral_constant::value || std::is_arithmetic::value> {}; + + // ###################################################################### + //! Creates implementation details for whether a member save_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #ifdef CEREAL_OLDER_GCC + #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##test_name##_impl \ + { \ + template struct test : no {}; \ + template \ + struct test(), \ + std::declval() versioned ) ) >::type> : yes {}; \ + \ + static const bool value = test(); \ + \ + template struct test2 : no {}; \ + template \ + struct test2(), \ + std::declval::type&>() versioned ) ) >::type> : yes {}; \ + static const bool not_const_type = test2(); \ + \ + static const bool valid = value || !not_const_type; \ + }; \ + } /* end namespace detail */ + #else /* NOT CEREAL_OLDER_GCC =================================== */ + #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( cereal::access::member_save_minimal( \ + std::declval(), \ + std::declval() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + \ + template \ + static auto test2(int) -> decltype( cereal::access::member_save_minimal_non_const( \ + std::declval(), \ + std::declval::type&>() versioned ), yes()); \ + template static no test2(...); \ + static const bool not_const_type = std::is_same(0)), yes>::value; \ + \ + static const bool valid = value || !not_const_type; \ + }; \ + } /* end namespace detail */ + #endif // NOT CEREAL_OLDER_GCC + + // ###################################################################### + //! Creates helpers for minimal save functions + /*! The get_member_*_type structs allow access to the return type of a save_minimal, + assuming that the function actually exists. If the function does not + exist, the type will be void. + + @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct get_member_##test_name##_type { using type = void; }; \ + \ + template \ + struct get_member_##test_name##_type \ + { \ + using type = decltype( cereal::access::member_save_minimal( std::declval(), \ + std::declval() versioned ) ); \ + }; \ + } /* end namespace detail */ + + // ###################################################################### + //! Creates a test for whether a member save_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal) */ + #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(test_name) \ + template \ + struct has_member_##test_name : std::integral_constant::value> \ + { \ + using check = typename detail::has_member_##test_name##_impl; \ + static_assert( check::valid, \ + "cereal detected a non-const member " #test_name ". \n " \ + #test_name " member functions must always be const" ); \ + \ + using type = typename detail::get_member_##test_name##_type::type; \ + static_assert( (check::value && is_minimal_type::value) || !check::value, \ + "cereal detected a member " #test_name " with an invalid return type. \n " \ + "return type must be arithmetic or string" ); \ + }; + + // ###################################################################### + // Member Save Minimal + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(save_minimal, ) + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(save_minimal, ) + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(save_minimal) + + // ###################################################################### + // Member Save Minimal (versioned) + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST) + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST) + CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(versioned_save_minimal) + + // ###################################################################### + #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL + #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL + #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST + + // ###################################################################### + //! Creates a test for whether a non-member save_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #define CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_non_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( \ + std::declval(), \ + std::declval() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + \ + template \ + static auto test2(int) -> decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( \ + std::declval(), \ + std::declval::type&>() versioned ), yes()); \ + template static no test2(...); \ + static const bool not_const_type = std::is_same(0)), yes>::value; \ + \ + static const bool valid = value || !not_const_type; \ + }; \ + \ + template \ + struct get_non_member_##test_name##_type { using type = void; }; \ + \ + template \ + struct get_non_member_##test_name##_type \ + { \ + using type = decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( std::declval(), \ + std::declval() versioned ) ); \ + }; \ + } /* end namespace detail */ \ + \ + template \ + struct has_non_member_##test_name : std::integral_constant::value> \ + { \ + using check = typename detail::has_non_member_##test_name##_impl; \ + static_assert( check::valid, \ + "cereal detected a non-const type parameter in non-member " #test_name ". \n " \ + #test_name " non-member functions must always pass their types as const" ); \ + \ + using type = typename detail::get_non_member_##test_name##_type::type; \ + static_assert( (check::value && is_minimal_type::value) || !check::value, \ + "cereal detected a non-member " #test_name " with an invalid return type. \n " \ + "return type must be arithmetic or string" ); \ + }; + + // ###################################################################### + // Non-Member Save Minimal + CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(save_minimal, ) + + // ###################################################################### + // Non-Member Save Minimal (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST) + + // ###################################################################### + #undef CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST + + // ###################################################################### + // Load Minimal Utilities + namespace detail + { + //! Used to help strip away conversion wrappers + /*! If someone writes a non-member load/save minimal function that accepts its + parameter as some generic template type and needs to perform trait checks + on that type, our NoConvert wrappers will interfere with this. Using + the struct strip_minmal, users can strip away our wrappers to get to + the underlying type, allowing traits to work properly */ + struct NoConvertBase {}; + + //! A struct that prevents implicit conversion + /*! Any type instantiated with this struct will be unable to implicitly convert + to another type. Is designed to only allow conversion to Source const &. + + @tparam Source the type of the original source */ + template + struct NoConvertConstRef : NoConvertBase + { + using type = Source; //!< Used to get underlying type easily + + template ::value>::type> + operator Dest () = delete; + + //! only allow conversion if the types are the same and we are converting into a const reference + template ::value>::type> + operator Dest const & (); + }; + + //! A struct that prevents implicit conversion + /*! Any type instantiated with this struct will be unable to implicitly convert + to another type. Is designed to only allow conversion to Source &. + + @tparam Source the type of the original source */ + template + struct NoConvertRef : NoConvertBase + { + using type = Source; //!< Used to get underlying type easily + + template ::value>::type> + operator Dest () = delete; + + #ifdef __clang__ + template ::value>::type> + operator Dest const & () = delete; + #endif // __clang__ + + //! only allow conversion if the types are the same and we are converting into a const reference + template ::value>::type> + operator Dest & (); + }; + + //! A type that can implicitly convert to anything else + struct AnyConvert + { + template + operator Dest & (); + + template + operator Dest const & () const; + }; + } // namespace detail + + // ###################################################################### + //! Creates a test for whether a member load_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + Our strategy here is to first check if a function matching the signature more or less exists + (allow anything like load_minimal(xxx) using AnyConvert, and then secondly enforce + that it has the correct signature using NoConvertConstRef + + @param test_name The name to give the test (e.g. load_minimal or versioned_load_minimal) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #ifdef CEREAL_OLDER_GCC + #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template struct has_member_##test_name##_impl : no {}; \ + template \ + struct has_member_##test_name##_impl(), \ + std::declval(), AnyConvert() versioned ) ) >::type> : yes {}; \ + \ + template struct has_member_##test_name##_type_impl : no {}; \ + template \ + struct has_member_##test_name##_type_impl(), \ + std::declval(), NoConvertConstRef() versioned ) ) >::type> : yes {}; \ + } /* end namespace detail */ + #else /* NOT CEREAL_OLDER_GCC =================================== */ + #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( cereal::access::member_load_minimal( \ + std::declval(), \ + std::declval(), AnyConvert() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + }; \ + template \ + struct has_member_##test_name##_type_impl \ + { \ + template \ + static auto test(int) -> decltype( cereal::access::member_load_minimal( \ + std::declval(), \ + std::declval(), NoConvertConstRef() versioned ), yes()); \ + template static no test(...); \ + static const bool value = std::is_same(0)), yes>::value; \ + \ + }; \ + } /* end namespace detail */ + #endif // NOT CEREAL_OLDER_GCC + + // ###################################################################### + //! Creates helpers for minimal load functions + /*! The has_member_*_wrapper structs ensure that the load and save types for the + requested function type match appropriately. + + @param load_test_name The name to give the test (e.g. load_minimal or versioned_load_minimal) + @param save_test_name The name to give the test (e.g. save_minimal or versioned_save_minimal, + should match the load name. + @param save_test_prefix The name to give the test (e.g. save_minimal or versioned_save_minimal, + should match the load name, without the trailing "_minimal" (e.g. + save or versioned_save). Needed because the preprocessor is an abomination. + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(load_test_name, save_test_name, save_test_prefix, versioned) \ + namespace detail \ + { \ + template \ + struct has_member_##load_test_name##_wrapper : std::false_type {}; \ + \ + template \ + struct has_member_##load_test_name##_wrapper \ + { \ + using AOut = typename detail::get_output_from_input::type; \ + \ + static_assert( has_member_##save_test_prefix##_minimal::value, \ + "cereal detected member " #load_test_name " but no valid member " #save_test_name ". \n " \ + "cannot evaluate correctness of " #load_test_name " without valid " #save_test_name "." ); \ + \ + using SaveType = typename detail::get_member_##save_test_prefix##_minimal_type::type; \ + const static bool value = has_member_##load_test_name##_impl::value; \ + const static bool valid = has_member_##load_test_name##_type_impl::value; \ + \ + static_assert( valid || !value, "cereal detected different or invalid types in corresponding member " \ + #load_test_name " and " #save_test_name " functions. \n " \ + "the paramater to " #load_test_name " must be a constant reference to the type that " \ + #save_test_name " returns." ); \ + }; \ + } /* end namespace detail */ + + // ###################################################################### + //! Creates a test for whether a member load_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + @param load_test_name The name to give the test (e.g. load_minimal or versioned_load_minimal) + @param load_test_prefix The above parameter minus the trailing "_minimal" */ + #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(load_test_name, load_test_prefix) \ + template \ + struct has_member_##load_test_prefix##_minimal : std::integral_constant::value>::value> {}; + + // ###################################################################### + // Member Load Minimal + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(load_minimal, ) + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(load_minimal, save_minimal, save, ) + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(load_minimal, load) + + // ###################################################################### + // Member Load Minimal (versioned) + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(versioned_load_minimal, CEREAL_MAKE_VERSIONED_TEST) + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(versioned_load_minimal, versioned_save_minimal, versioned_save, CEREAL_MAKE_VERSIONED_TEST) + CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(versioned_load_minimal, versioned_load) + + // ###################################################################### + #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL + #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL + #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST + + // ###################################################################### + // Non-Member Load Minimal + namespace detail + { + #ifdef CEREAL_OLDER_GCC + void CEREAL_LOAD_MINIMAL_FUNCTION_NAME(); // prevents nonsense complaining about not finding this + void CEREAL_SAVE_MINIMAL_FUNCTION_NAME(); + #endif // CEREAL_OLDER_GCC + } // namespace detail + + // ###################################################################### + //! Creates a test for whether a non-member load_minimal function exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper member function for the given archive. + + See notes from member load_minimal implementation. + + @param test_name The name to give the test (e.g. load_minimal or versioned_load_minimal) + @param save_name The corresponding name the save test would have (e.g. save_minimal or versioned_save_minimal) + @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */ + #define CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(test_name, save_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_non_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \ + std::declval(), std::declval(), AnyConvert() versioned ), yes() ); \ + template static no test( ... ); \ + static const bool exists = std::is_same( 0 ) ), yes>::value; \ + \ + template \ + static auto test2(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \ + std::declval(), std::declval(), NoConvertConstRef() versioned ), yes() ); \ + template static no test2( ... ); \ + static const bool valid = std::is_same( 0 ) ), yes>::value; \ + \ + template \ + static auto test3(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \ + std::declval(), NoConvertRef(), AnyConvert() versioned ), yes() ); \ + template static no test3( ... ); \ + static const bool const_valid = std::is_same( 0 ) ), yes>::value; \ + }; \ + \ + template \ + struct has_non_member_##test_name##_wrapper : std::false_type {}; \ + \ + template \ + struct has_non_member_##test_name##_wrapper \ + { \ + using AOut = typename detail::get_output_from_input::type; \ + \ + static_assert( detail::has_non_member_##save_name##_impl::valid, \ + "cereal detected non-member " #test_name " but no valid non-member " #save_name ". \n " \ + "cannot evaluate correctness of " #test_name " without valid " #save_name "." ); \ + \ + using SaveType = typename detail::get_non_member_##save_name##_type::type; \ + using check = has_non_member_##test_name##_impl; \ + static const bool value = check::exists; \ + \ + static_assert( check::valid || !check::exists, "cereal detected different types in corresponding non-member " \ + #test_name " and " #save_name " functions. \n " \ + "the paramater to " #test_name " must be a constant reference to the type that " #save_name " returns." ); \ + static_assert( check::const_valid || !check::exists, \ + "cereal detected an invalid serialization type parameter in non-member " #test_name ". " \ + #test_name " non-member functions must accept their serialization type by non-const reference" ); \ + }; \ + } /* namespace detail */ \ + \ + template \ + struct has_non_member_##test_name : std::integral_constant::exists>::value> {}; + + // ###################################################################### + // Non-Member Load Minimal + CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(load_minimal, save_minimal, ) + + // ###################################################################### + // Non-Member Load Minimal (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(versioned_load_minimal, versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST) + + // ###################################################################### + #undef CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST + + // ###################################################################### + //! Member load and construct check + template + struct has_member_load_and_construct : std::integral_constant( std::declval(), std::declval< ::cereal::construct&>() ) ), void>::value> + { }; + + // ###################################################################### + //! Member load and construct check (versioned) + template + struct has_member_versioned_load_and_construct : std::integral_constant( std::declval(), std::declval< ::cereal::construct&>(), 0 ) ), void>::value> + { }; + + // ###################################################################### + //! Creates a test for whether a non-member load_and_construct specialization exists + /*! This creates a class derived from std::integral_constant that will be true if + the type has the proper non-member function for the given archive. */ + #define CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(test_name, versioned) \ + namespace detail \ + { \ + template \ + struct has_non_member_##test_name##_impl \ + { \ + template \ + static auto test(int) -> decltype( LoadAndConstruct::load_and_construct( \ + std::declval(), std::declval< ::cereal::construct&>() versioned ), yes()); \ + template \ + static no test( ... ); \ + static const bool value = std::is_same( 0 ) ), yes>::value; \ + }; \ + } /* end namespace detail */ \ + template \ + struct has_non_member_##test_name : std::integral_constant::value> {}; + + // ###################################################################### + //! Non member load and construct check + CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(load_and_construct, ) + + // ###################################################################### + //! Non member load and construct check (versioned) + CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(versioned_load_and_construct, CEREAL_MAKE_VERSIONED_TEST) + + // ###################################################################### + //! Has either a member or non member load and construct + template + struct has_load_and_construct : std::integral_constant::value || has_non_member_load_and_construct::value || + has_member_versioned_load_and_construct::value || has_non_member_versioned_load_and_construct::value> + { }; + + // ###################################################################### + #undef CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST + + // ###################################################################### + // End of serialization existence tests + #undef CEREAL_MAKE_VERSIONED_TEST + + // ###################################################################### + template + struct has_member_split : std::integral_constant::value && has_member_save::value) || + (has_member_versioned_load::value && has_member_versioned_save::value)> {}; + + // ###################################################################### + template + struct has_non_member_split : std::integral_constant::value && has_non_member_save::value) || + (has_non_member_versioned_load::value && has_non_member_versioned_save::value)> {}; + + // ###################################################################### + template + struct has_invalid_output_versioning : std::integral_constant::value && has_member_save::value) || + (has_non_member_versioned_save::value && has_non_member_save::value) || + (has_member_versioned_serialize::value && has_member_serialize::value) || + (has_non_member_versioned_serialize::value && has_non_member_serialize::value) || + (has_member_versioned_save_minimal::value && has_member_save_minimal::value) || + (has_non_member_versioned_save_minimal::value && has_non_member_save_minimal::value)> {}; + + // ###################################################################### + template + struct has_invalid_input_versioning : std::integral_constant::value && has_member_load::value) || + (has_non_member_versioned_load::value && has_non_member_load::value) || + (has_member_versioned_serialize::value && has_member_serialize::value) || + (has_non_member_versioned_serialize::value && has_non_member_serialize::value) || + (has_member_versioned_load_minimal::value && has_member_load_minimal::value) || + (has_non_member_versioned_load_minimal::value && has_non_member_load_minimal::value)> {}; + + // ###################################################################### + namespace detail + { + //! Create a test for a cereal::specialization entry + #define CEREAL_MAKE_IS_SPECIALIZED_IMPL(name) \ + template \ + struct is_specialized_##name : std::integral_constant>::value> {} + + CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_serialize); + CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_load_save); + CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_load_save_minimal); + CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_serialize); + CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_load_save); + CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_load_save_minimal); + + #undef CEREAL_MAKE_IS_SPECIALIZED_IMPL + + //! Number of specializations detected + template + struct count_specializations : std::integral_constant::value + + is_specialized_member_load_save::value + + is_specialized_member_load_save_minimal::value + + is_specialized_non_member_serialize::value + + is_specialized_non_member_load_save::value + + is_specialized_non_member_load_save_minimal::value> {}; + } // namespace detail + + //! Check if any specialization exists for a type + template + struct is_specialized : std::integral_constant::value || + detail::is_specialized_member_load_save::value || + detail::is_specialized_member_load_save_minimal::value || + detail::is_specialized_non_member_serialize::value || + detail::is_specialized_non_member_load_save::value || + detail::is_specialized_non_member_load_save_minimal::value> + { + static_assert(detail::count_specializations::value <= 1, "More than one explicit specialization detected for type."); + }; + + //! Create the static assertion for some specialization + /*! This assertion will fail if the type is indeed specialized and does not have the appropriate + type of serialization functions */ + #define CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, print_name, spec_name) \ + static_assert( (is_specialized::value && detail::is_specialized_##spec_name::value && \ + (has_##name::value || has_##versioned_name::value)) \ + || !(is_specialized::value && detail::is_specialized_##spec_name::value), \ + "cereal detected " #print_name " specialization but no " #print_name " serialize function" ) + + //! Generates a test for specialization for versioned and unversioned functions + /*! This creates checks that can be queried to see if a given type of serialization function + has been specialized for this type */ + #define CEREAL_MAKE_IS_SPECIALIZED(name, versioned_name, spec_name) \ + template \ + struct is_specialized_##name : std::integral_constant::value && detail::is_specialized_##spec_name::value> \ + { CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, name, spec_name); }; \ + template \ + struct is_specialized_##versioned_name : std::integral_constant::value && detail::is_specialized_##spec_name::value> \ + { CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, versioned_name, spec_name); } + + CEREAL_MAKE_IS_SPECIALIZED(member_serialize, member_versioned_serialize, member_serialize); + CEREAL_MAKE_IS_SPECIALIZED(non_member_serialize, non_member_versioned_serialize, non_member_serialize); + + CEREAL_MAKE_IS_SPECIALIZED(member_save, member_versioned_save, member_load_save); + CEREAL_MAKE_IS_SPECIALIZED(non_member_save, non_member_versioned_save, non_member_load_save); + CEREAL_MAKE_IS_SPECIALIZED(member_load, member_versioned_load, member_load_save); + CEREAL_MAKE_IS_SPECIALIZED(non_member_load, non_member_versioned_load, non_member_load_save); + + CEREAL_MAKE_IS_SPECIALIZED(member_save_minimal, member_versioned_save_minimal, member_load_save_minimal); + CEREAL_MAKE_IS_SPECIALIZED(non_member_save_minimal, non_member_versioned_save_minimal, non_member_load_save_minimal); + CEREAL_MAKE_IS_SPECIALIZED(member_load_minimal, member_versioned_load_minimal, member_load_save_minimal); + CEREAL_MAKE_IS_SPECIALIZED(non_member_load_minimal, non_member_versioned_load_minimal, non_member_load_save_minimal); + + #undef CEREAL_MAKE_IS_SPECIALIZED_ASSERT + #undef CEREAL_MAKE_IS_SPECIALIZED + + // ###################################################################### + // detects if a type has any active minimal output serialization + template + struct has_minimal_output_serialization : std::integral_constant::value || + ((has_member_save_minimal::value || + has_non_member_save_minimal::value || + has_member_versioned_save_minimal::value || + has_non_member_versioned_save_minimal::value) && + !(is_specialized_member_serialize::value || + is_specialized_member_save::value))> {}; + + // ###################################################################### + // detects if a type has any active minimal input serialization + template + struct has_minimal_input_serialization : std::integral_constant::value || + ((has_member_load_minimal::value || + has_non_member_load_minimal::value || + has_member_versioned_load_minimal::value || + has_non_member_versioned_load_minimal::value) && + !(is_specialized_member_serialize::value || + is_specialized_member_load::value))> {}; + + // ###################################################################### + namespace detail + { + //! The number of output serialization functions available + /*! If specialization is being used, we'll count only those; otherwise we'll count everything */ + template + struct count_output_serializers : std::integral_constant::value ? count_specializations::value : + has_member_save::value + + has_non_member_save::value + + has_member_serialize::value + + has_non_member_serialize::value + + has_member_save_minimal::value + + has_non_member_save_minimal::value + + /*-versioned---------------------------------------------------------*/ + has_member_versioned_save::value + + has_non_member_versioned_save::value + + has_member_versioned_serialize::value + + has_non_member_versioned_serialize::value + + has_member_versioned_save_minimal::value + + has_non_member_versioned_save_minimal::value> {}; + } + + template + struct is_output_serializable : std::integral_constant::value == 1> {}; + + // ###################################################################### + namespace detail + { + //! The number of input serialization functions available + /*! If specialization is being used, we'll count only those; otherwise we'll count everything */ + template + struct count_input_serializers : std::integral_constant::value ? count_specializations::value : + has_member_load::value + + has_non_member_load::value + + has_member_serialize::value + + has_non_member_serialize::value + + has_member_load_minimal::value + + has_non_member_load_minimal::value + + /*-versioned---------------------------------------------------------*/ + has_member_versioned_load::value + + has_non_member_versioned_load::value + + has_member_versioned_serialize::value + + has_non_member_versioned_serialize::value + + has_member_versioned_load_minimal::value + + has_non_member_versioned_load_minimal::value> {}; + } + + template + struct is_input_serializable : std::integral_constant::value == 1> {}; + + // ###################################################################### + // Base Class Support + namespace detail + { + struct base_class_id + { + template + base_class_id(T const * const t) : + type(typeid(T)), + ptr(t), + hash(std::hash()(typeid(T)) ^ (std::hash()(t) << 1)) + { } + + bool operator==(base_class_id const & other) const + { return (type == other.type) && (ptr == other.ptr); } + + std::type_index type; + void const * ptr; + size_t hash; + }; + struct base_class_id_hash { size_t operator()(base_class_id const & id) const { return id.hash; } }; + } // namespace detail + + namespace detail + { + //! Common base type for base class casting + struct BaseCastBase {}; + + template + struct get_base_class; + + template class Cast, class Base> + struct get_base_class> + { + using type = Base; + }; + + //! Base class cast, behave as the test + template class Test, class Archive, + bool IsBaseCast = std::is_base_of::value> + struct has_minimal_base_class_serialization_impl : Test::type, Archive> + { }; + + //! Not a base class cast + template class Test, class Archive> + struct has_minimal_base_class_serialization_impl : std::false_type + { }; + } + + //! Checks to see if the base class used in a cast has a minimal serialization + /*! @tparam Cast Either base_class or virtual_base_class wrapped type + @tparam Test A has_minimal test (for either input or output) + @tparam Archive The archive to use with the test */ + template class Test, class Archive> + struct has_minimal_base_class_serialization : detail::has_minimal_base_class_serialization_impl + { }; + + + // ###################################################################### + namespace detail + { + struct shared_from_this_wrapper + { + template + static auto (check)( U const & t ) -> decltype( ::cereal::access::shared_from_this(t), std::true_type() ); + + static auto (check)( ... ) -> decltype( std::false_type() ); + + template + static auto get( U const & t ) -> decltype( t.shared_from_this() ); + }; + } + + //! Determine if T or any base class of T has inherited from std::enable_shared_from_this + template + struct has_shared_from_this : decltype((detail::shared_from_this_wrapper::check)(std::declval())) + { }; + + //! Get the type of the base class of T which inherited from std::enable_shared_from_this + template + struct get_shared_from_this_base + { + private: + using PtrType = decltype(detail::shared_from_this_wrapper::get(std::declval())); + public: + //! The type of the base of T that inherited from std::enable_shared_from_this + using type = typename std::decay::type; + }; + + // ###################################################################### + //! Extracts the true type from something possibly wrapped in a cereal NoConvert + /*! Internally cereal uses some wrapper classes to test the validity of non-member + minimal load and save functions. This can interfere with user type traits on + templated load and save minimal functions. To get to the correct underlying type, + users should use strip_minimal when performing any enable_if type type trait checks. + + See the enum serialization in types/common.hpp for an example of using this */ + template ::value> + struct strip_minimal + { + using type = T; + }; + + //! Specialization for types wrapped in a NoConvert + template + struct strip_minimal + { + using type = typename T::type; + }; + + // ###################################################################### + //! Determines whether the class T can be default constructed by cereal::access + template + struct is_default_constructible + { + #ifdef CEREAL_OLDER_GCC + template + struct test : no {}; + template + struct test() ) >::type> : yes {}; + static const bool value = test(); + #else // NOT CEREAL_OLDER_GCC ========================================= + template + static auto test(int) -> decltype( cereal::access::construct(), yes()); + template + static no test(...); + static const bool value = std::is_same(0)), yes>::value; + #endif // NOT CEREAL_OLDER_GCC + }; + + // ###################################################################### + namespace detail + { + //! Removes all qualifiers and minimal wrappers from an archive + template + using decay_archive = typename std::decay::type>::type; + } + + //! Checks if the provided archive type is equal to some cereal archive type + /*! This automatically does things such as std::decay and removing any other wrappers that may be + on the Archive template parameter. + + Example use: + @code{cpp} + // example use to disable a serialization function + template ::value> = sfinae> + void save( Archive & ar, MyType const & mt ); + @endcode */ + template + struct is_same_archive : std::integral_constant, CerealArchiveT>::value> + { }; + + // ###################################################################### + //! A macro to use to restrict which types of archives your function will work for. + /*! This requires you to have a template class parameter named Archive and replaces the void return + type for your function. + + INTYPE refers to the input archive type you wish to restrict on. + OUTTYPE refers to the output archive type you wish to restrict on. + + For example, if we want to limit a serialize to only work with binary serialization: + + @code{.cpp} + template + CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive) + serialize( Archive & ar, MyCoolType & m ) + { + ar & m; + } + @endcode + + If you need to do more restrictions in your enable_if, you will need to do this by hand. + */ + #define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE) \ + typename std::enable_if::value || cereal::traits::is_same_archive::value, void>::type + + //! Type traits only struct used to mark an archive as human readable (text based) + /*! Archives that wish to identify as text based/human readable should inherit from + this struct */ + struct TextArchive {}; + + //! Checks if an archive is a text archive (human readable) + template + struct is_text_archive : std::integral_constant>::value> + { }; + } // namespace traits + + // ###################################################################### + namespace detail + { + template ::value, + bool MemberVersioned = traits::has_member_versioned_load_and_construct::value, + bool NonMember = traits::has_non_member_load_and_construct::value, + bool NonMemberVersioned = traits::has_non_member_versioned_load_and_construct::value> + struct Construct + { + static_assert( cereal::traits::detail::delay_static_assert::value, + "cereal found more than one compatible load_and_construct function for the provided type and archive combination. \n\n " + "Types must either have a member load_and_construct function or a non-member specialization of LoadAndConstruct (you may not mix these). \n " + "In addition, you may not mix versioned with non-versioned load_and_construct functions. \n\n " ); + static T * load_andor_construct( A & /*ar*/, construct & /*construct*/ ) + { return nullptr; } + }; + + // no load and construct case + template + struct Construct + { + static_assert( ::cereal::traits::is_default_constructible::value, + "Trying to serialize a an object with no default constructor. \n\n " + "Types must either be default constructible or define either a member or non member Construct function. \n " + "Construct functions generally have the signature: \n\n " + "template \n " + "static void load_and_construct(Archive & ar, cereal::construct & construct) \n " + "{ \n " + " var a; \n " + " ar( a ) \n " + " construct( a ); \n " + "} \n\n" ); + static T * load_andor_construct() + { return ::cereal::access::construct(); } + }; + + // member non-versioned + template + struct Construct + { + static void load_andor_construct( A & ar, construct & construct ) + { + access::load_and_construct( ar, construct ); + } + }; + + // member versioned + template + struct Construct + { + static void load_andor_construct( A & ar, construct & construct ) + { + const auto version = ar.template loadClassVersion(); + access::load_and_construct( ar, construct, version ); + } + }; + + // non-member non-versioned + template + struct Construct + { + static void load_andor_construct( A & ar, construct & construct ) + { + LoadAndConstruct::load_and_construct( ar, construct ); + } + }; + + // non-member versioned + template + struct Construct + { + static void load_andor_construct( A & ar, construct & construct ) + { + const auto version = ar.template loadClassVersion(); + LoadAndConstruct::load_and_construct( ar, construct, version ); + } + }; + } // namespace detail +} // namespace cereal + +#endif // CEREAL_DETAILS_TRAITS_HPP_ diff --git a/tpl/cereal/include/cereal/details/util.hpp b/tpl/cereal/include/cereal/details/util.hpp new file mode 100644 index 0000000..ba24813 --- /dev/null +++ b/tpl/cereal/include/cereal/details/util.hpp @@ -0,0 +1,84 @@ +/*! \file util.hpp + \brief Internal misc utilities + \ingroup Internal */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_DETAILS_UTIL_HPP_ +#define CEREAL_DETAILS_UTIL_HPP_ + +#include +#include + +#ifdef _MSC_VER +namespace cereal +{ + namespace util + { + //! Demangles the type encoded in a string + /*! @internal */ + inline std::string demangle( std::string const & name ) + { return name; } + + //! Gets the demangled name of a type + /*! @internal */ + template inline + std::string demangledName() + { return typeid( T ).name(); } + } // namespace util +} // namespace cereal +#else // clang or gcc +#include +#include +namespace cereal +{ + namespace util + { + //! Demangles the type encoded in a string + /*! @internal */ + inline std::string demangle(std::string mangledName) + { + int status = 0; + char *demangledName = nullptr; + std::size_t len; + + demangledName = abi::__cxa_demangle(mangledName.c_str(), 0, &len, &status); + + std::string retName(demangledName); + free(demangledName); + + return retName; + } + + //! Gets the demangled name of a type + /*! @internal */ + template inline + std::string demangledName() + { return demangle(typeid(T).name()); } + } +} // namespace cereal +#endif // clang or gcc branch of _MSC_VER +#endif // CEREAL_DETAILS_UTIL_HPP_ diff --git a/tpl/cereal/include/cereal/external/base64.hpp b/tpl/cereal/include/cereal/external/base64.hpp new file mode 100644 index 0000000..7eee003 --- /dev/null +++ b/tpl/cereal/include/cereal/external/base64.hpp @@ -0,0 +1,127 @@ +/* + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +#ifndef CEREAL_EXTERNAL_BASE64_HPP_ +#define CEREAL_EXTERNAL_BASE64_HPP_ + +#include + +namespace cereal +{ + namespace base64 + { + static const std::string chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); + } + + inline std::string encode(unsigned char const* bytes_to_encode, size_t in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = static_cast((char_array_3[0] & 0xfc) >> 2); + char_array_4[1] = static_cast( ( ( char_array_3[0] & 0x03 ) << 4 ) + ( ( char_array_3[1] & 0xf0 ) >> 4 ) ); + char_array_4[2] = static_cast( ( ( char_array_3[1] & 0x0f ) << 2 ) + ( ( char_array_3[2] & 0xc0 ) >> 6 ) ); + char_array_4[3] = static_cast( char_array_3[2] & 0x3f ); + + for(i = 0; (i <4) ; i++) + ret += chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + } + + return ret; + } + + inline std::string decode(std::string const& encoded_string) { + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = static_cast(chars.find( char_array_4[i] )); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = static_cast(chars.find( char_array_4[j] )); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; + } + } // namespace base64 +} // namespace cereal + +#endif // CEREAL_EXTERNAL_BASE64_HPP_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/allocators.h b/tpl/cereal/include/cereal/external/rapidjson/allocators.h new file mode 100644 index 0000000..554dc4a --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/allocators.h @@ -0,0 +1,271 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ALLOCATORS_H_ +#define CEREAL_RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + CEREAL_RAPIDJSON_ASSERT(buffer != 0); + CEREAL_RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + CEREAL_RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = CEREAL_RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = CEREAL_RAPIDJSON_ALIGN(originalSize); + newSize = CEREAL_RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = CEREAL_RAPIDJSON_NEW(BaseAllocator()); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(CEREAL_RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_ENCODINGS_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/document.h b/tpl/cereal/include/cereal/external/rapidjson/document.h new file mode 100644 index 0000000..46faeb1 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/document.h @@ -0,0 +1,2575 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_DOCUMENT_H_ +#define CEREAL_RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include + +CEREAL_RAPIDJSON_DIAG_PUSH +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +CEREAL_RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_OFF(padded) +CEREAL_RAPIDJSON_DIAG_OFF(switch-enum) +CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +CEREAL_RAPIDJSON_DIAG_OFF(terminate) // ignore throwing CEREAL_RAPIDJSON_ASSERT in CEREAL_RAPIDJSON_NOEXCEPT functions +#endif +#endif // __GNUC__ + +#ifndef CEREAL_RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef CEREAL_RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c CEREAL_RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // CEREAL_RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // CEREAL_RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) CEREAL_RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ CEREAL_RAPIDJSON_ASSERT(s != 0); } + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { CEREAL_RAPIDJSON_ASSERT(s != 0); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref CEREAL_RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() CEREAL_RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) CEREAL_RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) CEREAL_RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + CEREAL_RAPIDJSON_ASSERT(type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef CEREAL_RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, CEREAL_RAPIDJSON_ENABLEIF((internal::IsSame))) CEREAL_RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) CEREAL_RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + CEREAL_RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) CEREAL_RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) CEREAL_RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) CEREAL_RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & CEREAL_RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & CEREAL_RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(CEREAL_RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) CEREAL_RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & CEREAL_RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & CEREAL_RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & CEREAL_RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) CEREAL_RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) CEREAL_RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) CEREAL_RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref CEREAL_RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) CEREAL_RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) CEREAL_RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) CEREAL_RAPIDJSON_NOEXCEPT { + CEREAL_RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) CEREAL_RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) CEREAL_RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + CEREAL_RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) CEREAL_RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) CEREAL_RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() CEREAL_RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref CEREAL_RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast(std::numeric_limits::max())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast(std::numeric_limits::min())) + && (d < static_cast(std::numeric_limits::max())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-std::numeric_limits::max()) + || a > static_cast(std::numeric_limits::max())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { CEREAL_RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { CEREAL_RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { CEREAL_RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + CEREAL_RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { CEREAL_RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { CEREAL_RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { CEREAL_RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { CEREAL_RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + CEREAL_RAPIDJSON_ASSERT(IsObject()); + CEREAL_RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + CEREAL_RAPIDJSON_ASSERT(IsObject()); + CEREAL_RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); + } + } + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + CEREAL_RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + CEREAL_RAPIDJSON_ASSERT(IsObject()); + CEREAL_RAPIDJSON_ASSERT(data_.o.size > 0); + CEREAL_RAPIDJSON_ASSERT(GetMembersPointer() != 0); + CEREAL_RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + CEREAL_RAPIDJSON_ASSERT(IsObject()); + CEREAL_RAPIDJSON_ASSERT(data_.o.size > 0); + CEREAL_RAPIDJSON_ASSERT(GetMembersPointer() != 0); + CEREAL_RAPIDJSON_ASSERT(first >= MemberBegin()); + CEREAL_RAPIDJSON_ASSERT(first <= last); + CEREAL_RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { CEREAL_RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { CEREAL_RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { CEREAL_RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { CEREAL_RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { CEREAL_RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + CEREAL_RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { CEREAL_RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { CEREAL_RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + CEREAL_RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + CEREAL_RAPIDJSON_ASSERT(IsArray()); + CEREAL_RAPIDJSON_ASSERT(data_.a.size > 0); + CEREAL_RAPIDJSON_ASSERT(GetElementsPointer() != 0); + CEREAL_RAPIDJSON_ASSERT(first >= Begin()); + CEREAL_RAPIDJSON_ASSERT(first <= last); + CEREAL_RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { CEREAL_RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { CEREAL_RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { CEREAL_RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { CEREAL_RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { CEREAL_RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { CEREAL_RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + CEREAL_RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + CEREAL_RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { CEREAL_RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { CEREAL_RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref CEREAL_RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + CEREAL_RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (CEREAL_RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (CEREAL_RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + CEREAL_RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct Flag { +#if CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif CEREAL_RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if CEREAL_RAPIDJSON_ENDIAN == CEREAL_RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION + + CEREAL_RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return CEREAL_RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + CEREAL_RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return CEREAL_RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + CEREAL_RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return CEREAL_RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + CEREAL_RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return CEREAL_RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + CEREAL_RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return CEREAL_RAPIDJSON_GETPOINTER(Member, data_.o.members); } + CEREAL_RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return CEREAL_RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) CEREAL_RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) CEREAL_RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + CEREAL_RAPIDJSON_ASSERT(IsString()); + CEREAL_RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) CEREAL_RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) CEREAL_RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) CEREAL_RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) CEREAL_RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + CEREAL_RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + CEREAL_RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + CEREAL_RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + CEREAL_RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // CEREAL_RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + CEREAL_RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + CEREAL_RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } +} + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { return value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +CEREAL_RAPIDJSON_NAMESPACE_END +CEREAL_RAPIDJSON_DIAG_POP + +#endif // CEREAL_RAPIDJSON_DOCUMENT_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/encodedstream.h b/tpl/cereal/include/cereal/external/rapidjson/encodedstream.h new file mode 100644 index 0000000..9671394 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ENCODEDSTREAM_H_ +#define CEREAL_RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define CEREAL_RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + CEREAL_RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + CEREAL_RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef CEREAL_RAPIDJSON_ENCODINGS_FUNC + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_FILESTREAM_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/encodings.h b/tpl/cereal/include/cereal/external/rapidjson/encodings.h new file mode 100644 index 0000000..ed00b97 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ENCODINGS_H_ +#define CEREAL_RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +CEREAL_RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +CEREAL_RAPIDJSON_DIAG_OFF(overflow) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFF >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + CEREAL_RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + CEREAL_RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, (v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + CEREAL_RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define CEREAL_RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + CEREAL_RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { CEREAL_RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef CEREAL_RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSC_VER) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_ENCODINGS_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/error/en.h b/tpl/cereal/include/cereal/external/rapidjson/error/en.h new file mode 100644 index 0000000..ea93bf8 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/error/en.h @@ -0,0 +1,74 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ERROR_EN_H_ +#define CEREAL_RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(switch-enum) +CEREAL_RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup CEREAL_RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const CEREAL_RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return CEREAL_RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return CEREAL_RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return CEREAL_RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return CEREAL_RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return CEREAL_RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return CEREAL_RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return CEREAL_RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return CEREAL_RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return CEREAL_RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return CEREAL_RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return CEREAL_RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return CEREAL_RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_ERROR_EN_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/error/error.h b/tpl/cereal/include/cereal/external/rapidjson/error/error.h new file mode 100644 index 0000000..c2638f1 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/error/error.h @@ -0,0 +1,155 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ERROR_ERROR_H_ +#define CEREAL_RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup CEREAL_RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup CEREAL_RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef CEREAL_RAPIDJSON_ERROR_CHARTYPE +#define CEREAL_RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref CEREAL_RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup CEREAL_RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef CEREAL_RAPIDJSON_ERROR_STRING +#define CEREAL_RAPIDJSON_ERROR_STRING(x) x +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup CEREAL_RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup CEREAL_RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup CEREAL_RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const CEREAL_RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const CEREAL_RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_ERROR_ERROR_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/filereadstream.h b/tpl/cereal/include/cereal/external/rapidjson/filereadstream.h new file mode 100644 index 0000000..abafb54 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_FILEREADSTREAM_H_ +#define CEREAL_RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code) +CEREAL_RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + CEREAL_RAPIDJSON_ASSERT(fp_ != 0); + CEREAL_RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_FILESTREAM_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/filewritestream.h b/tpl/cereal/include/cereal/external/rapidjson/filewritestream.h new file mode 100644 index 0000000..9a60099 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_FILEWRITESTREAM_H_ +#define CEREAL_RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + CEREAL_RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + char Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_FILESTREAM_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/fwd.h b/tpl/cereal/include/cereal/external/rapidjson/fwd.h new file mode 100644 index 0000000..1b15c66 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_FWD_H_ +#define CEREAL_RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/biginteger.h b/tpl/cereal/include/cereal/external/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..f8a5a50 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_BIGINTEGER_H_ +#define CEREAL_RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + CEREAL_RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + CEREAL_RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= CEREAL_RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + CEREAL_RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { CEREAL_RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + CEREAL_RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + CEREAL_RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_BIGINTEGER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/diyfp.h b/tpl/cereal/include/cereal/external/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..ab7d66b --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/diyfp.h @@ -0,0 +1,258 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef CEREAL_RAPIDJSON_DIYFP_H_ +#define CEREAL_RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = CEREAL_RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = CEREAL_RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = CEREAL_RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + CEREAL_RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), CEREAL_RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + CEREAL_RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), CEREAL_RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + CEREAL_RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), CEREAL_RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + CEREAL_RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), CEREAL_RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + CEREAL_RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), CEREAL_RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + CEREAL_RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), CEREAL_RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + CEREAL_RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), CEREAL_RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + CEREAL_RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), CEREAL_RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + CEREAL_RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), CEREAL_RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + CEREAL_RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), CEREAL_RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + CEREAL_RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), CEREAL_RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + CEREAL_RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), CEREAL_RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + CEREAL_RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), CEREAL_RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + CEREAL_RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), CEREAL_RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + CEREAL_RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), CEREAL_RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + CEREAL_RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), CEREAL_RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + CEREAL_RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), CEREAL_RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + CEREAL_RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), CEREAL_RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + CEREAL_RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), CEREAL_RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + CEREAL_RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), CEREAL_RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + CEREAL_RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), CEREAL_RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + CEREAL_RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), CEREAL_RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + CEREAL_RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), CEREAL_RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + CEREAL_RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), CEREAL_RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + CEREAL_RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), CEREAL_RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + CEREAL_RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), CEREAL_RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + CEREAL_RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), CEREAL_RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + CEREAL_RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), CEREAL_RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + CEREAL_RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), CEREAL_RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + CEREAL_RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), CEREAL_RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + CEREAL_RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), CEREAL_RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + CEREAL_RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), CEREAL_RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + CEREAL_RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), CEREAL_RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + CEREAL_RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), CEREAL_RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + CEREAL_RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), CEREAL_RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + CEREAL_RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), CEREAL_RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + CEREAL_RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), CEREAL_RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + CEREAL_RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), CEREAL_RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + CEREAL_RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), CEREAL_RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + CEREAL_RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), CEREAL_RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + CEREAL_RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), CEREAL_RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + CEREAL_RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), CEREAL_RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + CEREAL_RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), CEREAL_RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + CEREAL_RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_DIYFP_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/dtoa.h b/tpl/cereal/include/cereal/external/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..c59c92e --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef CEREAL_RAPIDJSON_DTOA_ +#define CEREAL_RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +CEREAL_RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -static_cast(kappa); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + CEREAL_RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_DTOA_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/ieee754.h b/tpl/cereal/include/cereal/external/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..35845a1 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_IEEE754_ +#define CEREAL_RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + CEREAL_RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return static_cast(order) + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = CEREAL_RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = CEREAL_RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = CEREAL_RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = CEREAL_RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_IEEE754_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/itoa.h b/tpl/cereal/include/cereal/external/rapidjson/internal/itoa.h new file mode 100644 index 0000000..3e83658 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/itoa.h @@ -0,0 +1,304 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ITOA_ +#define CEREAL_RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_ITOA_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/meta.h b/tpl/cereal/include/cereal/external/rapidjson/internal/meta.h new file mode 100644 index 0000000..c98db90 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/meta.h @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_INTERNAL_META_H_ +#define CEREAL_RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(6334) +#endif + +#if CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond CEREAL_RAPIDJSON_INTERNAL +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + CEREAL_RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define CEREAL_RAPIDJSON_REMOVEFPTR_(type) \ + typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::CEREAL_RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define CEREAL_RAPIDJSON_ENABLEIF(cond) \ + typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define CEREAL_RAPIDJSON_DISABLEIF(cond) \ + typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define CEREAL_RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define CEREAL_RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::CEREAL_RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_INTERNAL_META_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/pow10.h b/tpl/cereal/include/cereal/external/rapidjson/internal/pow10.h new file mode 100644 index 0000000..7f796a1 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_POW10_ +#define CEREAL_RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + CEREAL_RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_POW10_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/regex.h b/tpl/cereal/include/cereal/external/rapidjson/internal/regex.h new file mode 100644 index 0000000..3bfa3ec --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/regex.h @@ -0,0 +1,701 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_INTERNAL_REGEX_H_ +#define CEREAL_RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +CEREAL_RAPIDJSON_DIAG_OFF(switch-enum) +CEREAL_RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef CEREAL_RAPIDJSON_REGEX_VERBOSE +#define CEREAL_RAPIDJSON_REGEX_VERBOSE 0 +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef typename Encoding::Ch Ch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream > ds(ss); + Parse(ds); + } + + ~GenericRegex() { + Allocator::Free(stateSet_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + + template + bool Match(InputStream& is) const { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) const { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) const { + return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); + } + + bool Search(const Ch* s) const { + GenericStringStream is(s); + return Search(is); + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + template + class DecodedStream { + public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + + private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; + }; + + State& GetState(SizeType index) { + CEREAL_RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + CEREAL_RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + CEREAL_RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + CEREAL_RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if CEREAL_RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + + // Preallocate buffer for SearchWithAnchoring() + CEREAL_RAPIDJSON_ASSERT(stateSet_ == 0); + if (stateCount_ > 0) { + stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); + state0_.template Reserve(stateCount_); + state1_.template Reserve(stateCount_); + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + CEREAL_RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + CEREAL_RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + CEREAL_RAPIDJSON_ASSERT(n <= m); + CEREAL_RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + CEREAL_RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + CEREAL_RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == kAnyCharacterClass || + (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) const { + CEREAL_RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { + stateSet_[index >> 5] |= (1 << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = GetRange(rangeIndex); + if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + uint32_t* stateSet_; // allocated by states_.GetAllocator() + mutable Stack state0_; + mutable Stack state1_; + bool anchorBegin_; + bool anchorEnd_; +}; + +typedef GenericRegex > Regex; + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/stack.h b/tpl/cereal/include/cereal/external/rapidjson/internal/stack.h new file mode 100644 index 0000000..768551e --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/stack.h @@ -0,0 +1,230 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_INTERNAL_STACK_H_ +#define CEREAL_RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) CEREAL_RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + CEREAL_RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (CEREAL_RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + Expand(count); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + CEREAL_RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + CEREAL_RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + CEREAL_RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + CEREAL_RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + CEREAL_RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + CEREAL_RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_STACK_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/strfunc.h b/tpl/cereal/include/cereal/external/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..0c6973c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/strfunc.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_ +#define CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/strtod.h b/tpl/cereal/include/cereal/external/rapidjson/internal/strtod.h new file mode 100644 index 0000000..076434c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/strtod.h @@ -0,0 +1,269 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_STRTOD_ +#define CEREAL_RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(CEREAL_RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + CEREAL_RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + static_cast(kUlp); + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + CEREAL_RAPIDJSON_ASSERT(d >= 0.0); + CEREAL_RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= static_cast(delta); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_STRTOD_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/internal/swap.h b/tpl/cereal/include/cereal/external/rapidjson/internal/swap.h new file mode 100644 index 0000000..5d8910c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_INTERNAL_SWAP_H_ +#define CEREAL_RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) CEREAL_RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +CEREAL_RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/istreamwrapper.h b/tpl/cereal/include/cereal/external/rapidjson/istreamwrapper.h new file mode 100644 index 0000000..e1c7ea8 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/istreamwrapper.h @@ -0,0 +1,116 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_ +#define CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +CEREAL_RAPIDJSON_DIAG_OFF(4127) // ignore assert(false) for triggering exception +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return CEREAL_RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (CEREAL_RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + CEREAL_RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/memorybuffer.h b/tpl/cereal/include/cereal/external/rapidjson/memorybuffer.h new file mode 100644 index 0000000..0cd75cf --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_MEMORYBUFFER_H_ +#define CEREAL_RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_MEMORYBUFFER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/memorystream.h b/tpl/cereal/include/cereal/external/rapidjson/memorystream.h new file mode 100644 index 0000000..e48988c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/memorystream.h @@ -0,0 +1,76 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_MEMORYSTREAM_H_ +#define CEREAL_RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code) +CEREAL_RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF( 4127 ) // ignore assert(false) for triggering exception +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return CEREAL_RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return CEREAL_RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_MEMORYBUFFER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/msinttypes/inttypes.h b/tpl/cereal/include/cereal/external/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/tpl/cereal/include/cereal/external/rapidjson/msinttypes/stdint.h b/tpl/cereal/include/cereal/external/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..3d4477b --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/tpl/cereal/include/cereal/external/rapidjson/ostreamwrapper.h b/tpl/cereal/include/cereal/external/rapidjson/ostreamwrapper.h new file mode 100644 index 0000000..58c034a --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_ +#define CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + char Take() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/pointer.h b/tpl/cereal/include/cereal/external/rapidjson/pointer.h new file mode 100644 index 0000000..1e4836c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/pointer.h @@ -0,0 +1,1358 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_POINTER_H_ +#define CEREAL_RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup CEREAL_RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref CEREAL_RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + CEREAL_RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, StrLen(name), allocator); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = buffer[i]; + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + CEREAL_RAPIDJSON_ASSERT(token.IsUint64()); + CEREAL_RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + CEREAL_RAPIDJSON_ASSERT(source != NULL); + CEREAL_RAPIDJSON_ASSERT(nameBuffer_ == 0); + CEREAL_RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + CEREAL_RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + CEREAL_RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +CEREAL_RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_POINTER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/prettywriter.h b/tpl/cereal/include/cereal/external/rapidjson/prettywriter.h new file mode 100644 index 0000000..50a24fc --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/prettywriter.h @@ -0,0 +1,255 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_PRETTYWRITER_H_ +#define CEREAL_RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + CEREAL_RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + CEREAL_RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndObject(); + (void)ret; + CEREAL_RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + CEREAL_RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndArray(); + (void)ret; + CEREAL_RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + CEREAL_RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + CEREAL_RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/rapidjson.h b/tpl/cereal/include/cereal/external/rapidjson/rapidjson.h new file mode 100644 index 0000000..b79023c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/rapidjson.h @@ -0,0 +1,615 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_ +#define CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see CEREAL_RAPIDJSON_CONFIG + */ + +/*! \defgroup CEREAL_RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref CEREAL_RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define CEREAL_RAPIDJSON_STRINGIFY(x) CEREAL_RAPIDJSON_DO_STRINGIFY(x) +#define CEREAL_RAPIDJSON_DO_STRINGIFY(x) #x +//!@endcond + +/*! \def CEREAL_RAPIDJSON_MAJOR_VERSION + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def CEREAL_RAPIDJSON_MINOR_VERSION + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def CEREAL_RAPIDJSON_PATCH_VERSION + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def CEREAL_RAPIDJSON_VERSION_STRING + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define CEREAL_RAPIDJSON_MAJOR_VERSION 1 +#define CEREAL_RAPIDJSON_MINOR_VERSION 0 +#define CEREAL_RAPIDJSON_PATCH_VERSION 2 +#define CEREAL_RAPIDJSON_VERSION_STRING \ + CEREAL_RAPIDJSON_STRINGIFY(CEREAL_RAPIDJSON_MAJOR_VERSION.CEREAL_RAPIDJSON_MINOR_VERSION.CEREAL_RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def CEREAL_RAPIDJSON_NAMESPACE + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c CEREAL_RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref CEREAL_RAPIDJSON_NAMESPACE_BEGIN and \ref + CEREAL_RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define CEREAL_RAPIDJSON_NAMESPACE my::rapidjson + #define CEREAL_RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define CEREAL_RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def CEREAL_RAPIDJSON_NAMESPACE_BEGIN + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see CEREAL_RAPIDJSON_NAMESPACE +*/ +/*! \def CEREAL_RAPIDJSON_NAMESPACE_END + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see CEREAL_RAPIDJSON_NAMESPACE +*/ +#ifndef CEREAL_RAPIDJSON_NAMESPACE +#define CEREAL_RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef CEREAL_RAPIDJSON_NAMESPACE_BEGIN +#define CEREAL_RAPIDJSON_NAMESPACE_BEGIN namespace CEREAL_RAPIDJSON_NAMESPACE { +#endif +#ifndef CEREAL_RAPIDJSON_NAMESPACE_END +#define CEREAL_RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_HAS_STDSTRING + +#ifndef CEREAL_RAPIDJSON_HAS_STDSTRING +#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING +#define CEREAL_RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define CEREAL_RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def CEREAL_RAPIDJSON_HAS_STDSTRING + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(CEREAL_RAPIDJSON_HAS_STDSTRING) + +#if CEREAL_RAPIDJSON_HAS_STDSTRING +#include +#endif // CEREAL_RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_NO_INT64DEFINE + +/*! \def CEREAL_RAPIDJSON_NO_INT64DEFINE + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define CEREAL_RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef CEREAL_RAPIDJSON_NO_INT64DEFINE +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING +#define CEREAL_RAPIDJSON_NO_INT64DEFINE +#endif +#endif // CEREAL_RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_FORCEINLINE + +#ifndef CEREAL_RAPIDJSON_FORCEINLINE +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define CEREAL_RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define CEREAL_RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define CEREAL_RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // CEREAL_RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_ENDIAN +#define CEREAL_RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define CEREAL_RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def CEREAL_RAPIDJSON_ENDIAN + \ingroup CEREAL_RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define CEREAL_RAPIDJSON_ENDIAN to either + \ref CEREAL_RAPIDJSON_LITTLEENDIAN or \ref CEREAL_RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef CEREAL_RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && defined(_M_ARM) +# define CEREAL_RAPIDJSON_ENDIAN CEREAL_RAPIDJSON_LITTLEENDIAN +# elif defined(CEREAL_RAPIDJSON_DOXYGEN_RUNNING) +# define CEREAL_RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define CEREAL_RAPIDJSON_ENDIAN. +# endif +#endif // CEREAL_RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef CEREAL_RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define CEREAL_RAPIDJSON_64BIT 1 +#else +#define CEREAL_RAPIDJSON_64BIT 0 +#endif +#endif // CEREAL_RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup CEREAL_RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the CEREAL_RAPIDJSON_ALIGN function macro. +*/ +#ifndef CEREAL_RAPIDJSON_ALIGN +#if CEREAL_RAPIDJSON_64BIT == 1 +#define CEREAL_RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#else +#define CEREAL_RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef CEREAL_RAPIDJSON_UINT64_C2 +#define CEREAL_RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup CEREAL_RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if CEREAL_RAPIDJSON_64BIT != 1 +#error CEREAL_RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when CEREAL_RAPIDJSON_64BIT=1 +#endif +#define CEREAL_RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(CEREAL_RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define CEREAL_RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(CEREAL_RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define CEREAL_RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define CEREAL_RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_SSE2/CEREAL_RAPIDJSON_SSE42/CEREAL_RAPIDJSON_SIMD + +/*! \def CEREAL_RAPIDJSON_SIMD + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define CEREAL_RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define CEREAL_RAPIDJSON_SSE42 + \endcode + + \c CEREAL_RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c CEREAL_RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(CEREAL_RAPIDJSON_SSE2) || defined(CEREAL_RAPIDJSON_SSE42) \ + || defined(CEREAL_RAPIDJSON_DOXYGEN_RUNNING) +#define CEREAL_RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef CEREAL_RAPIDJSON_DOXYGEN_RUNNING +#define CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE +#endif +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref CEREAL_RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +CEREAL_RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +CEREAL_RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup CEREAL_RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining CEREAL_RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref CEREAL_RAPIDJSON_ERRORS APIs. +*/ +#ifndef CEREAL_RAPIDJSON_ASSERT +#include +#define CEREAL_RAPIDJSON_ASSERT(x) assert(x) +#endif // CEREAL_RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef CEREAL_RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +CEREAL_RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +CEREAL_RAPIDJSON_NAMESPACE_END + +#define CEREAL_RAPIDJSON_JOIN(X, Y) CEREAL_RAPIDJSON_DO_JOIN(X, Y) +#define CEREAL_RAPIDJSON_DO_JOIN(X, Y) CEREAL_RAPIDJSON_DO_JOIN2(X, Y) +#define CEREAL_RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def CEREAL_RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define CEREAL_RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::CEREAL_RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::CEREAL_RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + CEREAL_RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) CEREAL_RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_LIKELY, CEREAL_RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup CEREAL_RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef CEREAL_RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define CEREAL_RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define CEREAL_RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup CEREAL_RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef CEREAL_RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define CEREAL_RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define CEREAL_RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define CEREAL_RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define CEREAL_RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_DIAG_PUSH/POP, CEREAL_RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define CEREAL_RAPIDJSON_GNUC \ + CEREAL_RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(CEREAL_RAPIDJSON_GNUC) && CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,2,0)) + +#define CEREAL_RAPIDJSON_PRAGMA(x) _Pragma(CEREAL_RAPIDJSON_STRINGIFY(x)) +#define CEREAL_RAPIDJSON_DIAG_PRAGMA(x) CEREAL_RAPIDJSON_PRAGMA(GCC diagnostic x) +#define CEREAL_RAPIDJSON_DIAG_OFF(x) \ + CEREAL_RAPIDJSON_DIAG_PRAGMA(ignored CEREAL_RAPIDJSON_STRINGIFY(CEREAL_RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(CEREAL_RAPIDJSON_GNUC) && CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,6,0)) +#define CEREAL_RAPIDJSON_DIAG_PUSH CEREAL_RAPIDJSON_DIAG_PRAGMA(push) +#define CEREAL_RAPIDJSON_DIAG_POP CEREAL_RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define CEREAL_RAPIDJSON_DIAG_PUSH /* ignored */ +#define CEREAL_RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define CEREAL_RAPIDJSON_PRAGMA(x) __pragma(x) +#define CEREAL_RAPIDJSON_DIAG_PRAGMA(x) CEREAL_RAPIDJSON_PRAGMA(warning(x)) + +#define CEREAL_RAPIDJSON_DIAG_OFF(x) CEREAL_RAPIDJSON_DIAG_PRAGMA(disable: x) +#define CEREAL_RAPIDJSON_DIAG_PUSH CEREAL_RAPIDJSON_DIAG_PRAGMA(push) +#define CEREAL_RAPIDJSON_DIAG_POP CEREAL_RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define CEREAL_RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define CEREAL_RAPIDJSON_DIAG_PUSH /* ignored */ +#define CEREAL_RAPIDJSON_DIAG_POP /* ignored */ + +#endif // CEREAL_RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT +#define CEREAL_RAPIDJSON_NOEXCEPT noexcept +#else +#define CEREAL_RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // CEREAL_RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS +#define CEREAL_RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +#ifndef CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(CEREAL_RAPIDJSON_GNUC) && (CEREAL_RAPIDJSON_GNUC >= CEREAL_RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // CEREAL_RAPIDJSON_HAS_CXX11_RANGE_FOR + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef CEREAL_RAPIDJSON_NEW +///! customization point for global \c new +#define CEREAL_RAPIDJSON_NEW(x) new x +#endif +#ifndef CEREAL_RAPIDJSON_DELETE +///! customization point for global \c delete +#define CEREAL_RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see CEREAL_RAPIDJSON_NAMESPACE +*/ +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/reader.h b/tpl/cereal/include/cereal/external/rapidjson/reader.h new file mode 100644 index 0000000..ad8ff33 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/reader.h @@ -0,0 +1,1879 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_READER_H_ +#define CEREAL_RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(CEREAL_RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef CEREAL_RAPIDJSON_SSE42 +#include +#elif defined(CEREAL_RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +CEREAL_RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(old-style-cast) +CEREAL_RAPIDJSON_DIAG_OFF(padded) +CEREAL_RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define CEREAL_RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (CEREAL_RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + CEREAL_RAPIDJSON_MULTILINEMACRO_END +#endif +#define CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(CEREAL_RAPIDJSON_NOTHING) +//!@endcond + +/*! \def CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup CEREAL_RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see CEREAL_RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN +#define CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN \ + CEREAL_RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + CEREAL_RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def CEREAL_RAPIDJSON_PARSE_ERROR + \ingroup CEREAL_RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef CEREAL_RAPIDJSON_PARSE_ERROR +#define CEREAL_RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN \ + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + CEREAL_RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS +#define CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseDefaultFlags = CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef CEREAL_RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(CEREAL_RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#endif // CEREAL_RAPIDJSON_SSE2 + +#ifdef CEREAL_RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // CEREAL_RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (CEREAL_RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (CEREAL_RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (CEREAL_RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (CEREAL_RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (CEREAL_RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n'); + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + CEREAL_RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.StartObject())) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (CEREAL_RAPIDJSON_UNLIKELY(is.Peek() != '"')) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (CEREAL_RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + CEREAL_RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.StartArray())) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + CEREAL_RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (CEREAL_RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.Null())) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + CEREAL_RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (CEREAL_RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.Bool(true))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + CEREAL_RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (CEREAL_RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (CEREAL_RAPIDJSON_UNLIKELY(!handler.Bool(false))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + CEREAL_RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (CEREAL_RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + CEREAL_RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + CEREAL_RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + CEREAL_RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + CEREAL_RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (CEREAL_RAPIDJSON_UNLIKELY(!success)) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + CEREAL_RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (CEREAL_RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && CEREAL_RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if (CEREAL_RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (CEREAL_RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (CEREAL_RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (CEREAL_RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (CEREAL_RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (CEREAL_RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (CEREAL_RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static CEREAL_RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(CEREAL_RAPIDJSON_SSE2) || defined(CEREAL_RAPIDJSON_SSE42) + // StringStream -> StackStream + static CEREAL_RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (CEREAL_RAPIDJSON_UNLIKELY(*p == '\"') || CEREAL_RAPIDJSON_UNLIKELY(*p == '\\') || CEREAL_RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (CEREAL_RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static CEREAL_RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + CEREAL_RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (CEREAL_RAPIDJSON_UNLIKELY(*p == '\"') || CEREAL_RAPIDJSON_UNLIKELY(*p == '\\') || CEREAL_RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (CEREAL_RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static CEREAL_RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + CEREAL_RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (CEREAL_RAPIDJSON_UNLIKELY(*p == '\"') || CEREAL_RAPIDJSON_UNLIKELY(*p == '\\') || CEREAL_RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (CEREAL_RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} + + CEREAL_RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + CEREAL_RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + CEREAL_RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + CEREAL_RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} + ~NumberStream() {} + + CEREAL_RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + CEREAL_RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + ~NumberStream() {} + + CEREAL_RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (CEREAL_RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (CEREAL_RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (CEREAL_RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (CEREAL_RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (CEREAL_RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && CEREAL_RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + useNanOrInf = true; + if (CEREAL_RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { + d = std::numeric_limits::quiet_NaN(); + } + else if (CEREAL_RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + if (CEREAL_RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (CEREAL_RAPIDJSON_UNLIKELY(i64 >= CEREAL_RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (CEREAL_RAPIDJSON_LIKELY(i64 != CEREAL_RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (CEREAL_RAPIDJSON_UNLIKELY(i64 >= CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (CEREAL_RAPIDJSON_LIKELY(i64 != CEREAL_RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (CEREAL_RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (CEREAL_RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if CEREAL_RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > CEREAL_RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (CEREAL_RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (CEREAL_RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (CEREAL_RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (CEREAL_RAPIDJSON_UNLIKELY(exp > maxExp)) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + CEREAL_RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (CEREAL_RAPIDJSON_UNLIKELY(!cont)) + CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState + }; + + enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + CEREAL_RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond CEREAL_RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + CEREAL_RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + CEREAL_RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + CEREAL_RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + CEREAL_RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + CEREAL_RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: CEREAL_RAPIDJSON_ASSERT(src == IterativeParsingElementState); CEREAL_RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + CEREAL_RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_READER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/schema.h b/tpl/cereal/include/cereal/external/rapidjson/schema.h new file mode 100644 index 0000000..1298755 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/schema.h @@ -0,0 +1,2006 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef CEREAL_RAPIDJSON_SCHEMA_H_ +#define CEREAL_RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX || CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX +#define CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef CEREAL_RAPIDJSON_SCHEMA_VERBOSE +#define CEREAL_RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +CEREAL_RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +CEREAL_RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_OFF(weak-vtables) +CEREAL_RAPIDJSON_DIAG_OFF(exit-time-destructors) +CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +CEREAL_RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // CEREAL_RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN + +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE +#define CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +CEREAL_RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + CEREAL_RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(CEREAL_RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = CEREAL_RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = GetTypeless(); + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + if (allocator_) { + allocator_->Free(enum_); + } + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + allocator_->Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = GetTypeless(); + else + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = GetTypeless(); + + context.arrayElementIndex++; + } + return true; + } + + CEREAL_RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = GetTypeless(); + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define CEREAL_RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + return v;\ + } + + CEREAL_RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + CEREAL_RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + CEREAL_RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + CEREAL_RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + CEREAL_RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + CEREAL_RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + CEREAL_RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + CEREAL_RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + CEREAL_RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + CEREAL_RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + CEREAL_RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + CEREAL_RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + CEREAL_RAPIDJSON_STRING_(Not, 'n', 'o', 't') + CEREAL_RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + CEREAL_RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + CEREAL_RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + CEREAL_RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + CEREAL_RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + CEREAL_RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + CEREAL_RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + CEREAL_RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + CEREAL_RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + CEREAL_RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + CEREAL_RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + CEREAL_RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + CEREAL_RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + CEREAL_RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + CEREAL_RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef CEREAL_RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + static const SchemaType* GetTypeless() { + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + return &typeless; + } + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + return pattern->Search(str); + } +#elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + CEREAL_RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) + /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + CEREAL_RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = buffer[i]; + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + CEREAL_RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator()); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + refEntry->~SchemaRefEntry(); + } + + CEREAL_RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) CEREAL_RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + CEREAL_RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = SchemaType::GetTypeless(); + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + CEREAL_RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(outputHandler), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + CEREAL_RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +CEREAL_RAPIDJSON_MULTILINEMACRO_END +#else +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && outputHandler_.method arg2 + +#define CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = outputHandler_.StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = outputHandler_.Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = outputHandler_.StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + return StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = CEREAL_RAPIDJSON_NEW(StateAllocator()); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + if (CurrentContext().valueSchema) + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + CEREAL_RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + CEREAL_RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static OutputHandler& GetNullHandler() { + static OutputHandler nullHandler; + return nullHandler; + } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + bool valid_; +#if CEREAL_RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +CEREAL_RAPIDJSON_NAMESPACE_END +CEREAL_RAPIDJSON_DIAG_POP + +#endif // CEREAL_RAPIDJSON_SCHEMA_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/stream.h b/tpl/cereal/include/cereal/external/rapidjson/stream.h new file mode 100644 index 0000000..c05c2d4 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef CEREAL_RAPIDJSON_STREAM_H_ +#define CEREAL_RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { CEREAL_RAPIDJSON_ASSERT(false); } + void Flush() { CEREAL_RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { CEREAL_RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { CEREAL_RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +CEREAL_RAPIDJSON_NAMESPACE_END + +#endif // CEREAL_RAPIDJSON_STREAM_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/stringbuffer.h b/tpl/cereal/include/cereal/external/rapidjson/stringbuffer.h new file mode 100644 index 0000000..76eb730 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/stringbuffer.h @@ -0,0 +1,117 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_STRINGBUFFER_H_ +#define CEREAL_RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +CEREAL_RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_STRINGBUFFER_H_ diff --git a/tpl/cereal/include/cereal/external/rapidjson/writer.h b/tpl/cereal/include/cereal/external/rapidjson/writer.h new file mode 100644 index 0000000..ae86cb5 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidjson/writer.h @@ -0,0 +1,610 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef CEREAL_RAPIDJSON_WRITER_H_ +#define CEREAL_RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(CEREAL_RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef CEREAL_RAPIDJSON_SSE42 +#include +#elif defined(CEREAL_RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_PUSH +CEREAL_RAPIDJSON_DIAG_OFF(padded) +CEREAL_RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +CEREAL_RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup CEREAL_RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS +#define CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Inf, -Inf and NaN. + kWriteDefaultFlags = CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if CEREAL_RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + CEREAL_RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + CEREAL_RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + CEREAL_RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + CEREAL_RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return EndValue(WriteRawValue(json, length)); } + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (CEREAL_RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + CEREAL_RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && CEREAL_RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (CEREAL_RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return CEREAL_RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + CEREAL_RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (CEREAL_RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + CEREAL_RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + CEREAL_RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (CEREAL_RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(CEREAL_RAPIDJSON_SSE2) || defined(CEREAL_RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return CEREAL_RAPIDJSON_LIKELY(is.Tell() < length); + + if (!CEREAL_RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return CEREAL_RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (CEREAL_RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return CEREAL_RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // defined(CEREAL_RAPIDJSON_SSE2) || defined(CEREAL_RAPIDJSON_SSE42) + +CEREAL_RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +CEREAL_RAPIDJSON_DIAG_POP +#endif + +#endif // CEREAL_RAPIDJSON_CEREAL_RAPIDJSON_H_ diff --git a/tpl/cereal/include/cereal/external/rapidxml/license.txt b/tpl/cereal/include/cereal/external/rapidxml/license.txt new file mode 100644 index 0000000..0095bc7 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/license.txt @@ -0,0 +1,52 @@ +Use of this software is granted under one of the following two licenses, +to be chosen freely by the user. + +1. Boost Software License - Version 1.0 - August 17th, 2003 +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +2. The MIT License +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +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/tpl/cereal/include/cereal/external/rapidxml/manual.html b/tpl/cereal/include/cereal/external/rapidxml/manual.html new file mode 100644 index 0000000..4e6a5f1 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/manual.html @@ -0,0 +1,406 @@ +

RAPIDXML Manual

Version 1.13

Copyright (C) 2006, 2009 Marcin Kalicinski
See accompanying file
license.txt for license information.

Table of Contents

1. What is RapidXml?
1.1 Dependencies And Compatibility
1.2 Character Types And Encodings
1.3 Error Handling
1.4 Memory Allocation
1.5 W3C Compliance
1.6 API Design
1.7 Reliability
1.8 Acknowledgements
2. Two Minute Tutorial
2.1 Parsing
2.2 Accessing The DOM Tree
2.3 Modifying The DOM Tree
2.4 Printing XML
3. Differences From Regular XML Parsers
3.1 Lifetime Of Source Text
3.2 Ownership Of Strings
3.3 Destructive Vs Non-Destructive Mode
4. Performance
4.1 Comparison With Other Parsers
5. Reference

1. What is RapidXml?

RapidXml is an attempt to create the fastest XML DOM parser possible, while retaining useability, portability and reasonable W3C compatibility. It is an in-situ parser written in C++, with parsing speed approaching that of strlen() function executed on the same data.

+ Entire parser is contained in a single header file, so no building or linking is neccesary. To use it you just need to copy rapidxml.hpp file to a convenient place (such as your project directory), and include it where needed. You may also want to use printing functions contained in header rapidxml_print.hpp.

1.1 Dependencies And Compatibility

RapidXml has no dependencies other than a very small subset of standard C++ library (<cassert>, <cstdlib>, <new> and <exception>, unless exceptions are disabled). It should compile on any reasonably conformant compiler, and was tested on Visual C++ 2003, Visual C++ 2005, Visual C++ 2008, gcc 3, gcc 4, and Comeau 4.3.3. Care was taken that no warnings are produced on these compilers, even with highest warning levels enabled.

1.2 Character Types And Encodings

RapidXml is character type agnostic, and can work both with narrow and wide characters. Current version does not fully support UTF-16 or UTF-32, so use of wide characters is somewhat incapacitated. However, it should succesfully parse wchar_t strings containing UTF-16 or UTF-32 if endianness of the data matches that of the machine. UTF-8 is fully supported, including all numeric character references, which are expanded into appropriate UTF-8 byte sequences (unless you enable parse_no_utf8 flag).

+ Note that RapidXml performs no decoding - strings returned by name() and value() functions will contain text encoded using the same encoding as source file. Rapidxml understands and expands the following character references: &apos; &amp; &quot; &lt; &gt; &#...; Other character references are not expanded.

1.3 Error Handling

By default, RapidXml uses C++ exceptions to report errors. If this behaviour is undesirable, RAPIDXML_NO_EXCEPTIONS can be defined to suppress exception code. See parse_error class and parse_error_handler() function for more information.

1.4 Memory Allocation

RapidXml uses a special memory pool object to allocate nodes and attributes, because direct allocation using new operator would be far too slow. Underlying memory allocations performed by the pool can be customized by use of memory_pool::set_allocator() function. See class memory_pool for more information.

1.5 W3C Compliance

RapidXml is not a W3C compliant parser, primarily because it ignores DOCTYPE declarations. There is a number of other, minor incompatibilities as well. Still, it can successfully parse and produce complete trees of all valid XML files in W3C conformance suite (over 1000 files specially designed to find flaws in XML processors). In destructive mode it performs whitespace normalization and character entity substitution for a small set of built-in entities.

1.6 API Design

RapidXml API is minimalistic, to reduce code size as much as possible, and facilitate use in embedded environments. Additional convenience functions are provided in separate headers: rapidxml_utils.hpp and rapidxml_print.hpp. Contents of these headers is not an essential part of the library, and is currently not documented (otherwise than with comments in code).

1.7 Reliability

RapidXml is very robust and comes with a large harness of unit tests. Special care has been taken to ensure stability of the parser no matter what source text is thrown at it. One of the unit tests produces 100,000 randomly corrupted variants of XML document, which (when uncorrupted) contains all constructs recognized by RapidXml. RapidXml passes this test when it correctly recognizes that errors have been introduced, and does not crash or loop indefinitely.

+ Another unit test puts RapidXml head-to-head with another, well estabilished XML parser, and verifies that their outputs match across a wide variety of small and large documents.

+ Yet another test feeds RapidXml with over 1000 test files from W3C compliance suite, and verifies that correct results are obtained. There are also additional tests that verify each API function separately, and test that various parsing modes work as expected.

1.8 Acknowledgements

I would like to thank Arseny Kapoulkine for his work on pugixml, which was an inspiration for this project. Additional thanks go to Kristen Wegner for creating pugxml, from which pugixml was derived. Janusz Wohlfeil kindly ran RapidXml speed tests on hardware that I did not have access to, allowing me to expand performance comparison table.

2. Two Minute Tutorial

2.1 Parsing

The following code causes RapidXml to parse a zero-terminated string named text:
using namespace rapidxml;
+xml_document<> doc;    // character type defaults to char
+doc.parse<0>(text);    // 0 means default parse flags
+
doc object is now a root of DOM tree containing representation of the parsed XML. Because all RapidXml interface is contained inside namespace rapidxml, users must either bring contents of this namespace into scope, or fully qualify all the names. Class xml_document represents a root of the DOM hierarchy. By means of public inheritance, it is also an xml_node and a memory_pool. Template parameter of xml_document::parse() function is used to specify parsing flags, with which you can fine-tune behaviour of the parser. Note that flags must be a compile-time constant.

2.2 Accessing The DOM Tree

To access the DOM tree, use methods of xml_node and xml_attribute classes:
cout << "Name of my first node is: " << doc.first_node()->name() << "\n";
+xml_node<> *node = doc.first_node("foobar");
+cout << "Node foobar has value " << node->value() << "\n";
+for (xml_attribute<> *attr = node->first_attribute();
+     attr; attr = attr->next_attribute())
+{
+    cout << "Node foobar has attribute " << attr->name() << " ";
+    cout << "with value " << attr->value() << "\n";
+}
+

2.3 Modifying The DOM Tree

DOM tree produced by the parser is fully modifiable. Nodes and attributes can be added/removed, and their contents changed. The below example creates a HTML document, whose sole contents is a link to google.com website:
xml_document<> doc;
+xml_node<> *node = doc.allocate_node(node_element, "a", "Google");
+doc.append_node(node);
+xml_attribute<> *attr = doc.allocate_attribute("href", "google.com");
+node->append_attribute(attr);
+
One quirk is that nodes and attributes do not own the text of their names and values. This is because normally they only store pointers to the source text. So, when assigning a new name or value to the node, care must be taken to ensure proper lifetime of the string. The easiest way to achieve it is to allocate the string from the xml_document memory pool. In the above example this is not necessary, because we are only assigning character constants. But the code below uses memory_pool::allocate_string() function to allocate node name (which will have the same lifetime as the document), and assigns it to a new node:
xml_document<> doc;
+char *node_name = doc.allocate_string(name);        // Allocate string and copy name into it
+xml_node<> *node = doc.allocate_node(node_element, node_name);  // Set node name to node_name
+
Check Reference section for description of the entire interface.

2.4 Printing XML

You can print xml_document and xml_node objects into an XML string. Use print() function or operator <<, which are defined in rapidxml_print.hpp header.
using namespace rapidxml;
+xml_document<> doc;    // character type defaults to char
+// ... some code to fill the document
+
+// Print to stream using operator <<
+std::cout << doc;   
+
+// Print to stream using print function, specifying printing flags
+print(std::cout, doc, 0);   // 0 means default printing flags
+
+// Print to string using output iterator
+std::string s;
+print(std::back_inserter(s), doc, 0);
+
+// Print to memory buffer using output iterator
+char buffer[4096];                      // You are responsible for making the buffer large enough!
+char *end = print(buffer, doc, 0);      // end contains pointer to character after last printed character
+*end = 0;                               // Add string terminator after XML
+

3. Differences From Regular XML Parsers

RapidXml is an in-situ parser, which allows it to achieve very high parsing speed. In-situ means that parser does not make copies of strings. Instead, it places pointers to the source text in the DOM hierarchy.

3.1 Lifetime Of Source Text

In-situ parsing requires that source text lives at least as long as the document object. If source text is destroyed, names and values of nodes in DOM tree will become destroyed as well. Additionally, whitespace processing, character entity translation, and zero-termination of strings require that source text be modified during parsing (but see non-destructive mode). This makes the text useless for further processing once it was parsed by RapidXml.

+ In many cases however, these are not serious issues.

3.2 Ownership Of Strings

Nodes and attributes produced by RapidXml do not own their name and value strings. They merely hold the pointers to them. This means you have to be careful when setting these values manually, by using xml_base::name(const Ch *) or xml_base::value(const Ch *) functions. Care must be taken to ensure that lifetime of the string passed is at least as long as lifetime of the node/attribute. The easiest way to achieve it is to allocate the string from memory_pool owned by the document. Use memory_pool::allocate_string() function for this purpose.

3.3 Destructive Vs Non-Destructive Mode

By default, the parser modifies source text during the parsing process. This is required to achieve character entity translation, whitespace normalization, and zero-termination of strings.

+ In some cases this behaviour may be undesirable, for example if source text resides in read only memory, or is mapped to memory directly from file. By using appropriate parser flags (parse_non_destructive), source text modifications can be disabled. However, because RapidXml does in-situ parsing, it obviously has the following side-effects:

4. Performance

RapidXml achieves its speed through use of several techniques:
  • In-situ parsing. When building DOM tree, RapidXml does not make copies of string data, such as node names and values. Instead, it stores pointers to interior of the source text.
  • Use of template metaprogramming techniques. This allows it to move much of the work to compile time. Through magic of the templates, C++ compiler generates a separate copy of parsing code for any combination of parser flags you use. In each copy, all possible decisions are made at compile time and all unused code is omitted.
  • Extensive use of lookup tables for parsing.
  • Hand-tuned C++ with profiling done on several most popular CPUs.
This results in a very small and fast code: a parser which is custom tailored to exact needs with each invocation.

4.1 Comparison With Other Parsers

The table below compares speed of RapidXml to some other parsers, and to strlen() function executed on the same data. On a modern CPU (as of 2007), you can expect parsing throughput to be close to 1 GB/s. As a rule of thumb, parsing speed is about 50-100x faster than Xerces DOM, 30-60x faster than TinyXml, 3-12x faster than pugxml, and about 5% - 30% faster than pugixml, the fastest XML parser I know of.
  • The test file is a real-world, 50kB large, moderately dense XML file.
  • All timing is done by using RDTSC instruction present in Pentium-compatible CPUs.
  • No profile-guided optimizations are used.
  • All parsers are running in their fastest modes.
  • The results are given in CPU cycles per character, so frequency of CPUs is irrelevant.
  • The results are minimum values from a large number of runs, to minimize effects of operating system activity, task switching, interrupt handling etc.
  • A single parse of the test file takes about 1/10th of a millisecond, so with large number of runs there is a good chance of hitting at least one no-interrupt streak, and obtaining undisturbed results.
Platform
Compiler
strlen() RapidXml pugixml 0.3 pugxml TinyXml
Pentium 4
MSVC 8.0
2.5
5.4
7.0
61.7
298.8
Pentium 4
gcc 4.1.1
0.8
6.1
9.5
67.0
413.2
Core 2
MSVC 8.0
1.0
4.5
5.0
24.6
154.8
Core 2
gcc 4.1.1
0.6
4.6
5.4
28.3
229.3
Athlon XP
MSVC 8.0
3.1
7.7
8.0
25.5
182.6
Athlon XP
gcc 4.1.1
0.9
8.2
9.2
33.7
265.2
Pentium 3
MSVC 8.0
2.0
6.3
7.0
30.9
211.9
Pentium 3
gcc 4.1.1
1.0
6.7
8.9
35.3
316.0
(*) All results are in CPU cycles per character of source text

5. Reference

This section lists all classes, functions, constants etc. and describes them in detail.
class + template + rapidxml::memory_pool
+ constructor + memory_pool()
+ destructor + ~memory_pool()
function allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_string(const Ch *source=0, std::size_t size=0)
function clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0)
function clear()
function set_allocator(alloc_func *af, free_func *ff)

class rapidxml::parse_error
+ constructor + parse_error(const char *what, void *where)
function what() const
function where() const

class + template + rapidxml::xml_attribute
+ constructor + xml_attribute()
function document() const
function previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const

class + template + rapidxml::xml_base
+ constructor + xml_base()
function name() const
function name_size() const
function value() const
function value_size() const
function name(const Ch *name, std::size_t size)
function name(const Ch *name)
function value(const Ch *value, std::size_t size)
function value(const Ch *value)
function parent() const

class + template + rapidxml::xml_document
+ constructor + xml_document()
function parse(Ch *text)
function clear()

class + template + rapidxml::xml_node
+ constructor + xml_node(node_type type)
function type() const
function document() const
function first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function type(node_type type)
function prepend_node(xml_node< Ch > *child)
function append_node(xml_node< Ch > *child)
function insert_node(xml_node< Ch > *where, xml_node< Ch > *child)
function remove_first_node()
function remove_last_node()
function remove_node(xml_node< Ch > *where)
function remove_all_nodes()
function prepend_attribute(xml_attribute< Ch > *attribute)
function append_attribute(xml_attribute< Ch > *attribute)
function insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute)
function remove_first_attribute()
function remove_last_attribute()
function remove_attribute(xml_attribute< Ch > *where)
function remove_all_attributes()

namespace rapidxml
enum node_type
function parse_error_handler(const char *what, void *where)
function print(OutIt out, const xml_node< Ch > &node, int flags=0)
function print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0)
function operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node)
+ constant + parse_no_data_nodes
+ constant + parse_no_element_values
+ constant + parse_no_string_terminators
+ constant + parse_no_entity_translation
+ constant + parse_no_utf8
+ constant + parse_declaration_node
+ constant + parse_comment_nodes
+ constant + parse_doctype_node
+ constant + parse_pi_nodes
+ constant + parse_validate_closing_tags
+ constant + parse_trim_whitespace
+ constant + parse_normalize_whitespace
+ constant + parse_default
+ constant + parse_non_destructive
+ constant + parse_fastest
+ constant + parse_full
+ constant + print_no_indenting


class + template + rapidxml::memory_pool

+ + Defined in rapidxml.hpp
+ Base class for + xml_document

Description

This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. In most cases, you will not need to use this class directly. However, if you need to create nodes manually or modify names/values of nodes, you are encouraged to use memory_pool of relevant xml_document to allocate the memory. Not only is this faster than allocating them by using new operator, but also their lifetime will be tied to the lifetime of document, possibly simplyfing memory management.

+ Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. You can also call allocate_string() function to allocate strings. Such strings can then be used as names or values of nodes without worrying about their lifetime. Note that there is no free() function -- all allocations are freed at once when clear() function is called, or when the pool is destroyed.

+ It is also possible to create a standalone memory_pool, and use it to allocate nodes, whose lifetime will not be tied to any document.

+ Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. Until static memory is exhausted, no dynamic memory allocations are done. When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, by using global new[] and delete[] operators. This behaviour can be changed by setting custom allocation routines. Use set_allocator() function to set them.

+ Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. This value defaults to the size of pointer on target architecture.

+ To obtain absolutely top performance from the parser, it is important that all nodes are allocated from a single, contiguous block of memory. Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT to obtain best wasted memory to performance compromise. To do it, define their values before rapidxml.hpp file is included.

Parameters

Ch
Character type of created nodes.

+ constructor + memory_pool::memory_pool

Synopsis

memory_pool(); +

Description

Constructs empty pool with default allocator functions.

+ destructor + memory_pool::~memory_pool

Synopsis

~memory_pool(); +

Description

Destroys pool and frees all the memory. This causes memory occupied by nodes allocated by the pool to be freed. Nodes allocated from the pool are no longer valid.

function memory_pool::allocate_node

Synopsis

xml_node<Ch>* allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); +

Description

Allocates a new node from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

type
Type of node to create.
name
Name to assign to the node, or 0 to assign no name.
value
Value to assign to the node, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated node. This pointer will never be NULL.

function memory_pool::allocate_attribute

Synopsis

xml_attribute<Ch>* allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); +

Description

Allocates a new attribute from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

name
Name to assign to the attribute, or 0 to assign no name.
value
Value to assign to the attribute, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated attribute. This pointer will never be NULL.

function memory_pool::allocate_string

Synopsis

Ch* allocate_string(const Ch *source=0, std::size_t size=0); +

Description

Allocates a char array of given size from the pool, and optionally copies a given string to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

source
String to initialize the allocated memory with, or 0 to not initialize it.
size
Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.

Returns

Pointer to allocated char array. This pointer will never be NULL.

function memory_pool::clone_node

Synopsis

xml_node<Ch>* clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0); +

Description

Clones an xml_node and its hierarchy of child nodes and attributes. Nodes and attributes are allocated from this memory pool. Names and values are not cloned, they are shared between the clone and the source. Result node can be optionally specified as a second parameter, in which case its contents will be replaced with cloned source node. This is useful when you want to clone entire document.

Parameters

source
Node to clone.
result
Node to put results in, or 0 to automatically allocate result node

Returns

Pointer to cloned node. This pointer will never be NULL.

function memory_pool::clear

Synopsis

void clear(); +

Description

Clears the pool. This causes memory occupied by nodes allocated by the pool to be freed. Any nodes or strings allocated from the pool will no longer be valid.

function memory_pool::set_allocator

Synopsis

void set_allocator(alloc_func *af, free_func *ff); +

Description

Sets or resets the user-defined memory allocation functions for the pool. This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. Allocation function must not return invalid pointer on failure. It should either throw, stop the program, or use longjmp() function to pass control to other place of program. If it returns invalid pointer, results are undefined.

+ User defined allocation functions must have the following forms:

+void *allocate(std::size_t size);
+void free(void *pointer);

Parameters

af
Allocation function, or 0 to restore default function
ff
Free function, or 0 to restore default function

class rapidxml::parse_error

+ + Defined in rapidxml.hpp

Description

Parse error exception. This exception is thrown by the parser when an error occurs. Use what() function to get human-readable error message. Use where() function to get a pointer to position within source text where error was detected.

+ If throwing exceptions by the parser is undesirable, it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. This function must be defined by the user.

+ This class derives from std::exception class.

+ constructor + parse_error::parse_error

Synopsis

parse_error(const char *what, void *where); +

Description

Constructs parse error.

function parse_error::what

Synopsis

virtual const char* what() const; +

Description

Gets human readable description of error.

Returns

Pointer to null terminated description of the error.

function parse_error::where

Synopsis

Ch* where() const; +

Description

Gets pointer to character data where error happened. Ch should be the same as char type of xml_document that produced the error.

Returns

Pointer to location within the parsed string where error occured.

class + template + rapidxml::xml_attribute

+ + Defined in rapidxml.hpp
+ Inherits from + xml_base

Description

Class representing attribute node of XML document. Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). Note that after parse, both name and value of attribute will point to interior of source text used for parsing. Thus, this text must persist in memory for the lifetime of attribute.

Parameters

Ch
Character type to use.

+ constructor + xml_attribute::xml_attribute

Synopsis

xml_attribute(); +

Description

Constructs an empty attribute with the specified type. Consider using memory_pool of appropriate xml_document if allocating attributes manually.

function xml_attribute::document

Synopsis

xml_document<Ch>* document() const; +

Description

Gets document of which attribute is a child.

Returns

Pointer to document that contains this attribute, or 0 if there is no parent document.

function xml_attribute::previous_attribute

Synopsis

xml_attribute<Ch>* previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets previous attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_attribute::next_attribute

Synopsis

xml_attribute<Ch>* next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets next attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

class + template + rapidxml::xml_base

+ + Defined in rapidxml.hpp
+ Base class for + xml_attribute xml_node

Description

Base class for xml_node and xml_attribute implementing common functions: name(), name_size(), value(), value_size() and parent().

Parameters

Ch
Character type to use

+ constructor + xml_base::xml_base

Synopsis

xml_base(); +

function xml_base::name

Synopsis

Ch* name() const; +

Description

Gets name of the node. Interpretation of name depends on type of node. Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+ Use name_size() function to determine length of the name.

Returns

Name of node, or empty string if node has no name.

function xml_base::name_size

Synopsis

std::size_t name_size() const; +

Description

Gets size of node name, not including terminator character. This function works correctly irrespective of whether name is or is not zero terminated.

Returns

Size of node name, in characters.

function xml_base::value

Synopsis

Ch* value() const; +

Description

Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+ Use value_size() function to determine length of the value.

Returns

Value of node, or empty string if node has no value.

function xml_base::value_size

Synopsis

std::size_t value_size() const; +

Description

Gets size of node value, not including terminator character. This function works correctly irrespective of whether value is or is not zero terminated.

Returns

Size of node value, in characters.

function xml_base::name

Synopsis

void name(const Ch *name, std::size_t size); +

Description

Sets name of node to a non zero-terminated string. See Ownership Of Strings .

+ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

+ Size of name must be specified separately, because name does not have to be zero terminated. Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).

Parameters

name
Name of node to set. Does not have to be zero terminated.
size
Size of name, in characters. This does not include zero terminator, if one is present.

function xml_base::name

Synopsis

void name(const Ch *name); +

Description

Sets name of node to a zero-terminated string. See also Ownership Of Strings and xml_node::name(const Ch *, std::size_t).

Parameters

name
Name of node to set. Must be zero terminated.

function xml_base::value

Synopsis

void value(const Ch *value, std::size_t size); +

Description

Sets value of node to a non zero-terminated string. See Ownership Of Strings .

+ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

+ Size of value must be specified separately, because it does not have to be zero terminated. Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).

+ If an element has a child node of type node_data, it will take precedence over element value when printing. If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.

Parameters

value
value of node to set. Does not have to be zero terminated.
size
Size of value, in characters. This does not include zero terminator, if one is present.

function xml_base::value

Synopsis

void value(const Ch *value); +

Description

Sets value of node to a zero-terminated string. See also Ownership Of Strings and xml_node::value(const Ch *, std::size_t).

Parameters

value
Vame of node to set. Must be zero terminated.

function xml_base::parent

Synopsis

xml_node<Ch>* parent() const; +

Description

Gets node parent.

Returns

Pointer to parent node, or 0 if there is no parent.

class + template + rapidxml::xml_document

+ + Defined in rapidxml.hpp
+ Inherits from + xml_node memory_pool

Description

This class represents root of the DOM hierarchy. It is also an xml_node and a memory_pool through public inheritance. Use parse() function to build a DOM tree from a zero-terminated XML text string. parse() function allocates memory for nodes and attributes by using functions of xml_document, which are inherited from memory_pool. To access root node of the document, use the document itself, as if it was an xml_node.

Parameters

Ch
Character type to use.

+ constructor + xml_document::xml_document

Synopsis

xml_document(); +

Description

Constructs empty XML document.

function xml_document::parse

Synopsis

void parse(Ch *text); +

Description

Parses zero-terminated XML string according to given flags. Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. The string must persist for the lifetime of the document. In case of error, rapidxml::parse_error exception will be thrown.

+ If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. Make sure that data is zero-terminated.

+ Document can be parsed into multiple times. Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.

Parameters

text
XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.

function xml_document::clear

Synopsis

void clear(); +

Description

Clears the document by deleting all nodes and clearing the memory pool. All nodes owned by document pool are destroyed.

class + template + rapidxml::xml_node

+ + Defined in rapidxml.hpp
+ Inherits from + xml_base
+ Base class for + xml_document

Description

Class representing a node of XML document. Each node may have associated name and value strings, which are available through name() and value() functions. Interpretation of name and value depends on type of the node. Type of node can be determined by using type() function.

+ Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. Thus, this text must persist in the memory for the lifetime of node.

Parameters

Ch
Character type to use.

+ constructor + xml_node::xml_node

Synopsis

xml_node(node_type type); +

Description

Constructs an empty node with the specified type. Consider using memory_pool of appropriate document to allocate nodes manually.

Parameters

type
Type of node to construct.

function xml_node::type

Synopsis

node_type type() const; +

Description

Gets type of node.

Returns

Type of node.

function xml_node::document

Synopsis

xml_document<Ch>* document() const; +

Description

Gets document of which node is a child.

Returns

Pointer to document that contains this node, or 0 if there is no parent document.

function xml_node::first_node

Synopsis

xml_node<Ch>* first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets first child node, optionally matching node name.

Parameters

name
Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::last_node

Synopsis

xml_node<Ch>* last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets last child node, optionally matching node name. Behaviour is undefined if node has no children. Use first_node() to test if node has children.

Parameters

name
Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::previous_sibling

Synopsis

xml_node<Ch>* previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets previous sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::next_sibling

Synopsis

xml_node<Ch>* next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets next sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::first_attribute

Synopsis

xml_attribute<Ch>* first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets first attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::last_attribute

Synopsis

xml_attribute<Ch>* last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets last attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::type

Synopsis

void type(node_type type); +

Description

Sets type of node.

Parameters

type
Type of node to set.

function xml_node::prepend_node

Synopsis

void prepend_node(xml_node< Ch > *child); +

Description

Prepends a new child node. The prepended child becomes the first child, and all existing children are moved one position back.

Parameters

child
Node to prepend.

function xml_node::append_node

Synopsis

void append_node(xml_node< Ch > *child); +

Description

Appends a new child node. The appended child becomes the last child.

Parameters

child
Node to append.

function xml_node::insert_node

Synopsis

void insert_node(xml_node< Ch > *where, xml_node< Ch > *child); +

Description

Inserts a new child node at specified place inside the node. All children after and including the specified node are moved one position back.

Parameters

where
Place where to insert the child, or 0 to insert at the back.
child
Node to insert.

function xml_node::remove_first_node

Synopsis

void remove_first_node(); +

Description

Removes first child node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_last_node

Synopsis

void remove_last_node(); +

Description

Removes last child of the node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_node

Synopsis

void remove_node(xml_node< Ch > *where); +

Description

Removes specified child from the node.

function xml_node::remove_all_nodes

Synopsis

void remove_all_nodes(); +

Description

Removes all child nodes (but not attributes).

function xml_node::prepend_attribute

Synopsis

void prepend_attribute(xml_attribute< Ch > *attribute); +

Description

Prepends a new attribute to the node.

Parameters

attribute
Attribute to prepend.

function xml_node::append_attribute

Synopsis

void append_attribute(xml_attribute< Ch > *attribute); +

Description

Appends a new attribute to the node.

Parameters

attribute
Attribute to append.

function xml_node::insert_attribute

Synopsis

void insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute); +

Description

Inserts a new attribute at specified place inside the node. All attributes after and including the specified attribute are moved one position back.

Parameters

where
Place where to insert the attribute, or 0 to insert at the back.
attribute
Attribute to insert.

function xml_node::remove_first_attribute

Synopsis

void remove_first_attribute(); +

Description

Removes first attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_last_attribute

Synopsis

void remove_last_attribute(); +

Description

Removes last attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_attribute

Synopsis

void remove_attribute(xml_attribute< Ch > *where); +

Description

Removes specified attribute from node.

Parameters

where
Pointer to attribute to be removed.

function xml_node::remove_all_attributes

Synopsis

void remove_all_attributes(); +

Description

Removes all attributes of node.

enum node_type

Description

Enumeration listing all node types produced by the parser. Use xml_node::type() function to query node type.

Values

node_document
A document node. Name and value are empty.
node_element
An element node. Name contains element name. Value contains text of first data node.
node_data
A data node. Name is empty. Value contains data text.
node_cdata
A CDATA node. Name is empty. Value contains data text.
node_comment
A comment node. Name is empty. Value contains comment text.
node_declaration
A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
node_doctype
A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
node_pi
A PI node. Name contains target. Value contains instructions.

function parse_error_handler

Synopsis

void rapidxml::parse_error_handler(const char *what, void *where); +

Description

When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function is called to notify user about the error. It must be defined by the user.

+ This function cannot return. If it does, the results are undefined.

+ A very simple definition might look like that: + void rapidxml::parse_error_handler(const char *what, void *where) + { + std::cout << "Parse error: " << what << "\n"; + std::abort(); + } +

Parameters

what
Human readable description of the error.
where
Pointer to character data where error was detected.

function print

Synopsis

OutIt rapidxml::print(OutIt out, const xml_node< Ch > &node, int flags=0); +

Description

Prints XML to given output iterator.

Parameters

out
Output iterator to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output iterator pointing to position immediately after last character of printed text.

function print

Synopsis

std::basic_ostream<Ch>& rapidxml::print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0); +

Description

Prints XML to given output stream.

Parameters

out
Output stream to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output stream.

function operator<<

Synopsis

std::basic_ostream<Ch>& rapidxml::operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node); +

Description

Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.

Parameters

out
Output stream to print to.
node
Node to be printed.

Returns

Output stream.

+ constant + parse_no_data_nodes

Synopsis

const int parse_no_data_nodes + = 0x1; +

Description

Parse flag instructing the parser to not create data nodes. Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_element_values

Synopsis

const int parse_no_element_values + = 0x2; +

Description

Parse flag instructing the parser to not use text of first data node as a value of parent element. Can be combined with other flags by use of | operator. Note that child data nodes of element node take precendence over its value when printing. That is, if element has one or more child data nodes and a value, the value will be ignored. Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.

+ See xml_document::parse() function.

+ constant + parse_no_string_terminators

Synopsis

const int parse_no_string_terminators + = 0x4; +

Description

Parse flag instructing the parser to not place zero terminators after strings in the source text. By default zero terminators are placed, modifying source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_entity_translation

Synopsis

const int parse_no_entity_translation + = 0x8; +

Description

Parse flag instructing the parser to not translate entities in the source text. By default entities are translated, modifying source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_utf8

Synopsis

const int parse_no_utf8 + = 0x10; +

Description

Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. By default, UTF-8 handling is enabled. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_declaration_node

Synopsis

const int parse_declaration_node + = 0x20; +

Description

Parse flag instructing the parser to create XML declaration node. By default, declaration node is not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_comment_nodes

Synopsis

const int parse_comment_nodes + = 0x40; +

Description

Parse flag instructing the parser to create comments nodes. By default, comment nodes are not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_doctype_node

Synopsis

const int parse_doctype_node + = 0x80; +

Description

Parse flag instructing the parser to create DOCTYPE node. By default, doctype node is not created. Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_pi_nodes

Synopsis

const int parse_pi_nodes + = 0x100; +

Description

Parse flag instructing the parser to create PI nodes. By default, PI nodes are not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_validate_closing_tags

Synopsis

const int parse_validate_closing_tags + = 0x200; +

Description

Parse flag instructing the parser to validate closing tag names. If not set, name inside closing tag is irrelevant to the parser. By default, closing tags are not validated. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_trim_whitespace

Synopsis

const int parse_trim_whitespace + = 0x400; +

Description

Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. By default, whitespace is not trimmed. This flag does not cause the parser to modify source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_normalize_whitespace

Synopsis

const int parse_normalize_whitespace + = 0x800; +

Description

Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. By default, whitespace is not normalized. If this flag is specified, source text will be modified. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_default

Synopsis

const int parse_default + = 0; +

Description

Parse flags which represent default behaviour of the parser. This is always equal to 0, so that all other flags can be simply ored together. Normally there is no need to inconveniently disable flags by anding with their negated (~) values. This also means that meaning of each flag is a negation of the default setting. For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, and using the flag will disable it.

+ See xml_document::parse() function.

+ constant + parse_non_destructive

Synopsis

const int parse_non_destructive + = parse_no_string_terminators | parse_no_entity_translation; +

Description

A combination of parse flags that forbids any modifications of the source text. This also results in faster parsing. However, note that the following will occur:
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • entities will not be translated
  • whitespace will not be normalized
+See xml_document::parse() function.

+ constant + parse_fastest

Synopsis

const int parse_fastest + = parse_non_destructive | parse_no_data_nodes; +

Description

A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.

+ See xml_document::parse() function.

+ constant + parse_full

Synopsis

const int parse_full + = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; +

Description

A combination of parse flags resulting in largest amount of data being extracted. This usually results in slowest parsing.

+ See xml_document::parse() function.

+ constant + print_no_indenting

Synopsis

const int print_no_indenting + = 0x1; +

Description

Printer flag instructing the printer to suppress indenting of XML. See print() function.

\ No newline at end of file diff --git a/tpl/cereal/include/cereal/external/rapidxml/rapidxml.hpp b/tpl/cereal/include/cereal/external/rapidxml/rapidxml.hpp new file mode 100644 index 0000000..61013a9 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/rapidxml.hpp @@ -0,0 +1,2624 @@ +#ifndef CEREAL_RAPIDXML_HPP_INCLUDED +#define CEREAL_RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(CEREAL_RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant + #pragma warning(disable:4100) // unreferenced formal parameter +#endif + +/////////////////////////////////////////////////////////////////////////// +// CEREAL_RAPIDXML_PARSE_ERROR + +#if defined(CEREAL_RAPIDXML_NO_EXCEPTIONS) + +#define CEREAL_RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace cereal { +namespace rapidxml +{ + //! When exceptions are disabled by defining CEREAL_RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!

+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} +} // end namespace cereal + +#else + +#include // For std::exception + +#define CEREAL_RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace cereal { +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining CEREAL_RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what_, void *where_) + : m_what(what_) + , m_where(where_) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} +} // end namespace cereal + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef CEREAL_RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define CEREAL_RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define CEREAL_RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef CEREAL_RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define CEREAL_RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define CEREAL_RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace cereal { +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + + template + inline bool preserve_space(xml_node* node) + { + const Ch preserve_value[] = { Ch('p'), Ch('r'), Ch('e'), Ch('s'), Ch('e'), Ch('r'), Ch('v'), Ch('e') }; + const xml_attribute* space = node->first_attribute("xml:space"); + return space && internal::compare(space->value(), space->value_size(), preserve_value, sizeof(preserve_value) / sizeof(Ch), true); + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains CEREAL_RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at CEREAL_RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak CEREAL_RAPIDXML_STATIC_POOL_SIZE, CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE and CEREAL_RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining CEREAL_RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining CEREAL_RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining CEREAL_RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((CEREAL_RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (CEREAL_RAPIDXML_ALIGNMENT - 1))) & (CEREAL_RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef CEREAL_RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + CEREAL_RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = CEREAL_RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * CEREAL_RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[CEREAL_RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name_, std::size_t size) + { + m_name = const_cast(name_); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name_) + { + this->name(name_, internal::measure(name_)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value_, std::size_t size) + { + m_value = const_cast(value_); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value_) + { + this->value(value_, internal::measure(value_)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name_ = 0, std::size_t name_size_ = 0, bool case_sensitive = true) const + { + if (name_) + { + if (name_size_ == 0) + name_size_ = internal::measure(name_); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name_, name_size_, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type_) + : m_type(type_) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name_ = 0, std::size_t name_size_ = 0, bool case_sensitive = true) const + { + if (name_) + { + if (name_size_ == 0) + name_size_ = internal::measure(name_); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name_, name_size_, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name_ = 0, std::size_t name_size_ = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name_) + { + if (name_size_ == 0) + name_size_ = internal::measure(name_); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name_, name_size_, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name_ = 0, std::size_t name_size_ = 0, bool case_sensitive = true) const + { + if (name_) + { + if (name_size_ == 0) + name_size_ = internal::measure(name_); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name_, name_size_, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type_) + { + m_type = type_; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + CEREAL_RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + CEREAL_RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text, bool preserve_space) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + CEREAL_RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if ((Flags & parse_normalize_whitespace) && !preserve_space) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + CEREAL_RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value_ = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value_, text - value_); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value_ = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value_, text - value_); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name_ = text; + skip(text); + if (text == name_) + CEREAL_RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name_, text - name_); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value_ = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value_, text - value_); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + const bool preserve_space = internal::preserve_space(node); + + // Skip until end of data + Ch *value_ = text, *end; + if ((Flags & parse_normalize_whitespace) && !preserve_space) + end = skip_and_expand_character_refs(text, false); + else + end = skip_and_expand_character_refs(text, preserve_space); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if ((Flags & parse_trim_whitespace) && !preserve_space) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value_, end - value_); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value_, end - value_); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value_ = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value_, text - value_); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name_ = text; + skip(text); + if (text == name_) + CEREAL_RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name_, text - name_); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + CEREAL_RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + CEREAL_RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + Ch *contents_end = 0; + if (internal::preserve_space(node)) + { + contents_end = text; + } + + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + CEREAL_RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + CEREAL_RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + + if (contents_end && contents_end != contents_start) + { + node->value(contents_start, contents_end - contents_start); + node->value()[node->value_size()] = Ch('\0'); + } + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + CEREAL_RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name_ = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name_) + CEREAL_RAPIDXML_PARSE_ERROR("expected attribute name", name_); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name_, text - name_); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + CEREAL_RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + CEREAL_RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value_ = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text, false); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text, false); + + // Set attribute value + attribute->value(value_, end - value_); + + // Make sure that end quote is present + if (*text != quote) + CEREAL_RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 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, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} +} // end namespace cereal + +// Undefine internal macros +#undef CEREAL_RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/tpl/cereal/include/cereal/external/rapidxml/rapidxml_iterators.hpp b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_iterators.hpp new file mode 100644 index 0000000..736d46c --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_iterators.hpp @@ -0,0 +1,175 @@ +#ifndef CEREAL_RAPIDXML_ITERATORS_HPP_INCLUDED +#define CEREAL_RAPIDXML_ITERATORS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ + +#include "rapidxml.hpp" + +namespace cereal { +namespace rapidxml +{ + + //! Iterator of child nodes of xml_node + template + class node_iterator + { + + public: + + typedef typename xml_node value_type; + typedef typename xml_node &reference; + typedef typename xml_node *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + node_iterator() + : m_node(0) + { + } + + node_iterator(xml_node *node) + : m_node(node->first_node()) + { + } + + reference operator *() const + { + assert(m_node); + return *m_node; + } + + pointer operator->() const + { + assert(m_node); + return m_node; + } + + node_iterator& operator++() + { + assert(m_node); + m_node = m_node->next_sibling(); + return *this; + } + + node_iterator operator++(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + node_iterator& operator--() + { + assert(m_node && m_node->previous_sibling()); + m_node = m_node->previous_sibling(); + return *this; + } + + node_iterator operator--(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const node_iterator &rhs) + { + return m_node == rhs.m_node; + } + + bool operator !=(const node_iterator &rhs) + { + return m_node != rhs.m_node; + } + + private: + + xml_node *m_node; + + }; + + //! Iterator of child attributes of xml_node + template + class attribute_iterator + { + + public: + + typedef typename xml_attribute value_type; + typedef typename xml_attribute &reference; + typedef typename xml_attribute *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + attribute_iterator() + : m_attribute(0) + { + } + + attribute_iterator(xml_node *node) + : m_attribute(node->first_attribute()) + { + } + + reference operator *() const + { + assert(m_attribute); + return *m_attribute; + } + + pointer operator->() const + { + assert(m_attribute); + return m_attribute; + } + + attribute_iterator& operator++() + { + assert(m_attribute); + m_attribute = m_attribute->next_attribute(); + return *this; + } + + attribute_iterator operator++(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + attribute_iterator& operator--() + { + assert(m_attribute && m_attribute->previous_attribute()); + m_attribute = m_attribute->previous_attribute(); + return *this; + } + + attribute_iterator operator--(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const attribute_iterator &rhs) + { + return m_attribute == rhs.m_attribute; + } + + bool operator !=(const attribute_iterator &rhs) + { + return m_attribute != rhs.m_attribute; + } + + private: + + xml_attribute *m_attribute; + + }; + +} +} // namespace cereal + +#endif diff --git a/tpl/cereal/include/cereal/external/rapidxml/rapidxml_print.hpp b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_print.hpp new file mode 100644 index 0000000..ce10dbb --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_print.hpp @@ -0,0 +1,426 @@ +#ifndef CEREAL_RAPIDXML_PRINT_HPP_INCLUDED +#define CEREAL_RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef CEREAL_RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace cereal { +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent); + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int /*flags*/) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef CEREAL_RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} +} // namespace cereal + +#endif diff --git a/tpl/cereal/include/cereal/external/rapidxml/rapidxml_utils.hpp b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_utils.hpp new file mode 100644 index 0000000..e11ecf3 --- /dev/null +++ b/tpl/cereal/include/cereal/external/rapidxml/rapidxml_utils.hpp @@ -0,0 +1,123 @@ +#ifndef CEREAL_RAPIDXML_UTILS_HPP_INCLUDED +#define CEREAL_RAPIDXML_UTILS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "rapidxml.hpp" +#include +#include +#include +#include + +namespace cereal { +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} +} // namespace cereal + +#endif diff --git a/tpl/cereal/include/cereal/macros.hpp b/tpl/cereal/include/cereal/macros.hpp new file mode 100644 index 0000000..cfdc14c --- /dev/null +++ b/tpl/cereal/include/cereal/macros.hpp @@ -0,0 +1,135 @@ +/*! \file macros.hpp + \brief Preprocessor macros that can customise the cereal library + + By default, cereal looks for serialization functions with very + specific names, that is: serialize, load, save, load_minimal, + or save_minimal. + + This file allows an advanced user to change these names to conform + to some other style or preference. This is implemented using + preprocessor macros. + + As a result of this, in internal cereal code you will see macros + used for these function names. In user code, you should name + the functions like you normally would and not use the macros + to improve readability. + \ingroup utility */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CEREAL_MACROS_HPP_ +#define CEREAL_MACROS_HPP_ + +#ifndef CEREAL_THREAD_SAFE +//! Whether cereal should be compiled for a threaded environment +/*! This macro causes cereal to use mutexes to control access to + global internal state in a thread safe manner. + + Note that even with this enabled you must still ensure that + archives are accessed by only one thread at a time; it is safe + to use multiple archives in paralel, but not to access one archive + from many places simultaneously. */ +#define CEREAL_THREAD_SAFE 0 +#endif // CEREAL_THREAD_SAFE + +#ifndef CEREAL_SIZE_TYPE +//! Determines the data type used for size_type +/*! cereal uses size_type to ensure that the serialized size of + dynamic containers is compatible across different architectures + (e.g. 32 vs 64 bit), which may use different underlying types for + std::size_t. + + More information can be found in cereal/details/helpers.hpp. + + If you choose to modify this type, ensure that you use a fixed + size type (e.g. uint32_t). */ +#define CEREAL_SIZE_TYPE uint64_t +#endif // CEREAL_SIZE_TYPE + +// ###################################################################### +#ifndef CEREAL_SERIALIZE_FUNCTION_NAME +//! The serialization/deserialization function name to search for. +/*! You can define @c CEREAL_SERIALIZE_FUNCTION_NAME to be different assuming + you do so before this file is included. */ +#define CEREAL_SERIALIZE_FUNCTION_NAME serialize +#endif // CEREAL_SERIALIZE_FUNCTION_NAME + +#ifndef CEREAL_LOAD_FUNCTION_NAME +//! The deserialization (load) function name to search for. +/*! You can define @c CEREAL_LOAD_FUNCTION_NAME to be different assuming you do so + before this file is included. */ +#define CEREAL_LOAD_FUNCTION_NAME load +#endif // CEREAL_LOAD_FUNCTION_NAME + +#ifndef CEREAL_SAVE_FUNCTION_NAME +//! The serialization (save) function name to search for. +/*! You can define @c CEREAL_SAVE_FUNCTION_NAME to be different assuming you do so + before this file is included. */ +#define CEREAL_SAVE_FUNCTION_NAME save +#endif // CEREAL_SAVE_FUNCTION_NAME + +#ifndef CEREAL_LOAD_MINIMAL_FUNCTION_NAME +//! The deserialization (load_minimal) function name to search for. +/*! You can define @c CEREAL_LOAD_MINIMAL_FUNCTION_NAME to be different assuming you do so + before this file is included. */ +#define CEREAL_LOAD_MINIMAL_FUNCTION_NAME load_minimal +#endif // CEREAL_LOAD_MINIMAL_FUNCTION_NAME + +#ifndef CEREAL_SAVE_MINIMAL_FUNCTION_NAME +//! The serialization (save_minimal) function name to search for. +/*! You can define @c CEREAL_SAVE_MINIMAL_FUNCTION_NAME to be different assuming you do so + before this file is included. */ +#define CEREAL_SAVE_MINIMAL_FUNCTION_NAME save_minimal +#endif // CEREAL_SAVE_MINIMAL_FUNCTION_NAME + +// ###################################################################### +//! Defines the CEREAL_NOEXCEPT macro to use instead of noexcept +/*! If a compiler we support does not support noexcept, this macro + will detect this and define CEREAL_NOEXCEPT as a no-op + @internal */ +#if !defined(CEREAL_HAS_NOEXCEPT) + #if defined(__clang__) + #if __has_feature(cxx_noexcept) + #define CEREAL_HAS_NOEXCEPT + #endif + #else // NOT clang + #if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 || \ + defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 + #define CEREAL_HAS_NOEXCEPT + #endif // end GCC/MSVC check + #endif // end NOT clang block + + #ifndef CEREAL_NOEXCEPT + #ifdef CEREAL_HAS_NOEXCEPT + #define CEREAL_NOEXCEPT noexcept + #else + #define CEREAL_NOEXCEPT + #endif // end CEREAL_HAS_NOEXCEPT + #endif // end !defined(CEREAL_HAS_NOEXCEPT) +#endif // ifndef CEREAL_NOEXCEPT + +#endif // CEREAL_MACROS_HPP_ diff --git a/tpl/cereal/include/cereal/types/array.hpp b/tpl/cereal/include/cereal/types/array.hpp new file mode 100644 index 0000000..f5d510a --- /dev/null +++ b/tpl/cereal/include/cereal/types/array.hpp @@ -0,0 +1,79 @@ +/*! \file array.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_ARRAY_HPP_ +#define CEREAL_TYPES_ARRAY_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Saving for std::array primitive types + //! using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::array const & array ) + { + ar( binary_data( array.data(), sizeof(array) ) ); + } + + //! Loading for std::array primitive types + //! using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::array & array ) + { + ar( binary_data( array.data(), sizeof(array) ) ); + } + + //! Saving for std::array all other types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::array const & array ) + { + for( auto const & i : array ) + ar( i ); + } + + //! Loading for std::array all other types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::array & array ) + { + for( auto & i : array ) + ar( i ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_ARRAY_HPP_ diff --git a/tpl/cereal/include/cereal/types/base_class.hpp b/tpl/cereal/include/cereal/types/base_class.hpp new file mode 100644 index 0000000..37b551e --- /dev/null +++ b/tpl/cereal/include/cereal/types/base_class.hpp @@ -0,0 +1,201 @@ +/*! \file base_class.hpp + \brief Support for base classes (virtual and non-virtual) + \ingroup OtherTypes */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_BASE_CLASS_HPP_ +#define CEREAL_TYPES_BASE_CLASS_HPP_ + +#include "cereal/details/traits.hpp" +#include "cereal/details/polymorphic_impl_fwd.hpp" + +namespace cereal +{ + namespace base_class_detail + { + //! Used to register polymorphic relations and avoid the need to include + //! polymorphic.hpp when no polymorphism is used + /*! @internal */ + template ::value> + struct RegisterPolymorphicBaseClass + { + static void bind() + { } + }; + + //! Polymorphic version + /*! @internal */ + template + struct RegisterPolymorphicBaseClass + { + static void bind() + { detail::RegisterPolymorphicCaster::bind(); } + }; + } + + //! Casts a derived class to its non-virtual base class in a way that safely supports abstract classes + /*! This should be used in cases when a derived type needs to serialize its base type. This is better than directly + using static_cast, as it allows for serialization of pure virtual (abstract) base classes. + + This also automatically registers polymorphic relation between the base and derived class, assuming they + are indeed polymorphic. Note this is not the same as polymorphic type registration. For more information + see the documentation on polymorphism. + + \sa virtual_base_class + + @code{.cpp} + struct MyBase + { + int x; + + virtual void foo() = 0; + + template + void serialize( Archive & ar ) + { + ar( x ); + } + }; + + struct MyDerived : public MyBase //<-- Note non-virtual inheritance + { + int y; + + virtual void foo() {}; + + template + void serialize( Archive & ar ) + { + ar( cereal::base_class(this) ); + ar( y ); + } + }; + @endcode */ + template + struct base_class : private traits::detail::BaseCastBase + { + template + base_class(Derived const * derived) : + base_ptr(const_cast(static_cast(derived))) + { + static_assert( std::is_base_of::value, "Can only use base_class on a valid base class" ); + base_class_detail::RegisterPolymorphicBaseClass::bind(); + } + + Base * base_ptr; + }; + + //! Casts a derived class to its virtual base class in a way that allows cereal to track inheritance + /*! This should be used in cases when a derived type features virtual inheritance from some + base type. This allows cereal to track the inheritance and to avoid making duplicate copies + during serialization. + + It is safe to use virtual_base_class in all circumstances for serializing base classes, even in cases + where virtual inheritance does not take place, though it may be slightly faster to utilize + cereal::base_class<> if you do not need to worry about virtual inheritance. + + This also automatically registers polymorphic relation between the base and derived class, assuming they + are indeed polymorphic. Note this is not the same as polymorphic type registration. For more information + see the documentation on polymorphism. + + \sa base_class + + @code{.cpp} + struct MyBase + { + int x; + + template + void serialize( Archive & ar ) + { + ar( x ); + } + }; + + struct MyLeft : virtual MyBase //<-- Note the virtual inheritance + { + int y; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); + ar( y ); + } + }; + + struct MyRight : virtual MyBase + { + int z; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_clas( this ) ); + ar( z ); + } + }; + + // diamond virtual inheritance; contains one copy of each base class + struct MyDerived : virtual MyLeft, virtual MyRight + { + int a; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); // safely serialize data members in MyLeft + ar( cereal::virtual_base_class( this ) ); // safely serialize data members in MyRight + ar( a ); + + // Because we used virtual_base_class, cereal will ensure that only one instance of MyBase is + // serialized as we traverse the inheritance heirarchy. This means that there will be one copy + // each of the variables x, y, z, and a + + // If we had chosen to use static_cast<> instead, cereal would perform no tracking and + // assume that every base class should be serialized (in this case leading to a duplicate + // serialization of MyBase due to diamond inheritance + }; + } + @endcode */ + template + struct virtual_base_class : private traits::detail::BaseCastBase + { + template + virtual_base_class(Derived const * derived) : + base_ptr(const_cast(static_cast(derived))) + { + static_assert( std::is_base_of::value, "Can only use virtual_base_class on a valid base class" ); + base_class_detail::RegisterPolymorphicBaseClass::bind(); + } + + Base * base_ptr; + }; + +} // namespace cereal + +#endif // CEREAL_TYPES_BASE_CLASS_HPP_ diff --git a/tpl/cereal/include/cereal/types/bitset.hpp b/tpl/cereal/include/cereal/types/bitset.hpp new file mode 100644 index 0000000..12d3a82 --- /dev/null +++ b/tpl/cereal/include/cereal/types/bitset.hpp @@ -0,0 +1,176 @@ +/*! \file bitset.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_BITSET_HPP_ +#define CEREAL_TYPES_BITSET_HPP_ + +#include "cereal/cereal.hpp" +#include "cereal/types/string.hpp" +#include + +namespace cereal +{ + namespace bitset_detail + { + //! The type the bitset is encoded with + /*! @internal */ + enum class type : uint8_t + { + ulong, + ullong, + string, + bits + }; + } + + //! Serializing (save) for std::bitset when BinaryData optimization supported + template , Archive>::value> + = traits::sfinae> inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::bitset const & bits ) + { + ar( CEREAL_NVP_("type", bitset_detail::type::bits) ); + + // Serialize 8 bit chunks + std::uint8_t chunk = 0; + std::uint8_t mask = 0x80; + + // Set each chunk using a rotating mask for the current bit + for( std::size_t i = 0; i < N; ++i ) + { + if( bits[i] ) + chunk |= mask; + + mask >>= 1; + + // output current chunk when mask is empty (8 bits) + if( mask == 0 ) + { + ar( chunk ); + chunk = 0; + mask = 0x80; + } + } + + // serialize remainder, if it exists + if( mask != 0x80 ) + ar( chunk ); + } + + //! Serializing (save) for std::bitset when BinaryData is not supported + template , Archive>::value> + = traits::sfinae> inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::bitset const & bits ) + { + try + { + auto const b = bits.to_ulong(); + ar( CEREAL_NVP_("type", bitset_detail::type::ulong) ); + ar( CEREAL_NVP_("data", b) ); + } + catch( std::overflow_error const & ) + { + try + { + auto const b = bits.to_ullong(); + ar( CEREAL_NVP_("type", bitset_detail::type::ullong) ); + ar( CEREAL_NVP_("data", b) ); + } + catch( std::overflow_error const & ) + { + ar( CEREAL_NVP_("type", bitset_detail::type::string) ); + ar( CEREAL_NVP_("data", bits.to_string()) ); + } + } + } + + //! Serializing (load) for std::bitset + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::bitset & bits ) + { + bitset_detail::type t; + ar( CEREAL_NVP_("type", t) ); + + switch( t ) + { + case bitset_detail::type::ulong: + { + unsigned long b; + ar( CEREAL_NVP_("data", b) ); + bits = std::bitset( b ); + break; + } + case bitset_detail::type::ullong: + { + unsigned long long b; + ar( CEREAL_NVP_("data", b) ); + bits = std::bitset( b ); + break; + } + case bitset_detail::type::string: + { + std::string b; + ar( CEREAL_NVP_("data", b) ); + bits = std::bitset( b ); + break; + } + case bitset_detail::type::bits: + { + // Normally we would use BinaryData to route this at compile time, + // but doing this at runtime doesn't break any old serialization + std::uint8_t chunk = 0; + std::uint8_t mask = 0; + + bits.reset(); + + // Load one chunk at a time, rotating through the chunk + // to set bits in the bitset + for( std::size_t i = 0; i < N; ++i ) + { + if( mask == 0 ) + { + ar( chunk ); + mask = 0x80; + } + + if( chunk & mask ) + bits[i] = 1; + + mask >>= 1; + } + break; + } + default: + throw Exception("Invalid bitset data representation"); + } + } +} // namespace cereal + +#endif // CEREAL_TYPES_BITSET_HPP_ diff --git a/tpl/cereal/include/cereal/types/boost_variant.hpp b/tpl/cereal/include/cereal/types/boost_variant.hpp new file mode 100644 index 0000000..e1863c1 --- /dev/null +++ b/tpl/cereal/include/cereal/types/boost_variant.hpp @@ -0,0 +1,106 @@ +/*! \file boost_variant.hpp + \brief Support for boost::variant + \ingroup OtherTypes */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_BOOST_VARIANT_HPP_ +#define CEREAL_TYPES_BOOST_VARIANT_HPP_ + +#include "cereal/cereal.hpp" +#include +#include + +namespace cereal +{ + namespace variant_detail + { + //! @internal + template + struct variant_save_visitor : boost::static_visitor<> + { + variant_save_visitor(Archive & ar_) : ar(ar_) {} + + template + void operator()(T const & value) const + { + ar( CEREAL_NVP_("data", value) ); + } + + Archive & ar; + }; + + //! @internal + template + typename std::enable_if::value, void>::type + load_variant(Archive & /*ar*/, int /*target*/, Variant & /*variant*/) + { + throw ::cereal::Exception("Error traversing variant during load"); + } + + //! @internal + template + typename std::enable_if::value, void>::type + load_variant(Archive & ar, int target, Variant & variant) + { + if(N == target) + { + H value; + ar( CEREAL_NVP_("data", value) ); + variant = value; + } + else + load_variant(ar, target, variant); + } + + } // namespace variant_detail + + //! Saving for boost::variant + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, boost::variant const & variant ) + { + int32_t which = variant.which(); + ar( CEREAL_NVP_("which", which) ); + variant_detail::variant_save_visitor visitor(ar); + variant.apply_visitor(visitor); + } + + //! Loading for boost::variant + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, boost::variant & variant ) + { + typedef typename boost::variant::types types; + + int32_t which; + ar( CEREAL_NVP_("which", which) ); + if(which >= boost::mpl::size::value) + throw Exception("Invalid 'which' selector when deserializing boost::variant"); + + variant_detail::load_variant<0, boost::variant, VariantType1, VariantTypes...>(ar, which, variant); + } +} // namespace cereal + +#endif // CEREAL_TYPES_BOOST_VARIANT_HPP_ diff --git a/tpl/cereal/include/cereal/types/chrono.hpp b/tpl/cereal/include/cereal/types/chrono.hpp new file mode 100644 index 0000000..12ee4cb --- /dev/null +++ b/tpl/cereal/include/cereal/types/chrono.hpp @@ -0,0 +1,72 @@ +/*! \file chrono.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_CHRONO_HPP_ +#define CEREAL_TYPES_CHRONO_HPP_ + +#include + +namespace cereal +{ + //! Saving std::chrono::duration + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::chrono::duration const & dur ) + { + ar( CEREAL_NVP_("count", dur.count()) ); + } + + //! Loading std::chrono::duration + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::chrono::duration & dur ) + { + R count; + ar( CEREAL_NVP_("count", count) ); + + dur = std::chrono::duration{count}; + } + + //! Saving std::chrono::time_point + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::chrono::time_point const & dur ) + { + ar( CEREAL_NVP_("time_since_epoch", dur.time_since_epoch()) ); + } + + //! Loading std::chrono::time_point + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::chrono::time_point & dur ) + { + D elapsed; + ar( CEREAL_NVP_("time_since_epoch", elapsed) ); + + dur = std::chrono::time_point{elapsed}; + } +} // namespace cereal + +#endif // CEREAL_TYPES_CHRONO_HPP_ diff --git a/tpl/cereal/include/cereal/types/common.hpp b/tpl/cereal/include/cereal/types/common.hpp new file mode 100644 index 0000000..b239daa --- /dev/null +++ b/tpl/cereal/include/cereal/types/common.hpp @@ -0,0 +1,129 @@ +/*! \file common.hpp + \brief Support common types - always included automatically + \ingroup OtherTypes */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_COMMON_HPP_ +#define CEREAL_TYPES_COMMON_HPP_ + +#include "cereal/cereal.hpp" + +namespace cereal +{ + namespace common_detail + { + //! Serialization for arrays if BinaryData is supported and we are arithmetic + /*! @internal */ + template inline + void serializeArray( Archive & ar, T & array, std::true_type /* binary_supported */ ) + { + ar( binary_data( array, sizeof(array) ) ); + } + + //! Serialization for arrays if BinaryData is not supported or we are not arithmetic + /*! @internal */ + template inline + void serializeArray( Archive & ar, T & array, std::false_type /* binary_supported */ ) + { + for( auto & i : array ) + ar( i ); + } + + namespace + { + //! Gets the underlying type of an enum + /*! @internal */ + template + struct enum_underlying_type : std::false_type {}; + + //! Gets the underlying type of an enum + /*! Specialization for when we actually have an enum + @internal */ + template + struct enum_underlying_type { using type = typename std::underlying_type::type; }; + } // anon namespace + + //! Checks if a type is an enum + /*! This is needed over simply calling std::is_enum because the type + traits checking at compile time will attempt to call something like + load_minimal with a special NoConvertRef struct that wraps up the true type. + + This will strip away any of that and also expose the true underlying type. + @internal */ + template + class is_enum + { + private: + using DecayedT = typename std::decay::type; + using StrippedT = typename ::cereal::traits::strip_minimal::type; + + public: + static const bool value = std::is_enum::value; + using type = StrippedT; + using base_type = typename enum_underlying_type::type; + }; + } + + //! Saving for enum types + template inline + typename std::enable_if::value, + typename common_detail::is_enum::base_type>::type + CEREAL_SAVE_MINIMAL_FUNCTION_NAME( Archive const &, T const & t ) + { + return static_cast::base_type>(t); + } + + //! Loading for enum types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_MINIMAL_FUNCTION_NAME( Archive const &, T && t, + typename common_detail::is_enum::base_type const & value ) + { + t = reinterpret_cast::type const &>( value ); + } + + //! Serialization for raw pointers + /*! This exists only to throw a static_assert to let users know we don't support raw pointers. */ + template inline + void CEREAL_SERIALIZE_FUNCTION_NAME( Archive &, T * & ) + { + static_assert(cereal::traits::detail::delay_static_assert::value, + "Cereal does not support serializing raw pointers - please use a smart pointer"); + } + + //! Serialization for C style arrays + template inline + typename std::enable_if::value, void>::type + CEREAL_SERIALIZE_FUNCTION_NAME(Archive & ar, T & array) + { + common_detail::serializeArray( ar, array, + std::integral_constant, Archive>::value && + std::is_arithmetic::type>::value>() ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_COMMON_HPP_ diff --git a/tpl/cereal/include/cereal/types/complex.hpp b/tpl/cereal/include/cereal/types/complex.hpp new file mode 100644 index 0000000..7e6ff54 --- /dev/null +++ b/tpl/cereal/include/cereal/types/complex.hpp @@ -0,0 +1,56 @@ +/*! \file complex.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_COMPLEX_HPP_ +#define CEREAL_TYPES_COMPLEX_HPP_ + +#include + +namespace cereal +{ + //! Serializing (save) for std::complex + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::complex const & comp ) + { + ar( CEREAL_NVP_("real", comp.real()), + CEREAL_NVP_("imag", comp.imag()) ); + } + + //! Serializing (load) for std::complex + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::complex & bits ) + { + T real, imag; + ar( CEREAL_NVP_("real", real), + CEREAL_NVP_("imag", imag) ); + bits = {real, imag}; + } +} // namespace cereal + +#endif // CEREAL_TYPES_COMPLEX_HPP_ diff --git a/tpl/cereal/include/cereal/types/concepts/pair_associative_container.hpp b/tpl/cereal/include/cereal/types/concepts/pair_associative_container.hpp new file mode 100644 index 0000000..5f0fd74 --- /dev/null +++ b/tpl/cereal/include/cereal/types/concepts/pair_associative_container.hpp @@ -0,0 +1,73 @@ +/*! \file pair_associative_container.hpp + \brief Support for the PairAssociativeContainer refinement of the + AssociativeContainer concept. + \ingroup TypeConcepts */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_CONCEPTS_PAIR_ASSOCIATIVE_CONTAINER_HPP_ +#define CEREAL_CONCEPTS_PAIR_ASSOCIATIVE_CONTAINER_HPP_ + +#include "cereal/cereal.hpp" + +namespace cereal +{ + //! Saving for std-like pair associative containers + template class Map, typename... Args, typename = typename Map::mapped_type> inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, Map const & map ) + { + ar( make_size_tag( static_cast(map.size()) ) ); + + for( const auto & i : map ) + ar( make_map_item(i.first, i.second) ); + } + + //! Loading for std-like pair associative containers + template class Map, typename... Args, typename = typename Map::mapped_type> inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, Map & map ) + { + size_type size; + ar( make_size_tag( size ) ); + + map.clear(); + + auto hint = map.begin(); + for( size_t i = 0; i < size; ++i ) + { + typename Map::key_type key; + typename Map::mapped_type value; + + ar( make_map_item(key, value) ); + #ifdef CEREAL_OLDER_GCC + hint = map.insert( hint, std::make_pair(std::move(key), std::move(value)) ); + #else // NOT CEREAL_OLDER_GCC + hint = map.emplace_hint( hint, std::move( key ), std::move( value ) ); + #endif // NOT CEREAL_OLDER_GCC + } + } +} // namespace cereal + +#endif // CEREAL_CONCEPTS_PAIR_ASSOCIATIVE_CONTAINER_HPP_ diff --git a/tpl/cereal/include/cereal/types/deque.hpp b/tpl/cereal/include/cereal/types/deque.hpp new file mode 100644 index 0000000..0491d28 --- /dev/null +++ b/tpl/cereal/include/cereal/types/deque.hpp @@ -0,0 +1,62 @@ +/*! \file deque.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_DEQUE_HPP_ +#define CEREAL_TYPES_DEQUE_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Saving for std::deque + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::deque const & deque ) + { + ar( make_size_tag( static_cast(deque.size()) ) ); + + for( auto const & i : deque ) + ar( i ); + } + + //! Loading for std::deque + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::deque & deque ) + { + size_type size; + ar( make_size_tag( size ) ); + + deque.resize( static_cast( size ) ); + + for( auto & i : deque ) + ar( i ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_DEQUE_HPP_ diff --git a/tpl/cereal/include/cereal/types/forward_list.hpp b/tpl/cereal/include/cereal/types/forward_list.hpp new file mode 100644 index 0000000..db87e1f --- /dev/null +++ b/tpl/cereal/include/cereal/types/forward_list.hpp @@ -0,0 +1,68 @@ +/*! \file forward_list.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_FORWARD_LIST_HPP_ +#define CEREAL_TYPES_FORWARD_LIST_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Saving for std::forward_list all other types + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::forward_list const & forward_list ) + { + // write the size - note that this is slow because we need to traverse + // the entire list. there are ways we could avoid this but this was chosen + // since it works in the most general fashion with any archive type + size_type const size = std::distance( forward_list.begin(), forward_list.end() ); + + ar( make_size_tag( size ) ); + + // write the list + for( const auto & i : forward_list ) + ar( i ); + } + + //! Loading for std::forward_list all other types from + template + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::forward_list & forward_list ) + { + size_type size; + ar( make_size_tag( size ) ); + + forward_list.resize( static_cast( size ) ); + + for( auto & i : forward_list ) + ar( i ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_FORWARD_LIST_HPP_ diff --git a/tpl/cereal/include/cereal/types/functional.hpp b/tpl/cereal/include/cereal/types/functional.hpp new file mode 100644 index 0000000..0023ec4 --- /dev/null +++ b/tpl/cereal/include/cereal/types/functional.hpp @@ -0,0 +1,43 @@ +/*! \file functional.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2016, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_FUNCTIONAL_HPP_ +#define CEREAL_TYPES_FUNCTIONAL_HPP_ + +#include + +namespace cereal +{ + //! Saving for std::less + template inline + void serialize( Archive &, std::less & ) + { } +} // namespace cereal + +#endif // CEREAL_TYPES_FUNCTIONAL_HPP_ diff --git a/tpl/cereal/include/cereal/types/list.hpp b/tpl/cereal/include/cereal/types/list.hpp new file mode 100644 index 0000000..6008204 --- /dev/null +++ b/tpl/cereal/include/cereal/types/list.hpp @@ -0,0 +1,62 @@ +/*! \file list.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_LIST_HPP_ +#define CEREAL_TYPES_LIST_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Saving for std::list + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::list const & list ) + { + ar( make_size_tag( static_cast(list.size()) ) ); + + for( auto const & i : list ) + ar( i ); + } + + //! Loading for std::list + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::list & list ) + { + size_type size; + ar( make_size_tag( size ) ); + + list.resize( static_cast( size ) ); + + for( auto & i : list ) + ar( i ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_LIST_HPP_ diff --git a/tpl/cereal/include/cereal/types/map.hpp b/tpl/cereal/include/cereal/types/map.hpp new file mode 100644 index 0000000..41845e9 --- /dev/null +++ b/tpl/cereal/include/cereal/types/map.hpp @@ -0,0 +1,36 @@ +/*! \file map.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_MAP_HPP_ +#define CEREAL_TYPES_MAP_HPP_ + +#include "cereal/types/concepts/pair_associative_container.hpp" +#include + +#endif // CEREAL_TYPES_MAP_HPP_ diff --git a/tpl/cereal/include/cereal/types/memory.hpp b/tpl/cereal/include/cereal/types/memory.hpp new file mode 100644 index 0000000..40c965a --- /dev/null +++ b/tpl/cereal/include/cereal/types/memory.hpp @@ -0,0 +1,425 @@ +/*! \file memory.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_SHARED_PTR_HPP_ +#define CEREAL_TYPES_SHARED_PTR_HPP_ + +#include "cereal/cereal.hpp" +#include +#include + +// Work around MSVC not having alignof +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define CEREAL_ALIGNOF __alignof +#else // not MSVC 2013 or older +#define CEREAL_ALIGNOF alignof +#endif // end MSVC check + +namespace cereal +{ + namespace memory_detail + { + //! A wrapper class to notify cereal that it is ok to serialize the contained pointer + /*! This mechanism allows us to intercept and properly handle polymorphic pointers + @internal */ + template + struct PtrWrapper + { + PtrWrapper(T && p) : ptr(std::forward(p)) {} + T & ptr; + + PtrWrapper & operator=( PtrWrapper const & ) = delete; + }; + + //! Make a PtrWrapper + /*! @internal */ + template inline + PtrWrapper make_ptr_wrapper(T && t) + { + return {std::forward(t)}; + } + + //! A struct that acts as a wrapper around calling load_andor_construct + /*! The purpose of this is to allow a load_and_construct call to properly enter into the + 'data' NVP of the ptr_wrapper + @internal */ + template + struct LoadAndConstructLoadWrapper + { + LoadAndConstructLoadWrapper( T * ptr ) : + construct( ptr ) + { } + + //! Constructor for embedding an early call for restoring shared_from_this + template + LoadAndConstructLoadWrapper( T * ptr, F && sharedFromThisFunc ) : + construct( ptr, sharedFromThisFunc ) + { } + + inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar ) + { + ::cereal::detail::Construct::load_andor_construct( ar, construct ); + } + + ::cereal::construct construct; + }; + + //! A helper struct for saving and restoring the state of types that derive from + //! std::enable_shared_from_this + /*! This special struct is necessary because when a user uses load_and_construct, + the weak_ptr (or whatever implementation defined variant) that allows + enable_shared_from_this to function correctly will not be initialized properly. + + This internal weak_ptr can also be modified by the shared_ptr that is created + during the serialization of a polymorphic pointer, where cereal creates a + wrapper shared_ptr out of a void pointer to the real data. + + In the case of load_and_construct, this happens because it is the allocation + of shared_ptr that perform this initialization, which we let happen on a buffer + of memory (aligned_storage). This buffer is then used for placement new + later on, effectively overwriting any initialized weak_ptr with a default + initialized one, eventually leading to issues when the user calls shared_from_this. + + To get around these issues, we will store the memory for the enable_shared_from_this + portion of the class and replace it after whatever happens to modify it (e.g. the + user performing construction or the wrapper shared_ptr in saving). + + Example usage: + + @code{.cpp} + T * myActualPointer; + { + EnableSharedStateHelper helper( myActualPointer ); // save the state + std::shared_ptr myPtr( myActualPointer ); // modifies the internal weak_ptr + // helper restores state when it goes out of scope + } + @endcode + + When possible, this is designed to be used in an RAII fashion - it will save state on + construction and restore it on destruction. The restore can be done at an earlier time + (e.g. after construct() is called in load_and_construct) in which case the destructor will + do nothing. Performing the restore immediately following construct() allows a user to call + shared_from_this within their load_and_construct function. + + @tparam T Type pointed to by shared_ptr + @internal */ + template + class EnableSharedStateHelper + { + // typedefs for parent type and storage type + using BaseType = typename ::cereal::traits::get_shared_from_this_base::type; + using ParentType = std::enable_shared_from_this; + using StorageType = typename std::aligned_storage::type; + + public: + //! Saves the state of some type inheriting from enable_shared_from_this + /*! @param ptr The raw pointer held by the shared_ptr */ + inline EnableSharedStateHelper( T * ptr ) : + itsPtr( static_cast( ptr ) ), + itsState(), + itsRestored( false ) + { + std::memcpy( &itsState, itsPtr, sizeof(ParentType) ); + } + + //! Restores the state of the held pointer (can only be done once) + inline void restore() + { + if( !itsRestored ) + { + std::memcpy( itsPtr, &itsState, sizeof(ParentType) ); + itsRestored = true; + } + } + + //! Restores the state of the held pointer if not done previously + inline ~EnableSharedStateHelper() + { + restore(); + } + + private: + ParentType * itsPtr; + StorageType itsState; + bool itsRestored; + }; // end EnableSharedStateHelper + + //! Performs loading and construction for a shared pointer that is derived from + //! std::enable_shared_from_this + /*! @param ar The archive + @param ptr Raw pointer held by the shared_ptr + @internal */ + template inline + void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ ) + { + memory_detail::EnableSharedStateHelper state( ptr ); + memory_detail::LoadAndConstructLoadWrapper loadWrapper( ptr, [&](){ state.restore(); } ); + + // let the user perform their initialization, shared state will be restored as soon as construct() + // is called + ar( CEREAL_NVP_("data", loadWrapper) ); + } + + //! Performs loading and construction for a shared pointer that is NOT derived from + //! std::enable_shared_from_this + /*! This is the typical case, where we simply pass the load wrapper to the + archive. + + @param ar The archive + @param ptr Raw pointer held by the shared_ptr + @internal */ + template inline + void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::false_type /* has_shared_from_this */ ) + { + memory_detail::LoadAndConstructLoadWrapper loadWrapper( ptr ); + ar( CEREAL_NVP_("data", loadWrapper) ); + } + } // end namespace memory_detail + + //! Saving std::shared_ptr for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr const & ptr ) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) ); + } + + //! Loading std::shared_ptr, case when no user load and construct for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr & ptr ) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) ); + } + + //! Saving std::weak_ptr for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr const & ptr ) + { + auto const sptr = ptr.lock(); + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) ); + } + + //! Loading std::weak_ptr for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr & ptr ) + { + std::shared_ptr sptr; + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) ); + ptr = sptr; + } + + //! Saving std::unique_ptr for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr const & ptr ) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) ); + } + + //! Loading std::unique_ptr, case when user provides load_and_construct for non polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr & ptr ) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) ); + } + + // ###################################################################### + // Pointer wrapper implementations follow below + + //! Saving std::shared_ptr (wrapper implementation) + /*! @internal */ + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper const &> const & wrapper ) + { + auto & ptr = wrapper.ptr; + + uint32_t id = ar.registerSharedPointer( ptr.get() ); + ar( CEREAL_NVP_("id", id) ); + + if( id & detail::msb_32bit ) + { + ar( CEREAL_NVP_("data", *ptr) ); + } + } + + //! Loading std::shared_ptr, case when user load and construct (wrapper implementation) + /*! @internal */ + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper &> & wrapper ) + { + auto & ptr = wrapper.ptr; + + uint32_t id; + + ar( CEREAL_NVP_("id", id) ); + + if( id & detail::msb_32bit ) + { + // Storage type for the pointer - since we can't default construct this type, + // we'll allocate it using std::aligned_storage and use a custom deleter + using ST = typename std::aligned_storage::type; + + // Valid flag - set to true once construction finishes + // This prevents us from calling the destructor on + // uninitialized data. + auto valid = std::make_shared( false ); + + // Allocate our storage, which we will treat as + // uninitialized until initialized with placement new + ptr.reset( reinterpret_cast( new ST() ), + [=]( T * t ) + { + if( *valid ) + t->~T(); + + delete reinterpret_cast( t ); + } ); + + // Register the pointer + ar.registerSharedPointer( id, ptr ); + + // Perform the actual loading and allocation + memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this::type() ); + + // Mark pointer as valid (initialized) + *valid = true; + } + else + ptr = std::static_pointer_cast(ar.getSharedPointer(id)); + } + + //! Loading std::shared_ptr, case when no user load and construct (wrapper implementation) + /*! @internal */ + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper &> & wrapper ) + { + auto & ptr = wrapper.ptr; + + uint32_t id; + + ar( CEREAL_NVP_("id", id) ); + + if( id & detail::msb_32bit ) + { + ptr.reset( detail::Construct::load_andor_construct() ); + ar.registerSharedPointer( id, ptr ); + ar( CEREAL_NVP_("data", *ptr) ); + } + else + ptr = std::static_pointer_cast(ar.getSharedPointer(id)); + } + + //! Saving std::unique_ptr (wrapper implementation) + /*! @internal */ + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper const &> const & wrapper ) + { + auto & ptr = wrapper.ptr; + + // unique_ptr get one byte of metadata which signifies whether they were a nullptr + // 0 == nullptr + // 1 == not null + + if( !ptr ) + ar( CEREAL_NVP_("valid", uint8_t(0)) ); + else + { + ar( CEREAL_NVP_("valid", uint8_t(1)) ); + ar( CEREAL_NVP_("data", *ptr) ); + } + } + + //! Loading std::unique_ptr, case when user provides load_and_construct (wrapper implementation) + /*! @internal */ + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper &> & wrapper ) + { + uint8_t isValid; + ar( CEREAL_NVP_("valid", isValid) ); + + auto & ptr = wrapper.ptr; + + if( isValid ) + { + // Storage type for the pointer - since we can't default construct this type, + // we'll allocate it using std::aligned_storage + using ST = typename std::aligned_storage::type; + + // Allocate storage - note the ST type so that deleter is correct if + // an exception is thrown before we are initialized + std::unique_ptr stPtr( new ST() ); + + // Use wrapper to enter into "data" nvp of ptr_wrapper + memory_detail::LoadAndConstructLoadWrapper loadWrapper( reinterpret_cast( stPtr.get() ) ); + + // Initialize storage + ar( CEREAL_NVP_("data", loadWrapper) ); + + // Transfer ownership to correct unique_ptr type + ptr.reset( reinterpret_cast( stPtr.release() ) ); + } + else + ptr.reset( nullptr ); + } + + //! Loading std::unique_ptr, case when no load_and_construct (wrapper implementation) + /*! @internal */ + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper &> & wrapper ) + { + uint8_t isValid; + ar( CEREAL_NVP_("valid", isValid) ); + + auto & ptr = wrapper.ptr; + + if( isValid ) + { + ptr.reset( detail::Construct::load_andor_construct() ); + ar( CEREAL_NVP_( "data", *ptr ) ); + } + else + { + ptr.reset( nullptr ); + } + } +} // namespace cereal + +// automatically include polymorphic support +#include "cereal/types/polymorphic.hpp" + +#undef CEREAL_ALIGNOF +#endif // CEREAL_TYPES_SHARED_PTR_HPP_ \ No newline at end of file diff --git a/tpl/cereal/include/cereal/types/polymorphic.hpp b/tpl/cereal/include/cereal/types/polymorphic.hpp new file mode 100644 index 0000000..5acc11f --- /dev/null +++ b/tpl/cereal/include/cereal/types/polymorphic.hpp @@ -0,0 +1,481 @@ +/*! \file polymorphic.hpp + \brief Support for pointers to polymorphic base classes + \ingroup OtherTypes */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_POLYMORPHIC_HPP_ +#define CEREAL_TYPES_POLYMORPHIC_HPP_ + +#include "cereal/cereal.hpp" +#include "cereal/types/memory.hpp" + +#include "cereal/details/util.hpp" +#include "cereal/details/helpers.hpp" +#include "cereal/details/traits.hpp" +#include "cereal/details/polymorphic_impl.hpp" + +#ifdef _MSC_VER +#define CEREAL_STATIC_CONSTEXPR static +#else +#define CEREAL_STATIC_CONSTEXPR static constexpr +#endif + +//! Registers a derived polymorphic type with cereal +/*! Polymorphic types must be registered before smart + pointers to them can be serialized. Note that base + classes do not need to be registered. + + Registering a type lets cereal know how to properly + serialize it when a smart pointer to a base object is + used in conjunction with a derived class. + + This assumes that all relevant archives have also + previously been registered. Registration for archives + is usually done in the header file in which they are + defined. This means that type registration needs to + happen after specific archives to be used are included. + + It is recommended that type registration be done in + the header file in which the type is declared. + + Registration can also be placed in a source file, + but this may require the use of the + CEREAL_REGISTER_DYNAMIC_INIT macro (see below). + + Registration may be called repeatedly for the same + type in different translation units to add support + for additional archives if they are not initially + available (included and registered). + + When building serialization support as a DLL on + Windows, registration must happen in the header file. + On Linux and Mac things should still work properly + if placed in a source file, but see the above comments + on registering in source files. + + Polymorphic support in cereal requires RTTI to be + enabled */ +#define CEREAL_REGISTER_TYPE(...) \ + namespace cereal { \ + namespace detail { \ + template <> \ + struct binding_name<__VA_ARGS__> \ + { \ + CEREAL_STATIC_CONSTEXPR char const * name() { return #__VA_ARGS__; } \ + }; \ + } } /* end namespaces */ \ + CEREAL_BIND_TO_ARCHIVES(__VA_ARGS__) + +//! Registers a polymorphic type with cereal, giving it a +//! user defined name +/*! In some cases the default name used with + CEREAL_REGISTER_TYPE (the name of the type) may not be + suitable. This macro allows any name to be associated + with the type. The name should be unique */ +#define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name) \ + namespace cereal { \ + namespace detail { \ + template <> \ + struct binding_name \ + { CEREAL_STATIC_CONSTEXPR char const * name() { return Name; } }; \ + } } /* end namespaces */ \ + CEREAL_BIND_TO_ARCHIVES(T) + +//! Registers the base-derived relationship for a polymorphic type +/*! When polymorphic serialization occurs, cereal needs to know how to + properly cast between derived and base types for the polymorphic + type. Normally this happens automatically whenever cereal::base_class + or cereal::virtual_base_class are used to serialize a base class. In + cases where neither of these is ever called but a base class still + exists, this explicit registration is required. + + The Derived class should be the most derived type that will be serialized, + and the Base type any possible base that has not been covered under a base + class serialization that will be used to store a Derived pointer. + + Placement of this is the same as for CEREAL_REGISTER_TYPE. */ +#define CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, Derived) \ + namespace cereal { \ + namespace detail { \ + template <> \ + struct PolymorphicRelation \ + { static void bind() { RegisterPolymorphicCaster::bind(); } }; \ + } } /* end namespaces */ + +//! Adds a way to force initialization of a translation unit containing +//! calls to CEREAL_REGISTER_TYPE +/*! In C++, dynamic initialization of non-local variables of a translation + unit may be deferred until "the first odr-use of any function or variable + defined in the same translation unit as the variable to be initialized." + + Informally, odr-use means that your program takes the address of or binds + a reference directly to an object, which must have a definition. + + Since polymorphic type support in cereal relies on the dynamic + initialization of certain global objects happening before + serialization is performed, it is important to ensure that something + from files that call CEREAL_REGISTER_TYPE is odr-used before serialization + occurs, otherwise the registration will never take place. This may often + be the case when serialization is built as a shared library external from + your main program. + + This macro, with any name of your choosing, should be placed into the + source file that contains calls to CEREAL_REGISTER_TYPE. + + Its counterpart, CEREAL_FORCE_DYNAMIC_INIT, should be placed in its + associated header file such that it is included in the translation units + (source files) in which you want the registration to appear. + + @relates CEREAL_FORCE_DYNAMIC_INIT + */ +#define CEREAL_REGISTER_DYNAMIC_INIT(LibName) \ + namespace cereal { \ + namespace detail { \ + void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName() {} \ + } } /* end namespaces */ + +//! Forces dynamic initialization of polymorphic support in a +//! previously registered source file +/*! @sa CEREAL_REGISTER_DYNAMIC_INIT + + See CEREAL_REGISTER_DYNAMIC_INIT for detailed explanation + of how this macro should be used. The name used should + match that for CEREAL_REGISTER_DYNAMIC_INIT. */ +#define CEREAL_FORCE_DYNAMIC_INIT(LibName) \ + namespace cereal { \ + namespace detail { \ + void dynamic_init_dummy_##LibName(); \ + } /* end detail */ \ + namespace { \ + void dynamic_init_##LibName() \ + { \ + ::cereal::detail::dynamic_init_dummy_##LibName(); \ + } \ + } } /* end namespaces */ + +namespace cereal +{ + namespace polymorphic_detail + { + //! Error message used for unregistered polymorphic types + /*! @internal */ + #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name) \ + throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n" \ + "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive " \ + "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n" \ + "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT."); + + //! Get an input binding from the given archive by deserializing the type meta data + /*! @internal */ + template inline + typename ::cereal::detail::InputBindingMap::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid) + { + // If the nameid is zero, we serialized a null pointer + if(nameid == 0) + { + typename ::cereal::detail::InputBindingMap::Serializers emptySerializers; + emptySerializers.shared_ptr = [](void*, std::shared_ptr & ptr, std::type_info const &) { ptr.reset(); }; + emptySerializers.unique_ptr = [](void*, std::unique_ptr> & ptr, std::type_info const &) { ptr.reset( nullptr ); }; + return emptySerializers; + } + + std::string name; + if(nameid & detail::msb_32bit) + { + ar( CEREAL_NVP_("polymorphic_name", name) ); + ar.registerPolymorphicName(nameid, name); + } + else + name = ar.getPolymorphicName(nameid); + + auto const & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(name); + if(binding == bindingMap.end()) + UNREGISTERED_POLYMORPHIC_EXCEPTION(load, name) + return binding->second; + } + + //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee + /*! This check lets us try and skip doing polymorphic machinery if we can get away with + using the derived class serialize function + + Note that on MSVC 2013 preview, is_default_constructible returns true for abstract classes with + default constructors, but on clang/gcc this will return false. So we also need to check for that here. + @internal */ + template inline + typename std::enable_if<(traits::is_default_constructible::value + || traits::has_load_and_construct::value) + && !std::is_abstract::value, bool>::type + serialize_wrapper(Archive & ar, std::shared_ptr & ptr, std::uint32_t const nameid) + { + if(nameid & detail::msb2_32bit) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); + return true; + } + return false; + } + + //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee + /*! This check lets us try and skip doing polymorphic machinery if we can get away with + using the derived class serialize function + @internal */ + template inline + typename std::enable_if<(traits::is_default_constructible::value + || traits::has_load_and_construct::value) + && !std::is_abstract::value, bool>::type + serialize_wrapper(Archive & ar, std::unique_ptr & ptr, std::uint32_t const nameid) + { + if(nameid & detail::msb2_32bit) + { + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); + return true; + } + return false; + } + + //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee + /*! This case is for when we can't actually construct the shared pointer. Normally this would be caught + as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize + the pointer we'd end up back here recursively. So we have to catch the error here as well, if + this was a polymorphic type serialized by its proper pointer type + @internal */ + template inline + typename std::enable_if<(!traits::is_default_constructible::value + && !traits::has_load_and_construct::value) + || std::is_abstract::value, bool>::type + serialize_wrapper(Archive &, std::shared_ptr &, std::uint32_t const nameid) + { + if(nameid & detail::msb2_32bit) + throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function"); + return false; + } + + //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee + /*! This case is for when we can't actually construct the unique pointer. Normally this would be caught + as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize + the pointer we'd end up back here recursively. So we have to catch the error here as well, if + this was a polymorphic type serialized by its proper pointer type + @internal */ + template inline + typename std::enable_if<(!traits::is_default_constructible::value + && !traits::has_load_and_construct::value) + || std::is_abstract::value, bool>::type + serialize_wrapper(Archive &, std::unique_ptr &, std::uint32_t const nameid) + { + if(nameid & detail::msb2_32bit) + throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function"); + return false; + } + } // polymorphic_detail + + // ###################################################################### + // Pointer serialization for polymorphic types + + //! Saving std::shared_ptr for polymorphic types, abstract + template inline + typename std::enable_if::value && std::is_abstract::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr const & ptr ) + { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) ); + return; + } + + std::type_info const & ptrinfo = typeid(*ptr.get()); + static std::type_info const & tinfo = typeid(T); + // ptrinfo can never be equal to T info since we can't have an instance + // of an abstract object + // this implies we need to do the lookup + + auto const & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(std::type_index(ptrinfo)); + if(binding == bindingMap.end()) + UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name())) + + binding->second.shared_ptr(&ar, ptr.get(), tinfo); + } + + //! Saving std::shared_ptr for polymorphic types, not abstract + template inline + typename std::enable_if::value && !std::is_abstract::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr const & ptr ) + { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) ); + return; + } + + std::type_info const & ptrinfo = typeid(*ptr.get()); + static std::type_info const & tinfo = typeid(T); + + if(ptrinfo == tinfo) + { + // The 2nd msb signals that the following pointer does not need to be + // cast with our polymorphic machinery + ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) ); + + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); + + return; + } + + auto const & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(std::type_index(ptrinfo)); + if(binding == bindingMap.end()) + UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name())) + + binding->second.shared_ptr(&ar, ptr.get(), tinfo); + } + + //! Loading std::shared_ptr for polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr & ptr ) + { + std::uint32_t nameid; + ar( CEREAL_NVP_("polymorphic_id", nameid) ); + + // Check to see if we can skip all of this polymorphism business + if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid)) + return; + + auto binding = polymorphic_detail::getInputBinding(ar, nameid); + std::shared_ptr result; + binding.shared_ptr(&ar, result, typeid(T)); + ptr = std::static_pointer_cast(result); + } + + //! Saving std::weak_ptr for polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr const & ptr ) + { + auto const sptr = ptr.lock(); + ar( CEREAL_NVP_("locked_ptr", sptr) ); + } + + //! Loading std::weak_ptr for polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr & ptr ) + { + std::shared_ptr sptr; + ar( CEREAL_NVP_("locked_ptr", sptr) ); + ptr = sptr; + } + + //! Saving std::unique_ptr for polymorphic types that are abstract + template inline + typename std::enable_if::value && std::is_abstract::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr const & ptr ) + { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) ); + return; + } + + std::type_info const & ptrinfo = typeid(*ptr.get()); + static std::type_info const & tinfo = typeid(T); + // ptrinfo can never be equal to T info since we can't have an instance + // of an abstract object + // this implies we need to do the lookup + + auto const & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(std::type_index(ptrinfo)); + if(binding == bindingMap.end()) + UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name())) + + binding->second.unique_ptr(&ar, ptr.get(), tinfo); + } + + //! Saving std::unique_ptr for polymorphic types, not abstract + template inline + typename std::enable_if::value && !std::is_abstract::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr const & ptr ) + { + if(!ptr) + { + // same behavior as nullptr in memory implementation + ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) ); + return; + } + + std::type_info const & ptrinfo = typeid(*ptr.get()); + static std::type_info const & tinfo = typeid(T); + + if(ptrinfo == tinfo) + { + // The 2nd msb signals that the following pointer does not need to be + // cast with our polymorphic machinery + ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) ); + + ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); + + return; + } + + auto const & bindingMap = detail::StaticObject>::getInstance().map; + + auto binding = bindingMap.find(std::type_index(ptrinfo)); + if(binding == bindingMap.end()) + UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name())) + + binding->second.unique_ptr(&ar, ptr.get(), tinfo); + } + + //! Loading std::unique_ptr, case when user provides load_and_construct for polymorphic types + template inline + typename std::enable_if::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr & ptr ) + { + std::uint32_t nameid; + ar( CEREAL_NVP_("polymorphic_id", nameid) ); + + // Check to see if we can skip all of this polymorphism business + if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid)) + return; + + auto binding = polymorphic_detail::getInputBinding(ar, nameid); + std::unique_ptr> result; + binding.unique_ptr(&ar, result, typeid(T)); + ptr.reset(static_cast(result.release())); + } + + #undef UNREGISTERED_POLYMORPHIC_EXCEPTION +} // namespace cereal +#endif // CEREAL_TYPES_POLYMORPHIC_HPP_ diff --git a/tpl/cereal/include/cereal/types/queue.hpp b/tpl/cereal/include/cereal/types/queue.hpp new file mode 100644 index 0000000..52d26a2 --- /dev/null +++ b/tpl/cereal/include/cereal/types/queue.hpp @@ -0,0 +1,132 @@ +/*! \file queue.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_QUEUE_HPP_ +#define CEREAL_TYPES_QUEUE_HPP_ + +#include "cereal/details/helpers.hpp" +#include + +// The default container for queue is deque, so let's include that too +#include "cereal/types/deque.hpp" +// The default comparator for queue is less +#include "cereal/types/functional.hpp" + +namespace cereal +{ + namespace queue_detail + { + //! Allows access to the protected container in queue + /*! @internal */ + template inline + C const & container( std::queue const & queue ) + { + struct H : public std::queue + { + static C const & get( std::queue const & q ) + { + return q.*(&H::c); + } + }; + + return H::get( queue ); + } + + //! Allows access to the protected container in priority queue + /*! @internal */ + template inline + C const & container( std::priority_queue const & priority_queue ) + { + struct H : public std::priority_queue + { + static C const & get( std::priority_queue const & pq ) + { + return pq.*(&H::c); + } + }; + + return H::get( priority_queue ); + } + + //! Allows access to the protected comparator in priority queue + /*! @internal */ + template inline + Comp const & comparator( std::priority_queue const & priority_queue ) + { + struct H : public std::priority_queue + { + static Comp const & get( std::priority_queue const & pq ) + { + return pq.*(&H::comp); + } + }; + + return H::get( priority_queue ); + } + } + + //! Saving for std::queue + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::queue const & queue ) + { + ar( CEREAL_NVP_("container", queue_detail::container( queue )) ); + } + + //! Loading for std::queue + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::queue & queue ) + { + C container; + ar( CEREAL_NVP_("container", container) ); + queue = std::queue( std::move( container ) ); + } + + //! Saving for std::priority_queue + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::priority_queue const & priority_queue ) + { + ar( CEREAL_NVP_("comparator", queue_detail::comparator( priority_queue )) ); + ar( CEREAL_NVP_("container", queue_detail::container( priority_queue )) ); + } + + //! Loading for std::priority_queue + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::priority_queue & priority_queue ) + { + Comp comparator; + ar( CEREAL_NVP_("comparator", comparator) ); + + C container; + ar( CEREAL_NVP_("container", container) ); + + priority_queue = std::priority_queue( comparator, std::move( container ) ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_QUEUE_HPP_ diff --git a/tpl/cereal/include/cereal/types/set.hpp b/tpl/cereal/include/cereal/types/set.hpp new file mode 100644 index 0000000..149a5b4 --- /dev/null +++ b/tpl/cereal/include/cereal/types/set.hpp @@ -0,0 +1,103 @@ +/*! \file set.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_SET_HPP_ +#define CEREAL_TYPES_SET_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + namespace set_detail + { + //! @internal + template inline + void save( Archive & ar, SetT const & set ) + { + ar( make_size_tag( static_cast(set.size()) ) ); + + for( const auto & i : set ) + ar( i ); + } + + //! @internal + template inline + void load( Archive & ar, SetT & set ) + { + size_type size; + ar( make_size_tag( size ) ); + + set.clear(); + + auto hint = set.begin(); + for( size_type i = 0; i < size; ++i ) + { + typename SetT::key_type key; + + ar( key ); + #ifdef CEREAL_OLDER_GCC + hint = set.insert( hint, std::move( key ) ); + #else // NOT CEREAL_OLDER_GCC + hint = set.emplace_hint( hint, std::move( key ) ); + #endif // NOT CEREAL_OLDER_GCC + } + } + } + + //! Saving for std::set + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::set const & set ) + { + set_detail::save( ar, set ); + } + + //! Loading for std::set + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::set & set ) + { + set_detail::load( ar, set ); + } + + //! Saving for std::multiset + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::multiset const & multiset ) + { + set_detail::save( ar, multiset ); + } + + //! Loading for std::multiset + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::multiset & multiset ) + { + set_detail::load( ar, multiset ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_SET_HPP_ diff --git a/tpl/cereal/include/cereal/types/stack.hpp b/tpl/cereal/include/cereal/types/stack.hpp new file mode 100644 index 0000000..b69b660 --- /dev/null +++ b/tpl/cereal/include/cereal/types/stack.hpp @@ -0,0 +1,76 @@ +/*! \file stack.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_STACK_HPP_ +#define CEREAL_TYPES_STACK_HPP_ + +#include "cereal/cereal.hpp" +#include + +// The default container for stack is deque, so let's include that too +#include "cereal/types/deque.hpp" + +namespace cereal +{ + namespace stack_detail + { + //! Allows access to the protected container in stack + template inline + C const & container( std::stack const & stack ) + { + struct H : public std::stack + { + static C const & get( std::stack const & s ) + { + return s.*(&H::c); + } + }; + + return H::get( stack ); + } + } + + //! Saving for std::stack + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::stack const & stack ) + { + ar( CEREAL_NVP_("container", stack_detail::container( stack )) ); + } + + //! Loading for std::stack + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::stack & stack ) + { + C container; + ar( CEREAL_NVP_("container", container) ); + stack = std::stack( std::move( container ) ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_STACK_HPP_ diff --git a/tpl/cereal/include/cereal/types/string.hpp b/tpl/cereal/include/cereal/types/string.hpp new file mode 100644 index 0000000..e7488a9 --- /dev/null +++ b/tpl/cereal/include/cereal/types/string.hpp @@ -0,0 +1,61 @@ +/*! \file string.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_STRING_HPP_ +#define CEREAL_TYPES_STRING_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Serialization for basic_string types, if binary data is supported + template inline + typename std::enable_if, Archive>::value, void>::type + CEREAL_SAVE_FUNCTION_NAME(Archive & ar, std::basic_string const & str) + { + // Save number of chars + the data + ar( make_size_tag( static_cast(str.size()) ) ); + ar( binary_data( str.data(), str.size() * sizeof(CharT) ) ); + } + + //! Serialization for basic_string types, if binary data is supported + template inline + typename std::enable_if, Archive>::value, void>::type + CEREAL_LOAD_FUNCTION_NAME(Archive & ar, std::basic_string & str) + { + size_type size; + ar( make_size_tag( size ) ); + str.resize(static_cast(size)); + ar( binary_data( const_cast( str.data() ), static_cast(size) * sizeof(CharT) ) ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_STRING_HPP_ + diff --git a/tpl/cereal/include/cereal/types/tuple.hpp b/tpl/cereal/include/cereal/types/tuple.hpp new file mode 100644 index 0000000..7e56f0d --- /dev/null +++ b/tpl/cereal/include/cereal/types/tuple.hpp @@ -0,0 +1,123 @@ +/*! \file tuple.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_TUPLE_HPP_ +#define CEREAL_TYPES_TUPLE_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + namespace tuple_detail + { + //! Creates a c string from a sequence of characters + /*! The c string created will alwas be prefixed by "tuple_element" + Based on code from: http://stackoverflow/a/20973438/710791 + @internal */ + template + struct char_seq_to_c_str + { + static const int size = 14;// Size of array for the word: tuple_element + typedef const char (&arr_type)[sizeof...(Cs) + size]; + static const char str[sizeof...(Cs) + size]; + }; + + // the word tuple_element plus a number + //! @internal + template + const char char_seq_to_c_str::str[sizeof...(Cs) + size] = + {'t','u','p','l','e','_','e','l','e','m','e','n','t', Cs..., '\0'}; + + //! Converts a number into a sequence of characters + /*! @tparam Q The quotient of dividing the original number by 10 + @tparam R The remainder of dividing the original number by 10 + @tparam C The sequence built so far + @internal */ + template + struct to_string_impl + { + using type = typename to_string_impl::type; + }; + + //! Base case with no quotient + /*! @internal */ + template + struct to_string_impl<0, R, C...> + { + using type = char_seq_to_c_str; + }; + + //! Generates a c string for a given index of a tuple + /*! Example use: + @code{cpp} + tuple_element_name<3>::c_str();// returns "tuple_element3" + @endcode + @internal */ + template + struct tuple_element_name + { + using type = typename to_string_impl::type; + static const typename type::arr_type c_str(){ return type::str; }; + }; + + // unwinds a tuple to save it + //! @internal + template + struct serialize + { + template inline + static void apply( Archive & ar, std::tuple & tuple ) + { + serialize::template apply( ar, tuple ); + ar( CEREAL_NVP_(tuple_element_name::c_str(), + std::get( tuple )) ); + } + }; + + // Zero height specialization - nothing to do here + //! @internal + template <> + struct serialize<0> + { + template inline + static void apply( Archive &, std::tuple & ) + { } + }; + } + + //! Serializing for std::tuple + template inline + void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, std::tuple & tuple ) + { + tuple_detail::serialize>::value>::template apply( ar, tuple ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_TUPLE_HPP_ diff --git a/tpl/cereal/include/cereal/types/unordered_map.hpp b/tpl/cereal/include/cereal/types/unordered_map.hpp new file mode 100644 index 0000000..3b0f80b --- /dev/null +++ b/tpl/cereal/include/cereal/types/unordered_map.hpp @@ -0,0 +1,36 @@ +/*! \file unordered_map.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_UNORDERED_MAP_HPP_ +#define CEREAL_TYPES_UNORDERED_MAP_HPP_ + +#include "cereal/types/concepts/pair_associative_container.hpp" +#include + +#endif // CEREAL_TYPES_UNORDERED_MAP_HPP_ diff --git a/tpl/cereal/include/cereal/types/unordered_set.hpp b/tpl/cereal/include/cereal/types/unordered_set.hpp new file mode 100644 index 0000000..b86d8e5 --- /dev/null +++ b/tpl/cereal/include/cereal/types/unordered_set.hpp @@ -0,0 +1,99 @@ +/*! \file unordered_set.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_UNORDERED_SET_HPP_ +#define CEREAL_TYPES_UNORDERED_SET_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + namespace unordered_set_detail + { + //! @internal + template inline + void save( Archive & ar, SetT const & set ) + { + ar( make_size_tag( static_cast(set.size()) ) ); + + for( const auto & i : set ) + ar( i ); + } + + //! @internal + template inline + void load( Archive & ar, SetT & set ) + { + size_type size; + ar( make_size_tag( size ) ); + + set.clear(); + set.reserve( static_cast( size ) ); + + for( size_type i = 0; i < size; ++i ) + { + typename SetT::key_type key; + + ar( key ); + set.emplace( std::move( key ) ); + } + } + } + + //! Saving for std::unordered_set + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unordered_set const & unordered_set ) + { + unordered_set_detail::save( ar, unordered_set ); + } + + //! Loading for std::unordered_set + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unordered_set & unordered_set ) + { + unordered_set_detail::load( ar, unordered_set ); + } + + //! Saving for std::unordered_multiset + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unordered_multiset const & unordered_multiset ) + { + unordered_set_detail::save( ar, unordered_multiset ); + } + + //! Loading for std::unordered_multiset + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unordered_multiset & unordered_multiset ) + { + unordered_set_detail::load( ar, unordered_multiset ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_UNORDERED_SET_HPP_ diff --git a/tpl/cereal/include/cereal/types/utility.hpp b/tpl/cereal/include/cereal/types/utility.hpp new file mode 100644 index 0000000..076bea8 --- /dev/null +++ b/tpl/cereal/include/cereal/types/utility.hpp @@ -0,0 +1,47 @@ +/*! \file utility.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_UTILITY_HPP_ +#define CEREAL_TYPES_UTILITY_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Serializing for std::pair + template inline + void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, std::pair & pair ) + { + ar( CEREAL_NVP_("first", pair.first), + CEREAL_NVP_("second", pair.second) ); + } +} // namespace cereal + +#endif // CEREAL_TYPES_UTILITY_HPP_ diff --git a/tpl/cereal/include/cereal/types/valarray.hpp b/tpl/cereal/include/cereal/types/valarray.hpp new file mode 100644 index 0000000..dcc471d --- /dev/null +++ b/tpl/cereal/include/cereal/types/valarray.hpp @@ -0,0 +1,89 @@ +/*! \file valarray.hpp +\brief Support for types found in \ +\ingroup STLSupport */ + +/* +Copyright (c) 2014, Randolph Voorhies, Shane Grant +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of cereal nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CEREAL_TYPES_VALARRAY_HPP_ +#define CEREAL_TYPES_VALARRAY_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Saving for std::valarray arithmetic types, using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::valarray const & valarray ) + { + ar( make_size_tag( static_cast(valarray.size()) ) ); // number of elements + ar( binary_data( &valarray[0], valarray.size() * sizeof(T) ) ); // &valarray[0] ok since guaranteed contiguous + } + + //! Loading for std::valarray arithmetic types, using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::valarray & valarray ) + { + size_type valarraySize; + ar( make_size_tag( valarraySize ) ); + + valarray.resize( static_cast( valarraySize ) ); + ar( binary_data( &valarray[0], static_cast( valarraySize ) * sizeof(T) ) ); + } + + //! Saving for std::valarray all other types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::valarray const & valarray ) + { + ar( make_size_tag( static_cast(valarray.size()) ) ); // number of elements + for(auto && v : valarray) + ar(v); + } + + //! Loading for std::valarray all other types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::valarray & valarray ) + { + size_type valarraySize; + ar( make_size_tag( valarraySize ) ); + + valarray.resize( static_cast( valarraySize ) ); + for(auto && v : valarray) + ar(v); + } +} // namespace cereal + +#endif // CEREAL_TYPES_VALARRAY_HPP_ diff --git a/tpl/cereal/include/cereal/types/vector.hpp b/tpl/cereal/include/cereal/types/vector.hpp new file mode 100644 index 0000000..8302cb9 --- /dev/null +++ b/tpl/cereal/include/cereal/types/vector.hpp @@ -0,0 +1,112 @@ +/*! \file vector.hpp + \brief Support for types found in \ + \ingroup STLSupport */ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TYPES_VECTOR_HPP_ +#define CEREAL_TYPES_VECTOR_HPP_ + +#include "cereal/cereal.hpp" +#include + +namespace cereal +{ + //! Serialization for std::vectors of arithmetic (but not bool) using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value && !std::is_same::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::vector const & vector ) + { + ar( make_size_tag( static_cast(vector.size()) ) ); // number of elements + ar( binary_data( vector.data(), vector.size() * sizeof(T) ) ); + } + + //! Serialization for std::vectors of arithmetic (but not bool) using binary serialization, if supported + template inline + typename std::enable_if, Archive>::value + && std::is_arithmetic::value && !std::is_same::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::vector & vector ) + { + size_type vectorSize; + ar( make_size_tag( vectorSize ) ); + + vector.resize( static_cast( vectorSize ) ); + ar( binary_data( vector.data(), static_cast( vectorSize ) * sizeof(T) ) ); + } + + //! Serialization for non-arithmetic vector types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::vector const & vector ) + { + ar( make_size_tag( static_cast(vector.size()) ) ); // number of elements + for(auto && v : vector) + ar( v ); + } + + //! Serialization for non-arithmetic vector types + template inline + typename std::enable_if, Archive>::value + || !std::is_arithmetic::value, void>::type + CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::vector & vector ) + { + size_type size; + ar( make_size_tag( size ) ); + + vector.resize( static_cast( size ) ); + for(auto && v : vector) + ar( v ); + } + + //! Serialization for bool vector types + template inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::vector const & vector ) + { + ar( make_size_tag( static_cast(vector.size()) ) ); // number of elements + for(auto && v : vector) + ar( static_cast(v) ); + } + + //! Serialization for bool vector types + template inline + void CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::vector & vector ) + { + size_type size; + ar( make_size_tag( size ) ); + + vector.resize( static_cast( size ) ); + for(auto && v : vector) + { + bool b; + ar( b ); + v = b; + } + } +} // namespace cereal + +#endif // CEREAL_TYPES_VECTOR_HPP_ diff --git a/tpl/cereal/sandbox/CMakeLists.txt b/tpl/cereal/sandbox/CMakeLists.txt new file mode 100644 index 0000000..52c8df8 --- /dev/null +++ b/tpl/cereal/sandbox/CMakeLists.txt @@ -0,0 +1,17 @@ +add_subdirectory(sandbox_shared_lib) + +add_executable(sandbox sandbox.cpp) +add_executable(sandbox_json sandbox_json.cpp) +add_executable(sandbox_rtti sandbox_rtti.cpp) + +add_executable(sandbox_vs sandbox_vs.cpp) +target_link_libraries(sandbox_vs sandbox_vs_dll) +include_directories(sandbox_shared_lib) + +if(Boost_FOUND) + add_executable(performance performance.cpp) + if(MSVC) + set_target_properties(performance PROPERTIES COMPILE_DEFINITIONS "BOOST_SERIALIZATION_DYN_LINK") + endif() + target_link_libraries(performance ${Boost_LIBRARIES}) +endif(Boost_FOUND) diff --git a/tpl/cereal/sandbox/performance.cpp b/tpl/cereal/sandbox/performance.cpp new file mode 100644 index 0000000..e5d61f0 --- /dev/null +++ b/tpl/cereal/sandbox/performance.cpp @@ -0,0 +1,469 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4244 4267) +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//! Runs serialization to save data to an ostringstream +/*! Used to time how long it takes to save data to an ostringstream. + Everything that happens within the save function will be timed, including + any set-up necessary to perform the serialization. + + @param data The data to save + @param saveFunction A function taking in an ostringstream and the data and returning void + @return The ostringstream and the time it took to save the data */ +template +std::chrono::nanoseconds +saveData( T const & data, std::function saveFunction, std::ostringstream & os ) +{ + auto start = std::chrono::high_resolution_clock::now(); + saveFunction( os, data ); + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start ); +} + +//! Runs serialization to load data to from an istringstream +/*! Used to time how long it takes to load data from an istringstream. + Everything that happens within the load function will be timed, including + any set-up necessary to perform the serialization. + + @param dataStream The saved data stream + @param loadFunction A function taking in an istringstream and a data reference and returning void + @return The loaded data and the time it took to save the data */ +template +std::pair +loadData( std::ostringstream const & dataStream, std::function loadFunction ) +{ + T data; + std::istringstream os( dataStream.str() ); + + auto start = std::chrono::high_resolution_clock::now(); + loadFunction( os, data ); + + return {data, std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start )}; +} + +struct cerealBinary +{ + //! Saves data to a cereal binary archive + template + static void save( std::ostringstream & os, T const & data ) + { + cereal::BinaryOutputArchive oar(os); + oar(data); + } + + //! Loads data to a cereal binary archive + template + static void load( std::istringstream & is, T & data ) + { + cereal::BinaryInputArchive iar(is); + iar(data); + } +}; + +struct boostBinary +{ + //! Saves data to a boost binary archive + template + static void save( std::ostringstream & os, T const & data ) + { + boost::archive::binary_oarchive oar(os); + oar & data; + } + + //! Loads data to a boost binary archive + template + static void load( std::istringstream & is, T & data ) + { + boost::archive::binary_iarchive iar(is); + iar & data; + } +}; + +struct binary +{ + typedef boostBinary boost; + typedef cerealBinary cereal; +}; + +//! Times how long it takes to serialize (load and store) some data +/*! Times how long and the size of the serialization object used to serialize + some data. Result is output to standard out. + + @tparam SerializationT The serialization struct that has all save and load functions + @tparam DataTCereal The type of data to test for cereal + @tparam DataTBoost The type of data to test for boost + @param name The name for this test + @param data The data to serialize for cereal + @param data The data to serialize for boost + @param numAverages The number of times to average + @param validateData Whether data should be validated (input == output) */ +template +void test( std::string const & name, + DataTCereal const & dataC, + DataTBoost const & dataB, + size_t numAverages = 100, + bool validateData = false ); + +template +void test( std::string const & name, + DataTCereal const & dataC, + DataTBoost const & dataB, + size_t numAverages, + bool /*validateData*/ ) +{ + std::cout << "-----------------------------------" << std::endl; + std::cout << "Running test: " << name << std::endl; + + std::chrono::nanoseconds totalBoostSave{0}; + std::chrono::nanoseconds totalBoostLoad{0}; + + std::chrono::nanoseconds totalCerealSave{0}; + std::chrono::nanoseconds totalCerealLoad{0}; + + size_t boostSize = 0; + size_t cerealSize = 0; + + for(size_t i = 0; i < numAverages; ++i) + { + // Boost + { + std::ostringstream os; + auto saveResult = saveData( dataB, {SerializationT::boost::template save}, os ); + totalBoostSave += saveResult; + if(!boostSize) + boostSize = os.tellp(); + + auto loadResult = loadData( os, {SerializationT::boost::template load} ); + totalBoostLoad += loadResult.second; + } + + // Cereal + { + std::ostringstream os; + auto saveResult = saveData( dataC, {SerializationT::cereal::template save}, os ); + totalCerealSave += saveResult; + if(!cerealSize) + cerealSize = os.tellp(); + + auto loadResult = loadData( os, {SerializationT::cereal::template load} ); + totalCerealLoad += loadResult.second; + } + } + + // Averages + double averageBoostSave = std::chrono::duration_cast(totalBoostSave).count() / static_cast( numAverages ); + double averageBoostLoad = std::chrono::duration_cast(totalBoostLoad).count() / static_cast( numAverages ); + + double averageCerealSave = std::chrono::duration_cast(totalCerealSave).count() / static_cast( numAverages ); + double averageCerealLoad = std::chrono::duration_cast(totalCerealLoad).count() / static_cast( numAverages ); + + // Percentages relative to boost + double cerealSaveP = averageCerealSave / averageBoostSave; + double cerealLoadP = averageCerealLoad / averageBoostLoad; + double cerealSizeP = cerealSize / static_cast( boostSize ); + + std::cout << " Boost results:" << std::endl; + std::cout << boost::format("\tsave | time: %06.4fms (%1.2f) size: %20.8fkb (%1.8f) total: %6.1fms") + % averageBoostSave % 1.0 % (boostSize / 1024.0) % 1.0 % static_cast( std::chrono::duration_cast(totalBoostSave).count() ); + std::cout << std::endl; + std::cout << boost::format("\tload | time: %06.4fms (%1.2f) total: %6.1fms") + % averageBoostLoad % 1.0 % static_cast( std::chrono::duration_cast(totalBoostLoad).count() ); + std::cout << std::endl; + + std::cout << " Cereal results:" << std::endl; + std::cout << boost::format("\tsave | time: %06.4fms (%1.2f) size: %20.8fkb (%1.8f) total: %6.1fms") + % averageCerealSave % cerealSaveP % (cerealSize / 1024.0) % cerealSizeP % static_cast( std::chrono::duration_cast(totalCerealSave).count() ); + std::cout << std::endl; + std::cout << boost::format("\tload | time: %06.4fms (%1.2f) total: %6.1fms") + % averageCerealLoad % cerealLoadP % static_cast( std::chrono::duration_cast(totalCerealLoad).count() ); + std::cout << std::endl; +} + +template +void test( std::string const & name, + DataT const & data, + size_t numAverages = 100, + bool validateData = false ) +{ + return test( name, data, data, numAverages, validateData ); +} + +template +typename std::enable_if::value, T>::type +random_value(std::mt19937 & gen) +{ return std::uniform_real_distribution(-10000.0, 10000.0)(gen); } + +template +typename std::enable_if::value && sizeof(T) != sizeof(char), T>::type +random_value(std::mt19937 & gen) +{ return std::uniform_int_distribution(std::numeric_limits::lowest(), std::numeric_limits::max())(gen); } + +template +typename std::enable_if::value && sizeof(T) == sizeof(char), T>::type +random_value(std::mt19937 & gen) +{ return static_cast( std::uniform_int_distribution(std::numeric_limits::lowest(), std::numeric_limits::max())(gen) ); } + +template +typename std::enable_if::value, std::string>::type +random_value(std::mt19937 & gen) +{ + std::string s(std::uniform_int_distribution(3, 30)(gen), ' '); + for(char & c : s) + c = std::uniform_int_distribution(' ', '~')(gen); + return s; +} + +template +std::basic_string random_basic_string(std::mt19937 & gen, size_t maxSize = 30) +{ + std::basic_string s(std::uniform_int_distribution(3, maxSize)(gen), ' '); + for(C & c : s) + c = static_cast( std::uniform_int_distribution( '~', '~' )(gen) ); + return s; + return s; +} + +template +std::string random_binary_string(std::mt19937 & gen) +{ + std::string s(N, ' '); + for(auto & c : s ) + c = std::uniform_int_distribution('0', '1')(gen); + return s; +} + +struct PoDStructCereal +{ + int32_t a; + int64_t b; + float c; + double d; + + template + void serialize( Archive & ar ) + { + ar(a, b, c, d); + } +}; + +struct PoDStructBoost +{ + int32_t a; + int64_t b; + float c; + double d; + + template + void serialize( Archive & ar, const unsigned int /*version*/ ) + { + ar & a & b & c & d; + } +}; + +struct PoDChildCereal : virtual PoDStructCereal +{ + PoDChildCereal() : v(1024) + { } + + std::vector v; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class(this), v ); + } +}; + +struct PoDChildBoost : virtual PoDStructBoost +{ + PoDChildBoost() : v(1024) + { } + + std::vector v; + + template + void serialize( Archive & ar, const unsigned int /*version*/ ) + { + ar & boost::serialization::base_object(*this); + ar & v; + } +}; + +int main() +{ + std::random_device rd; + std::mt19937 gen(rd()); + auto rngC = [&](){ return random_value(gen); }; + auto rngD = [&](){ return random_value(gen); }; + const bool randomize = false; + + //######################################## + auto vectorDoubleTest = [&](size_t s, bool randomize_) + { + std::ostringstream name; + name << "Vector(double) size " << s; + + std::vector data(s); + if(randomize_) + for( auto & d : data ) + d = rngD(); + + test( name.str(), data ); + }; + + vectorDoubleTest(1, randomize); // 8B + vectorDoubleTest(16, randomize); // 128B + vectorDoubleTest(1024, randomize); // 8KB + vectorDoubleTest(1024*1024, randomize); // 8MB + + //######################################## + auto vectorCharTest = [&](size_t s, bool randomize_) + { + std::ostringstream name; + name << "Vector(uint8_t) size " << s; + + std::vector data(s); + if(randomize_) + for( auto & d : data ) + d = rngC(); + + test( name.str(), data ); + }; + + vectorCharTest(1024*1024*64, randomize); + + //######################################## + auto vectorPoDStructTest = [&](size_t s) + { + std::ostringstream name; + name << "Vector(PoDStruct) size " << s; + + std::vector dataC(s); + std::vector dataB(s); + test( name.str(), dataC, dataB ); + }; + + vectorPoDStructTest(1); + vectorPoDStructTest(64); + vectorPoDStructTest(1024); + vectorPoDStructTest(1024*1024); + vectorPoDStructTest(1024*1024*2); + + //######################################## + auto vectorPoDChildTest = [&](size_t s) + { + std::ostringstream name; + name << "Vector(PoDChild) size " << s; + + std::vector dataC(s); + std::vector dataB(s); + test( name.str(), dataC, dataB ); + }; + + vectorPoDChildTest(1024); + vectorPoDChildTest(1024*32); + + //######################################## + auto stringTest = [&](size_t s) + { + std::ostringstream name; + name << "String size " << s; + + std::string data = random_basic_string(gen, s); + std::cout << "data.size " << data.size() << std::endl; + test( name.str(), data ); + }; + + stringTest(200000); + stringTest(2000000); + stringTest(20000000); + + //######################################## + auto vectorStringTest = [&](size_t s) + { + std::ostringstream name; + name << "Vector(String) size " << s; + + std::vector data(s); + for(size_t i=0; i(gen); + + test( name.str(), data ); + }; + + vectorStringTest(512); + vectorStringTest(1024); + vectorStringTest(1024*64); + vectorStringTest(1024*128); + + //######################################## + auto mapPoDStructTest = [&](size_t s) + { + std::ostringstream name; + name << "Map(PoDStruct) size " < mC; + std::map mB; + for(size_t i=0; i(name.str(), mC, mB); + }; + + mapPoDStructTest(1024); + mapPoDStructTest(1024*64); + + return 0; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/tpl/cereal/sandbox/sandbox.cpp b/tpl/cereal/sandbox/sandbox.cpp new file mode 100644 index 0000000..af28b22 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox.cpp @@ -0,0 +1,820 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +#include +#include +#include +#include +#include +#include + +class Base +{ + private: + friend class cereal::access; + template + void serialize( Archive & ar ) + { + std::cout << "Base serialize" << std::endl; + ar( x ); + } + + virtual void foo() = 0; + + public: + int x; +}; + +class Derived : public Base +{ + public: + using Base::x; + Derived() : Base(), y() {} + Derived( int d, int b ) + { + y = d; + x = b; + } + + template + void save( Archive & ar ) const + { + ar( cereal::virtual_base_class(this) ); + std::cout << "Derived save" << std::endl; + ar( y ); + } + + template + void load( Archive & ar ) + { + ar( cereal::virtual_base_class(this) ); + std::cout << "Derived load" << std::endl; + ar( y ); + } + + void foo() {} + + int y; +}; + +namespace cereal +{ + template struct specialize {}; +} + +CEREAL_REGISTER_TYPE(Derived) + +// ################################### +struct Test1 +{ + int a; + + private: + friend class cereal::access; + template + void serialize(Archive & ar) + { + ar(CEREAL_NVP(a)); + } +}; + +// ################################### +class Test2 +{ + public: + Test2() {} + Test2( int x ) : a( x ) {} + int a; + + private: + friend class cereal::access; + + template + void save(Archive & ar) const + { + ar(a); + } + + template + void load(Archive & ar) + { + ar(a); + } +}; + +// ################################### +struct Test3 +{ + int a; +}; + +template +void serialize(Archive & ar, Test3 & t) +{ + ar(CEREAL_NVP(t.a)); +} + +namespace test4 +{ + // ################################### + struct Test4 + { + int a; + }; + + template + void save(Archive & ar, Test4 const & t) + { + ar(CEREAL_NVP(t.a)); + } + + template + void load(Archive & ar, Test4 & t) + { + ar(CEREAL_NVP(t.a)); + } +} + +class Private +{ + public: + Private() : a('z') {} + + private: + char a; + + friend class cereal::access; + + template + void serialize(Archive & ar) + { + ar(a); + } +}; + +struct Everything +{ + int x; + int y; + Test1 t1; + Test2 t2; + Test3 t3; + test4::Test4 t4; + std::string s; + + template + void serialize(Archive & ar) + { + ar(CEREAL_NVP(x)); + ar(CEREAL_NVP(y)); + ar(CEREAL_NVP(t1)); + ar(CEREAL_NVP(t2)); + ar(CEREAL_NVP(t3)); + ar(CEREAL_NVP(t4)); + ar(CEREAL_NVP(s)); + } + + bool operator==(Everything const & o) + { + return + x == o.x && + y == o.y && + t1.a == o.t1.a && + t2.a == o.t2.a && + t3.a == o.t3.a && + t4.a == o.t4.a && + s == o.s; + } +}; + +struct EmptyStruct +{ + template + void serialize(Archive &) + { + std::cout << "Side effects!" << std::endl; + } +}; + +struct NonEmptyStruct +{ + int x, y, z; +}; + +struct NoDefaultCtor +{ +private: + NoDefaultCtor() {}; + int z; + NoDefaultCtor( int x, bool ) :y(x) {} +public: + NoDefaultCtor(int x) : y(x) + { } + + friend class cereal::access; + + int y; + + template + void serialize( Archive & ar ) + { + ar( y ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int yy; + ar( yy ); + construct( yy, true ); + construct->z = 33; + construct.ptr()->z = 33; + } +}; + +//namespace cereal +//{ +// template <> +// struct LoadAndConstruct +// { +// template +// static void load_and_construct( Archive & ar, cereal::construct & construct ) +// { +// int y; +// ar( y ); +// construct( y ); +// } +// }; +//} + +struct unordered_naming +{ + int x; + int y; + int z; + + template + void save( Archive & ar ) const + { + ar( CEREAL_NVP(x), + CEREAL_NVP(z), + CEREAL_NVP(y) ); + } + + template + void load( Archive & ar ) + { + ar( x, + CEREAL_NVP(y), + CEREAL_NVP(z) ); + } + + bool operator==( unordered_naming const & other ) const + { + return x == other.x && y == other.y && z == other.z; + } +}; + +std::ostream& operator<<(std::ostream& os, unordered_naming const & s) +{ + os << "[x: " << s.x << " y: " << s.y << " z: " << s.z << "]"; + return os; +} + +template +void test_unordered_loads() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rngI = [](){ return 1; }; + auto rngF = [](){ return 2.0f; }; + auto rngD = [](){ return 3.2; }; + + for(int i=0; i<100; ++i) + { + auto const name1 = "1"; + auto const name2 = "2"; + auto const name3 = "3"; + auto const name4 = "4"; + auto const name5 = "5"; + auto const name6 = "6"; + auto const name7 = "7"; + + int o_int1 = rngI(); + double o_double2 = rngD(); + std::vector o_vecbool3 = { true, false, true, false, true }; + int o_int4 = rngI(); + int o_int5 = rngI(); + int o_int6 = rngI(); + std::pair o_un7; + o_un7.first = rngF(); + o_un7.second.x = rngI(); + o_un7.second.y = rngI(); + o_un7.second.z = rngI(); + + { + std::ofstream os("test.xml"); + OArchive oar(os); + + oar( cereal::make_nvp( name1, o_int1 ), + cereal::make_nvp( name2, o_double2 ), + cereal::make_nvp( name3, o_vecbool3 ), + cereal::make_nvp( name4, o_int4 ), + cereal::make_nvp( name5, o_int5 ), + cereal::make_nvp( name6, o_int6 ), + cereal::make_nvp( name7, o_un7 ) ); + } + + decltype(o_int1) i_int1; + decltype(o_double2) i_double2; + decltype(o_vecbool3) i_vecbool3; + decltype(o_int4) i_int4; + decltype(o_int5) i_int5; + decltype(o_int6) i_int6; + decltype(o_un7) i_un7; + + std::ifstream is("test.xml"); + { + IArchive iar(is); + + iar( cereal::make_nvp( name7, o_un7 ), + cereal::make_nvp( name2, i_double2 ), + cereal::make_nvp( name4, i_int4 ), + cereal::make_nvp( name3, i_vecbool3 ), + cereal::make_nvp( name1, i_int1 ), + cereal::make_nvp( name5, i_int5 ), + i_int6 ); + } + } +} + +class BoostTransitionMS +{ + public: + BoostTransitionMS() {} + BoostTransitionMS( int xx ) : x(xx) {} + + int getX(){ return x; } + void setX( int xx ){ x = xx; } + + private: + friend class cereal::access; + int x; + + template + void serialize( Archive & ar, const std::uint32_t /*version*/ ) + { ar( x ); } +}; + +class BoostTransitionSplit +{ + public: + BoostTransitionSplit() {} + BoostTransitionSplit( int xx ) : x(xx) {} + + int getX(){ return x; } + void setX( int xx ){ x = xx; } + + private: + friend class cereal::access; + int x; + + template + void save( Archive & ar, const std::uint32_t /*version*/ ) const + { ar( x ); } + + template + void load( Archive & ar, const std::uint32_t /*version*/ ) + { ar( x ); } +}; + +class BoostTransitionNMS +{ + public: + BoostTransitionNMS() {} + BoostTransitionNMS( int xx ) : x(xx) {} + + int x; +}; + +template +void serialize( Archive & ar, BoostTransitionNMS & bnms, const std::uint32_t version ) +{ ar( bnms.x ); std::cout << "NMS version: " << version << std::endl; } + +struct BoostTransitionNMSplit +{ + public: + BoostTransitionNMSplit() {} + BoostTransitionNMSplit( int xx ) : x(xx) {} + + int x; +}; + +template +void save( Archive & ar, BoostTransitionNMSplit const & bnsplit, const std::uint32_t version ) +{ ar( bnsplit.x ); std::cout << "NMsave version: " << version << std::endl; } + +template +void load( Archive & ar, BoostTransitionNMSplit & bnsplit, const std::uint32_t version ) +{ ar( bnsplit.x ); std::cout << "NMload version: " << version << std::endl; } + +// ###################################################################### +int main() +{ + std::cout << std::boolalpha << std::endl; + + Everything e_out; + e_out.x = 99; + e_out.y = 100; + e_out.t1 = {1}; + e_out.t2 = {2}; + e_out.t3 = {3}; + e_out.t4 = {4}; + e_out.s = "Hello, World!"; + std::unique_ptr nodefault( new NoDefaultCtor( 3 ) ); + + Test2 t2 = {22}; + + { + std::ofstream os("out.txt", std::ios::binary); + cereal::BinaryOutputArchive archive(os); + archive(CEREAL_NVP(e_out)); + archive(t2); + archive(nodefault); + } + + Everything e_in; + + std::unique_ptr nodefaultin( new NoDefaultCtor( 1 ) ); + + { + std::ifstream is("out.txt", std::ios::binary); + cereal::BinaryInputArchive archive(is); + archive(CEREAL_NVP(e_in)); + archive(t2); + archive(nodefaultin); + std::remove("out.txt"); + } + + assert(e_in == e_out); + assert(nodefault->y == nodefaultin->y); + + { + cereal::BinaryOutputArchive archive(std::cout); + int xxx[] = {-1, 95, 3}; + archive( xxx ); + + cereal::XMLOutputArchive archive2(std::cout, cereal::XMLOutputArchive::Options(std::numeric_limits::max_digits10, true, true)); + archive2( xxx ); + + std::vector yyy = {1, 2, 3}; + archive2( yyy ); + + archive2.saveBinaryValue( xxx, sizeof(int)*3 ); + } + + { + std::ofstream os("out.xml"); + cereal::XMLOutputArchive oar( os ); + //cereal::XMLOutputArchive oar( std::cout ); + + oar( cereal::make_nvp("hello", 5 ) ); + + std::string bla("bla"); + oar( bla ); + + auto intptr = std::make_shared(99); + oar( CEREAL_NVP(intptr) ); + + std::map map1 = + { + {"one", 1}, + {"two", 2}, + {"three", 3} + }; + + oar( CEREAL_NVP(map1) ); + + int x = 3; + oar( CEREAL_NVP(x) ); + oar( 5 ); + oar( 3.3 ); + oar( 3.2f ); + oar( true ); + + std::array arr = {{1, 2, 3, 4, 5}}; + oar( arr ); + + std::vector vec = {"hey", + "there", + "buddy"}; + + std::vector> vec2 = {vec, vec, vec}; + + oar( cereal::make_nvp("EVERYTHING", e_out) ); + oar( vec ); + oar( vec2 ); + + int xxx[] = {-1, 95, 3}; + oar.saveBinaryValue( xxx, sizeof(int)*3, "xxxbinary" ); + //oar.saveBinaryValue( xxx, sizeof(int)*3 ); + + std::unique_ptr d1( new Derived(3, 4) ); + std::unique_ptr d2( new Derived(4, 5) ); + std::shared_ptr d3( new Derived(5, 6) ); + oar( d1 ); + oar( d2 ); + oar( d3 ); + } + + { + std::ifstream is("out.xml"); + cereal::XMLInputArchive iar( is ); + + int hello; + iar( cereal::make_nvp("hello", hello) ); + assert( hello == 5 ); + + std::string bla; + iar( bla ); + assert( bla == "bla" ); + + std::shared_ptr intptr; + iar( CEREAL_NVP(intptr) ); + assert( *intptr == 99 ); + + std::map map1; + + iar( CEREAL_NVP(map1) ); + assert( map1["one"] == 1 ); + assert( map1["two"] == 2 ); + assert( map1["three"] == 3 ); + + + int x; + iar( CEREAL_NVP(x) ); + assert( x == 3 ); + + int x5; + iar( x5 ); + assert( x5 == 5 ); + + double x33; + iar( x33 ); + assert( x33 == 3.3 ); + + float x32; + iar( x32 ); + assert( x32 == 3.2f ); + + bool xtrue; + iar( xtrue ); + assert( xtrue == true ); + + std::array arr; + iar( arr ); + for( int i = 0; i < 5; ++i ) + assert( arr[i] == (i+1) ); + + Everything e; + iar( cereal::make_nvp("EVERYTHING", e) ); + assert( e == e_out ); + + std::vector vec; + iar( vec ); + assert( vec[0] == "hey" ); + assert( vec[1] == "there" ); + assert( vec[2] == "buddy" ); + + std::vector> vec2; + iar( vec2 ); + for( auto & v : vec2 ) + { + assert( v[0] == "hey" ); + assert( v[1] == "there" ); + assert( v[2] == "buddy" ); + } + + int xxx[3]; + iar.loadBinaryValue( xxx, sizeof(int)*3 ); + assert( xxx[0] == -1 ); + assert( xxx[1] == 95 ); + assert( xxx[2] == 3 ); + + std::unique_ptr d1; + std::unique_ptr d2; + std::shared_ptr d3; + + iar( d1 ); + assert( d1->x == 4 && d1->y == 3 ); + iar( d2 ); + assert( dynamic_cast(d2.get())->x == 5 && dynamic_cast(d2.get())->y == 4 ); + iar( d3 ); + assert( dynamic_cast(d3.get())->x == 6 && dynamic_cast(d3.get())->y == 5 ); + } + + { + std::ofstream b("endian.out", std::ios::binary); + cereal::PortableBinaryOutputArchive oar(b); + + bool bb = true; + char a = 'a'; + int x = 1234; + float y = 1.324f; + double z = 3.1452; + long double d = 1.123451234512345; + long long j = 2394873298472343; + + oar( bb, a, x, y, z, d, j ); + std::cout << bb << " " << a << " " << x << " " << y << " " << z << " " << d << " " << j << std::endl; + // valgrind will complain about uninitialized bytes here - seems to be the padding caused by the long double and + // long long allocations (this padding just exists on the stack and is never used anywhere) + // see https://bugs.kde.org/show_bug.cgi?id=197915 + } + { + std::ifstream b("endian.out", std::ios::binary); + cereal::PortableBinaryInputArchive iar(b); + + bool bb; + char a; + int x; + float y; + double z; + long double d; + long long j; + + iar( bb, a, x, y, z, d, j ); + + std::cout << bb << " " << a << " " << x << " " << y << " " << z << " " << d << " " << j << std::endl; + + std::remove("endian.out"); + } + + { + std::ofstream ss("xml_ordering.out"); + cereal::XMLOutputArchive ar(ss); + + double one = 1; + double two = 2; + double three = 3; + std::vector four = {1, 2, 3, 4}; + + // Output is ordered 3 2 1 4 + ar( three, CEREAL_NVP(two), one, cereal::make_nvp("five", four) ); + } + + { + std::ifstream ss("xml_ordering.out"); + cereal::XMLInputArchive ar(ss); + + // Output prodered out of order, try to load in order 1 2 3 4 + double one; + double two; + double three; + std::vector four; + + ar( one ); // cereal can only give warnings if you used an NVP! + ar( CEREAL_NVP( two ) ); + ar( three ); + + try + { + ar( CEREAL_NVP( three ) ); + } + catch( cereal::Exception const & e ) + { + std::cout << e.what() << std::endl; + std::cout << "Looked for three but we didn't use an NVP when saving" << std::endl; + } + ar( cereal::make_nvp("five", four) ); + ar( cereal::make_nvp("five", four) ); // do it a second time since it shouldn't matter as we provide the name + + std::cout << one << std::endl; + std::cout << two << std::endl; + std::cout << three << std::endl; + for( auto i : four ) std::cout << i << " "; + std::cout << std::endl; + } + + { + // Boost transition layer stuff + std::ofstream ss("cereal_version.out"); + cereal::XMLOutputArchive ar(ss); + + BoostTransitionMS b(3); + ar( b, b ); + + BoostTransitionSplit c(4); + ar( c, c ); + + BoostTransitionNMS d(5); + ar( d, d ); + + BoostTransitionNMSplit e(32); + ar( e, e ); + } + + { + // Boost transition layer stuff + std::ifstream ss("cereal_version.out"); + cereal::XMLInputArchive ar(ss); + + BoostTransitionMS b; + ar( b ); + assert( b.getX() == 3 ); + b.setX( 0 ); + ar( b ); + assert( b.getX() == 3 ); + + BoostTransitionSplit c; + ar( c ); + assert( c.getX() == 4 ); + c.setX( 0 ); + ar( c ); + assert( c.getX() == 4 ); + + BoostTransitionNMS d; + ar( d ); + assert( d.x == 5 ); + d.x = 0; + ar( d ); + assert( d.x == 5 ); + + BoostTransitionNMSplit e; + ar( e ); + assert( e.x == 32 ); + e.x = 0; + ar( e ); + assert( e.x == 32 ); + } + +#ifdef CEREAL_FUTURE_EXPERIMENTAL + { + // Any testing + int x = 32; + int * xx = &x; + std::string y("hello"); + cereal::detail::Any a(xx); + auto b = a; + + std::cout << *((int *)a) << std::endl; + *((int*)a) = 44; + std::cout << *((int *)b) << std::endl; + std::cout << *((int *)a) << std::endl; + + a = cereal::detail::Any(y); + std::string a_out = a; + std::cout << a_out << std::endl; + } +#endif // CEREAL_FUTURE_EXPERIMENTAL + + return 0; +} + +CEREAL_CLASS_VERSION(BoostTransitionMS, 1) +CEREAL_CLASS_VERSION(BoostTransitionSplit, 2) +CEREAL_CLASS_VERSION(BoostTransitionNMS, 3) +// keep the other at default version (0) diff --git a/tpl/cereal/sandbox/sandbox_json.cpp b/tpl/cereal/sandbox/sandbox_json.cpp new file mode 100644 index 0000000..e767ca5 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_json.cpp @@ -0,0 +1,446 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// ################################### +struct Test1 +{ + int a; + + private: + friend class cereal::access; + template + void serialize(Archive & ar) + { + ar(CEREAL_NVP(a)); + } +}; + +// ################################### +class Test2 +{ + public: + Test2() {} + Test2( int x ) : a( x ) {} + int a; + + private: + friend class cereal::access; + + template + void save(Archive & ar) const + { + ar(a); + } + + template + void load(Archive & ar) + { + ar(a); + } +}; + +// ################################### +struct Test3 +{ + int a; +}; + +template +void serialize(Archive & ar, Test3 & t) +{ + ar(CEREAL_NVP(t.a)); +} + +namespace test4 +{ + // ################################### + struct Test4 + { + int a; + }; + + template + void save(Archive & ar, Test4 const & t) + { + ar(CEREAL_NVP(t.a)); + } + + template + void load(Archive & ar, Test4 & t) + { + ar(CEREAL_NVP(t.a)); + } +} + +class Private +{ + public: + Private() : a('z') {} + + private: + char a; + + friend class cereal::access; + + template + void serialize(Archive & ar) + { + ar(a); + } +}; + +struct Everything +{ + int x; + int y; + Test1 t1; + Test2 t2; + Test3 t3; + test4::Test4 t4; + std::string s; + + template + void serialize(Archive & ar) + { + ar(CEREAL_NVP(x)); + ar(CEREAL_NVP(y)); + ar(CEREAL_NVP(t1)); + ar(CEREAL_NVP(t2)); + ar(CEREAL_NVP(t3)); + ar(CEREAL_NVP(t4)); + ar(CEREAL_NVP(s)); + } + + bool operator==(Everything const & o) + { + return + x == o.x && + y == o.y && + t1.a == o.t1.a && + t2.a == o.t2.a && + t3.a == o.t3.a && + t4.a == o.t4.a && + s == o.s; + } +}; + + +struct SubFixture +{ + SubFixture() : a( 3 ), + b( 9999 ), + c( 100.1f ), + d( 2000.9 ), + s( "hello, world!" ) + {} + + int a; + uint64_t b; + float c; + double d; + std::string s; + + template + void serialize(Archive & ar) + { + ar( CEREAL_NVP(a), + b, + c, + CEREAL_NVP(d), + CEREAL_NVP(s) ); + } + void change() + { + a = 4; + b = 4; + c = 4; + d = 4; + s = "4"; + } +}; + +struct Fixture +{ + SubFixture f1, f2, f3; + int array[4]; + + Fixture() + { + array[0] = 1; + array[1] = 2; + array[2] = 3; + array[3] = 4; + } + + template + void save(Archive & ar) const + { + ar( f1, + CEREAL_NVP(f2), + f3 ); + ar.saveBinaryValue( array, sizeof(int)*4, "cool array man" ); + } + + template + void load(Archive & ar) + { + ar( f1, + CEREAL_NVP(f2), + f3 ); + ar.loadBinaryValue( array, sizeof(int)*4 ); + } + + void change() + { + f1.change(); + f2.change(); + f3.change(); + } +}; + +struct AAA +{ + AAA() : one( 1 ), two( 2 ), three( { {1, 2, 3}, { 4, 5, 6 }, {} } ) {} + int one, two; + + std::vector> three; + + template + void serialize(Archive & ar) + { + ar( CEREAL_NVP(one), CEREAL_NVP(two) ); + //ar( CEREAL_NVP(three) ); + } +}; + +class Stuff +{ + public: + Stuff() {} + + void fillData() + { + std::vector> t1{ {0, -1.0f}, + { 0, -2.9932f }, + { 0, -3.5f } }; + std::vector> t2{ {1.0f, 0}, + { 2.2f, 0 }, + { 3.3f, 0 } }; + data["imaginary"] = t1; + data["real"] = t2; + } + + private: + std::map>> data; + + friend class cereal::access; + + template + void serialize( Archive & ar ) + { + ar( CEREAL_NVP(data) ); + } +}; + +struct OOJson +{ + OOJson() = default; + OOJson( int aa, int bb, bool cc, double dd ) : + a( aa ), b( bb ), c{ cc, dd } + { + d[0] = 0; d[1] = 1; d[2] = 2; + } + + int a; + int b; + std::pair c; + float d[3]; + + template + void serialize( Archive & ar ) + { + ar( CEREAL_NVP(c) ); + ar( CEREAL_NVP(a) ); + ar( b ); + ar( CEREAL_NVP(d) ); + } +}; + +// ###################################################################### +int main() +{ + std::cout << std::boolalpha << std::endl; + + { + std::ofstream os("file.json"); + cereal::JSONOutputArchive oar( os ); + + //auto f = std::make_shared(); + //auto f2 = f; + //oar( f ); + //oar( f2 ); + Stuff s; s.fillData(); + oar( cereal::make_nvp("best data ever", s) ); + } + + { + std::ifstream is("file.json"); + std::string str((std::istreambuf_iterator(is)), std::istreambuf_iterator()); + std::cout << "---------------------" << std::endl << str << std::endl << "---------------------" << std::endl; + } + + // playground + { + cereal::JSONOutputArchive archive( std::cout ); + bool arr[] = {true, false}; + std::vector vec = {1, 2, 3, 4, 5}; + archive( CEREAL_NVP(vec), + arr ); + auto f = std::make_shared(); + auto f2 = f; + archive( f ); + archive( f2 ); + } + + // test out of order + std::stringstream oos; + { + cereal::JSONOutputArchive ar(oos); + cereal::JSONOutputArchive ar2(std::cout, + cereal::JSONOutputArchive::Options(2, cereal::JSONOutputArchive::Options::IndentChar::space, 2) ); + + ar( cereal::make_nvp( "1", 1 ), + cereal::make_nvp( "2", 2 ), + 3, + 0, // unused + cereal::make_nvp( "4", 4 ), + cereal::make_nvp( "5", 5 ) ); + + int x = 33; + ar.saveBinaryValue( &x, sizeof(int), "bla" ); + + ar2( cereal::make_nvp( "1", 1 ), + cereal::make_nvp( "2", 2 ), + 3, + 0, // unused + cereal::make_nvp( "4", 4 ), + cereal::make_nvp( "5", 5 ) ); + ar2.saveBinaryValue( &x, sizeof(int), "bla" ); + + OOJson oo( 1, 2, true, 4.2 ); + ar( CEREAL_NVP(oo) ); + ar2( CEREAL_NVP(oo) ); + + // boost stuff + ar & cereal::make_nvp("usingop&", oo ) & 6; + ar << 5 << 4 << 3; + + ar2 & cereal::make_nvp("usingop&", oo ) & 6; + ar2 << 5 << 4 << 3; + + long double ld = std::numeric_limits::max(); + long long ll = std::numeric_limits::max(); + unsigned long long ull = std::numeric_limits::max(); + + ar( CEREAL_NVP(ld), + CEREAL_NVP(ll), + CEREAL_NVP(ull) ); + + ar2( CEREAL_NVP(ld), + CEREAL_NVP(ll), + CEREAL_NVP(ull) ); + } + + { + cereal::JSONInputArchive ar(oos); + int i1, i2, i3, i4, i5, x; + + ar( i1 ); + ar( cereal::make_nvp( "2", i2 ), i3 ); + + ar( cereal::make_nvp( "4", i4 ), + i5 ); + + ar.loadBinaryValue( &x, sizeof(int) ); + + OOJson ii; + ar( cereal::make_nvp("oo", ii) ); + ar( cereal::make_nvp( "2", i2 ) ); + + std::cout << i1 << " " << i2 << " " << i3 << " " << i4 << " " << i5 << std::endl; + std::cout << x << std::endl; + std::cout << ii.a << " " << ii.b << " " << ii.c.first << " " << ii.c.second << " "; + for( auto z : ii.d ) + std::cout << z << " "; + std::cout << std::endl; + + OOJson oo; + ar >> cereal::make_nvp("usingop&", oo ); + std::cout << oo.a << " " << oo.b << " " << oo.c.first << " " << oo.c.second << " "; + for( auto z : oo.d ) + std::cout << z << " "; + + int aa, a, b, c; + ar & aa & a & b & c; + std::cout << aa << " " << a << " " << b << " " << c << std::endl; + + long double ld; + long long ll; + unsigned long long ull; + + ar( CEREAL_NVP(ld), + CEREAL_NVP(ll), + CEREAL_NVP(ull) ); + + std::cout << (ld == std::numeric_limits::max()) << std::endl; + std::cout << (ll == std::numeric_limits::max()) << std::endl; + std::cout << (ull == std::numeric_limits::max()) << std::endl; + } + + return 0; +} diff --git a/tpl/cereal/sandbox/sandbox_rtti.cpp b/tpl/cereal/sandbox/sandbox_rtti.cpp new file mode 100644 index 0000000..896d033 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_rtti.cpp @@ -0,0 +1,231 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +struct Base +{ + int y; + virtual void foo() = 0; + + template + void save(Archive & ar) const + { + std::cout << "Saving Base" << std::endl; + ar( y ); + } + + template + void load(Archive & ar) + { + std::cout << "Loading Base" << std::endl; + ar( y ); + } +}; + +struct MyType : public Base +{ + int x; + + void foo() {} + + template + void save(Archive & ar) const + { + std::cout << "Saving MyType" << std::endl; + ar( cereal::virtual_base_class( this ) ); + } + + template + void load(Archive & ar) + { + std::cout << "Loading MyType" << std::endl; + ar( cereal::base_class( this ) ); + } +}; +CEREAL_REGISTER_TYPE(MyType) + +struct YourType : public Base +{ + YourType(int xx) : x(xx) {} + YourType() : x(-1) {} + int x; + + void foo() {} + + template + void save(Archive & ar) const + { + std::cout << "Saving YourType" << std::endl; + ar( x ); + } + + template + void load(Archive & ar) + { + std::cout << "Loading YourType" << std::endl; + ar( x ); + } +}; + +CEREAL_REGISTER_TYPE(YourType) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, YourType) + +struct OurBase +{ + virtual void foo() {} + + template + void serialize(Archive &) + { } +}; + +struct OurType : public OurBase +{ + OurType() : OurBase(), x() {} + OurType(int x_) : x(x_) {} + void foo() {} + + int x; + + template + void serialize(Archive & ar) + { + ar( x ); + } +}; + +struct BaseVirtual +{ + int x; + template + void serialize( Archive & ar ) + { ar( x ); } + virtual void foo() = 0; +}; + +struct DerivedVirtual : public virtual BaseVirtual +{ + int y; + virtual void foo() {} + + template + void save( Archive & ar ) const + { + ar( cereal::virtual_base_class( this ) ); + ar( y ); + } + + template + void load( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); + ar( y ); + } +}; + +struct TestType +{ + int x; + template + void serialize( Archive & ar ) + { + ar( x ); + } +}; + +namespace cereal +{ + template struct specialize {}; + template struct specialize {}; +} + +struct AAA +{ + virtual void foo() = 0; +}; + +struct BBB : AAA +{ + void foo() {} + template + void serialize( Archive & ) {} +}; + +CEREAL_REGISTER_TYPE(BBB) + +template void nop(T&&) {} + +int main() +{ + { + std::ofstream ostream("rtti.txt"); + //cereal::BinaryOutputArchive oarchive(ostream); + cereal::XMLOutputArchive oarchive(ostream); + + std::shared_ptr ptr1 = std::make_shared(); + std::shared_ptr ptr2 = std::make_shared(33); + std::unique_ptr ptr3(new MyType()); + std::weak_ptr ptr4 = ptr2; + + std::shared_ptr ptr5 = std::make_shared(99); + + oarchive(ptr1); + oarchive(ptr2); + oarchive(ptr3); + oarchive(ptr4); + oarchive(ptr5); + + //std::shared_ptr a = std::make_shared(); + //oarchive(a); + } + + { + std::ifstream istream("rtti.txt"); + //cereal::BinaryInputArchive iarchive(istream); + cereal::XMLInputArchive iarchive(istream); + + std::shared_ptr ptr1; + std::shared_ptr ptr2; + std::unique_ptr ptr3; + std::weak_ptr ptr4; + + std::shared_ptr ptr5; + + iarchive(ptr1); + iarchive(ptr2); + iarchive(ptr3); + iarchive(ptr4); + iarchive(ptr5); + } +} diff --git a/tpl/cereal/sandbox/sandbox_shared_lib/CMakeLists.txt b/tpl/cereal/sandbox/sandbox_shared_lib/CMakeLists.txt new file mode 100644 index 0000000..5d8ea08 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_shared_lib/CMakeLists.txt @@ -0,0 +1 @@ +add_library(sandbox_vs_dll SHARED base.cpp derived.cpp) diff --git a/tpl/cereal/sandbox/sandbox_shared_lib/base.cpp b/tpl/cereal/sandbox/sandbox_shared_lib/base.cpp new file mode 100755 index 0000000..9d8e360 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_shared_lib/base.cpp @@ -0,0 +1,9 @@ +#ifndef CEREAL_DLL_USE +#define CEREAL_DLL_MAKE +#endif +#include "base.hpp" + +template void Base::serialize + ( cereal::XMLOutputArchive & ar, std::uint32_t const version ); +template void Base::serialize + ( cereal::XMLInputArchive & ar, std::uint32_t const version ); diff --git a/tpl/cereal/sandbox/sandbox_shared_lib/base.hpp b/tpl/cereal/sandbox/sandbox_shared_lib/base.hpp new file mode 100755 index 0000000..388a015 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_shared_lib/base.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include + +#if defined (_WINDLL) +#define DECLSPECIFIER __declspec(dllexport) +#elif defined(MSC_VER) +#define DECLSPECIFIER __declspec(dllimport) +#else +#define DECLSPECIFIER +#endif + +int doit(); + +class VersionTest +{ + public: + int x; + template + void serialize( Archive & ar, const std::uint32_t /* version */ ) + { ar( x ); } +}; + +class Base +{ + public: + friend class cereal::access; + + template < class Archive > + void serialize(Archive &, std::uint32_t const) {} + virtual ~Base() {} +}; + +extern template DECLSPECIFIER void Base::serialize +( cereal::XMLInputArchive & ar, std::uint32_t const version ); + +extern template DECLSPECIFIER void Base::serialize +( cereal::XMLOutputArchive & ar, std::uint32_t const version ); + +CEREAL_CLASS_VERSION(VersionTest, 1) diff --git a/tpl/cereal/sandbox/sandbox_shared_lib/derived.cpp b/tpl/cereal/sandbox/sandbox_shared_lib/derived.cpp new file mode 100755 index 0000000..52043e2 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_shared_lib/derived.cpp @@ -0,0 +1,10 @@ +#ifndef CEREAL_DLL_USE +#define CEREAL_DLL_MAKE +#endif +#include "derived.hpp" + +template void Derived::serialize + ( cereal::XMLOutputArchive & ar, std::uint32_t const version ); + +template void Derived::serialize + ( cereal::XMLInputArchive & ar, std::uint32_t const version ); diff --git a/tpl/cereal/sandbox/sandbox_shared_lib/derived.hpp b/tpl/cereal/sandbox/sandbox_shared_lib/derived.hpp new file mode 100755 index 0000000..6f6cfb6 --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_shared_lib/derived.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "base.hpp" +class Derived : public Base +{ + private: + friend class cereal::access; + template + void serialize(Archive & ar, std::uint32_t const) + { + ar(cereal::base_class(this)); + } +}; + +extern template DECLSPECIFIER void Derived::serialize + ( cereal::XMLOutputArchive & ar, std::uint32_t const version ); +extern template DECLSPECIFIER void Derived::serialize + ( cereal::XMLInputArchive & ar, std::uint32_t const version ); + +CEREAL_REGISTER_TYPE(Derived) diff --git a/tpl/cereal/sandbox/sandbox_vs.cpp b/tpl/cereal/sandbox/sandbox_vs.cpp new file mode 100644 index 0000000..fb7fceb --- /dev/null +++ b/tpl/cereal/sandbox/sandbox_vs.cpp @@ -0,0 +1,278 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +//CEREAL_FORCE_LINK_SHARED_LIBRARY(Sandbox) + +struct Archive {}; +CEREAL_SETUP_ARCHIVE_TRAITS(Archive, Archive) + +struct Test +{ + template + void serialzize( Archive & ) + { + std::cout << "hey there" << std::endl; + } + + template + void save( Archive & ) const + { + std::cout << "saved by the bell" << std::endl; + } + + template + void load( Archive & ) + { + std::cout << "locked and loaded" << std::endl; + } + + template + static void load_and_construct( Archive &, cereal::construct & ) + { + } + + template + int save_minimal() const + { + return 0; + } + + template + int save_minimal(const std::uint32_t) const + { + return 1; + } + + template + void load_minimal( int & ) + { } +}; + +template +void serialize( Archive &, Test & ) +{ } + +template +void load( Archive &, Test & ) +{ } + +template +void save( Archive &, Test const & ) +{ } + +template +int save_minimal( Test const & ) +{ return 0; } + +template +int save_minimal( Test const &, const std::uint32_t ) +{ return 0; } + +namespace cereal +{ + template <> + struct LoadAndConstruct + { + template + static void load_and_construct( Archive &, cereal::construct & construct ) + { + construct(); + } + }; +} + +struct A +{ + virtual void foo() = 0; +}; + +struct B : A +{ + void foo() {} + + template + void serialize( Archive & ) + { + std::cout << "i'm in your b" << std::endl; + } +}; + +struct C +{ + char a; +}; + +CEREAL_REGISTER_TYPE(B) +CEREAL_REGISTER_POLYMORPHIC_RELATION(A, B) + +class MemberMinimal +{ + public: + MemberMinimal() = default; + template + int save_minimal( Archive const & ) const + { + return x; + } + + template + void load_minimal( Archive const &, int const & str ) + { + x = str; + } + + public: + int x; +}; + +int main() +{ + typedef Test T; + std::cout << std::boolalpha; + + // Test Load and Construct internal/external + std::cout << "\tload_and_construct" << std::endl; + std::cout << cereal::traits::has_member_load_and_construct::value << std::endl; + std::cout << cereal::traits::has_non_member_load_and_construct::value << std::endl; + + // serialize + std::cout << "\tserialize" << std::endl; + std::cout << cereal::traits::has_member_serialize::value << std::endl; + std::cout << cereal::traits::has_non_member_serialize::value << std::endl; + + // load + std::cout << "\tload" << std::endl; + std::cout << cereal::traits::has_member_load::value << std::endl; + std::cout << cereal::traits::has_non_member_load::value << std::endl; + + // load minimal + std::cout << "\tload minimal" << std::endl; + std::cout << cereal::traits::has_member_load::value << std::endl; + + // save + std::cout << "\tsave" << std::endl; + std::cout << cereal::traits::has_member_save::value << std::endl; + std::cout << cereal::traits::has_non_member_save::value << std::endl; + + // save_minimal + std::cout << "\tsave_minimal" << std::endl; + std::cout << cereal::traits::has_member_save_minimal::value << std::endl; + std::cout << cereal::traits::has_non_member_save_minimal::value << std::endl; + + // save_minimal_versioned + std::cout << "\tsave_minimal versioned" << std::endl; + std::cout << cereal::traits::has_member_versioned_save_minimal::value << std::endl; + std::cout << cereal::traits::has_non_member_versioned_save_minimal::value << std::endl; + + // splittable + std::cout << "\t splittable" << std::endl; + std::cout << cereal::traits::has_member_split::value << std::endl; + std::cout << cereal::traits::has_non_member_split::value << std::endl; + + // serialiable + std::cout << "\toutput serializable" << std::endl; + std::cout << cereal::traits::is_output_serializable::value << std::endl; + +#if !defined(__INTEL_COMPILER) + //! TODO: This causes icc to crash + std::cout << cereal::traits::is_input_serializable::value << std::endl; +#endif + + // specialized + std::cout << "\tspecialized" << std::endl; + std::cout << cereal::traits::detail::is_specialized_member_serialize::value << std::endl; + std::cout << cereal::traits::detail::is_specialized_member_load_save::value << std::endl; + std::cout << cereal::traits::detail::is_specialized_non_member_serialize::value << std::endl; + std::cout << cereal::traits::detail::is_specialized_non_member_load_save::value << std::endl; + std::cout << cereal::traits::detail::count_specializations::value << std::endl; + std::cout << cereal::traits::is_specialized::value << std::endl; + + // array size + std::cout << typeid(A).name() << std::endl; + std::cout << typeid(cereal::traits::has_load_and_construct).name() << std::endl; + + // extra testing + std::cout << "\textra" << std::endl; + std::cout << cereal::traits::has_member_save_minimal::value << std::endl; + std::cout << cereal::traits::has_member_load_minimal::value << std::endl; + + // DLL testing + std::cout << "------DLL TESTING------" << std::endl; + std::stringstream dllSS1; + std::stringstream dllSS2; + { + cereal::XMLOutputArchive out(dllSS1); + VersionTest x{1}; + std::shared_ptr p = std::make_shared(); + out(x); + out( p ); + + std::shared_ptr ay = std::make_shared(); + out(ay); + } + + std::cout << dllSS1.str() << std::endl; + + { + VersionTest x; + std::shared_ptr p; + std::shared_ptr ay; + { + cereal::XMLInputArchive in(dllSS1); + in(x); + in( p ); + in( ay ); + } + { + cereal::XMLOutputArchive out(dllSS2); + out( x ); + out( p ); + out( ay ); + } + } + + std::cout << dllSS2.str() << std::endl; + + return 0; +} diff --git a/tpl/cereal/scripts/add_rapidjson_prefix.sh b/tpl/cereal/scripts/add_rapidjson_prefix.sh new file mode 100755 index 0000000..c5b3980 --- /dev/null +++ b/tpl/cereal/scripts/add_rapidjson_prefix.sh @@ -0,0 +1,4 @@ +# Applies renaming within all of the rapidjson source files to add a cereal prefix +find ./../include/cereal/external/rapidjson/ -type f -name \*.h -exec sed -i "s/RAPIDJSON_/CEREAL_RAPIDJSON_/g" {} \; +echo "Remember to backport any cereal specific changes not in this version of RapidJSON!" +echo "See https://github.com/USCiLab/cereal/commits/develop/include/cereal/external/rapidjson" diff --git a/tpl/cereal/scripts/appveyor.bat b/tpl/cereal/scripts/appveyor.bat new file mode 100644 index 0000000..5e478de --- /dev/null +++ b/tpl/cereal/scripts/appveyor.bat @@ -0,0 +1,75 @@ +@echo off +setlocal enabledelayedexpansion + +if not defined APPVEYOR ( + @echo This script is meant to be used with AppVeyor CI. This can be used as reference. + @echo I sincerely recommend not using it for building/testing cereal locally. + exit /b 0 +) + +if not defined BOOST_ROOT ( + set BOOST_ROOT=C:\Libraries\boost +) +if not defined VS_VERSION_MAJOR ( + set VS_VERSION_MAJOR=14 +) +if not defined VS_VERSION_YEAR ( + if "%VS_VERSION_MAJOR%" == "12" ( + set VS_VERSION_YEAR=2013 + ) else if "%VS_VERSION_MAJOR%" == "14" ( + set VS_VERSION_YEAR=2015 + ) else ( + @echo Cannot use Visual Studio version %VS_VERSION_MAJOR% + exit /b 1 + ) +) +if not defined CMAKE_GENERATOR_PREFIX ( + set CMAKE_GENERATOR_PREFIX=Visual Studio %VS_VERSION_MAJOR% %VS_VERSION_YEAR% +) + +@rem CONFIGURATION is (one of the entries) defined in appveyor.yml +if not defined CONFIGURATION ( + set CONFIGURATION=Release +) +@rem PLATFORM is (one of the entries) defined in appveyor.yml +if "%PLATFORM%"=="x64" ( + set BIT_COUNT=64 + set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_PREFIX% Win64 +) else ( + set BIT_COUNT=32 + set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_PREFIX% +) + +set BOOST_LIBRARYDIR=%BOOST_ROOT%\lib%BIT_COUNT%-msvc-%VS_VERSION_MAJOR%.0 + +set START_DIR=%CD% + +if not exist build\NUL mkdir build +cd build + +if "%~1" == "test" ( + @rem overloading the batch script; Run tests if the first argument is `test` (without quotes). + @rem Cereal uses Boost Unit test framework. Rather than modifying the code to load boost test + @rem dll from its location OR copying the boost dlls to the directory of every test being run, + @rem we use another option Windows leaves us - modify the PATH. + for %%i in (ctest.exe) do set CTEST_EXE=%%~$PATH:i + PATH %BOOST_LIBRARYDIR% + "!CTEST_EXE!" -C %CONFIGURATION% + if %errorlevel% neq 0 exit /b %errorlevel% + goto done +) + +if "%PLATFORM%" == "x64" ( + @rem please excuse the hack - CMake is unable to produce multiarch MSVC projects + cmake -G "%CMAKE_GENERATOR_PREFIX%" -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% .. + cmake --build . --config %CONFIGURATION% --target portability_test32 + del CMakeCache.txt + rmdir /s /q CMakeFiles +) + +cmake -G "%CMAKE_GENERATOR_NAME%" -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% .. +@rem left the actual build for later - AppVeyor enables parallel jobs in a much cleaner way than msbuild + +:done +@REM go back home +cd %START_DIR% diff --git a/tpl/cereal/scripts/renameincludes.sh b/tpl/cereal/scripts/renameincludes.sh new file mode 100755 index 0000000..acc2fa2 --- /dev/null +++ b/tpl/cereal/scripts/renameincludes.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +destdir="include_renamed" + +echo -n "New prefix: " +read newprefix + +cp -r include ${destdir} + +newprefix=$(echo ${newprefix} | sed -e 's/\//\\\//') + +find ${destdir} -name '*.hpp' -exec sed -i "s/#include + -P "${CMAKE_CURRENT_SOURCE_DIR}/run_portability_test.cmake") + + elseif(MSVC) + add_executable(portability_test32 portability_test.cpp) + endif() +endif() + +# Build all of the non-special tests +foreach(TEST_SOURCE ${TESTS}) + + string(REPLACE ".cpp" "" TEST_TARGET "${TEST_SOURCE}") + set(TEST_TARGET "test_${TEST_TARGET}") + + # Check to see if our target is listed in "SPECIAL_TESTS" + list(FIND SPECIAL_TESTS "${TEST_SOURCE}" IS_SPECIAL_TEST) + + if(IS_SPECIAL_TEST EQUAL -1) + + add_executable(${TEST_TARGET} ${TEST_SOURCE}) + target_link_libraries(${TEST_TARGET} ${CEREAL_THREAD_LIBS}) + add_test("${TEST_TARGET}" "${TEST_TARGET}") + + # If we are on a 64-bit machine, create an extra 32-bit version of the test if portability testing is enabled + if((NOT MSVC) AND (${CMAKE_SIZEOF_VOID_P} EQUAL 8) AND (NOT SKIP_PORTABILITY_TEST)) + add_executable(${TEST_TARGET}_32 ${TEST_SOURCE}) + set_target_properties(${TEST_TARGET}_32 PROPERTIES + COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + add_test("${TEST_TARGET}_32" "${TEST_TARGET}_32") + endif() + + endif() + +endforeach() + +# Add the valgrind target +if(NOT MSVC) + add_custom_target(valgrind + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/run_valgrind.sh") + + # Add the coverage target + add_custom_target(coverage) + add_custom_command(TARGET coverage + COMMAND "${CMAKE_SOURCE_DIR}/scripts/updatecoverage.sh" ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/coverage") + + # add tests to coverage + foreach(TEST_SOURCE ${TESTS}) + string(REPLACE ".cpp" "" COVERAGE_TARGET "${TEST_SOURCE}") + set(COVERAGE_TARGET "coverage_${COVERAGE_TARGET}") + + # Check to see if our target is listed in "SPECIAL_TESTS" + list(FIND SPECIAL_TESTS "${TEST_SOURCE}" IS_SPECIAL_TEST) + + if(IS_SPECIAL_TEST EQUAL -1) + add_dependencies(coverage ${COVERAGE_TARGET}) + + add_executable(${COVERAGE_TARGET} EXCLUDE_FROM_ALL ${TEST_SOURCE}) + set_target_properties(${COVERAGE_TARGET} PROPERTIES COMPILE_FLAGS "-coverage") + set_target_properties(${COVERAGE_TARGET} PROPERTIES LINK_FLAGS "-coverage") + set_target_properties(${COVERAGE_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/coverage") + target_link_libraries(${COVERAGE_TARGET} ${CEREAL_THREAD_LIBS}) + endif() + endforeach() +endif(NOT MSVC) + +if(NOT CMAKE_VERSION VERSION_LESS 3.0) + add_test(test_cmake_config_module ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake-config-module.cmake) +endif() diff --git a/tpl/cereal/unittests/array.cpp b/tpl/cereal/unittests/array.cpp new file mode 100644 index 0000000..5cd1c39 --- /dev/null +++ b/tpl/cereal/unittests/array.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "array.hpp" + +TEST_SUITE("array"); + +TEST_CASE("binary_array") +{ + test_array(); +} + +TEST_CASE("portable_binary_array") +{ + test_array(); +} + +TEST_CASE("xml_array") +{ + test_array(); +} + +TEST_CASE("json_array") +{ + test_array(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/array.hpp b/tpl/cereal/unittests/array.hpp new file mode 100644 index 0000000..766225b --- /dev/null +++ b/tpl/cereal/unittests/array.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_ARRAY_H_ +#define CEREAL_TEST_ARRAY_H_ +#include "common.hpp" + +template inline +void test_array() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::array o_podarray; + for(auto & elem : o_podarray) + elem = random_value(gen); + + std::array o_iserarray; + for(auto & elem : o_iserarray) + elem = StructInternalSerialize( random_value(gen), random_value(gen) ); + + std::array o_isplarray; + for(auto & elem : o_isplarray) + elem = StructInternalSplit( random_value(gen), random_value(gen) ); + + std::array o_eserarray; + for(auto & elem : o_eserarray) + elem = StructExternalSerialize( random_value(gen), random_value(gen) ); + + std::array o_esplarray; + for(auto & elem : o_esplarray) + elem = StructExternalSplit( random_value(gen), random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podarray); + oar(o_iserarray); + oar(o_isplarray); + oar(o_eserarray); + oar(o_esplarray); + } + + std::array i_podarray; + std::array i_iserarray; + std::array i_isplarray; + std::array i_eserarray; + std::array i_esplarray; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podarray); + iar(i_iserarray); + iar(i_isplarray); + iar(i_eserarray); + iar(i_esplarray); + } + + check_collection( i_podarray, o_podarray ); + check_collection( i_iserarray, o_iserarray ); + check_collection( i_isplarray, o_isplarray ); + check_collection( i_eserarray, o_eserarray ); + check_collection( i_esplarray, o_esplarray ); + } +} + +#endif // CEREAL_TEST_ARRAY_H_ diff --git a/tpl/cereal/unittests/basic_string.cpp b/tpl/cereal/unittests/basic_string.cpp new file mode 100644 index 0000000..245530e --- /dev/null +++ b/tpl/cereal/unittests/basic_string.cpp @@ -0,0 +1,171 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "basic_string.hpp" + +TEST_SUITE("basic_string"); + +TEST_CASE("binary_string") +{ + test_string_all(); +} + +TEST_CASE("portable_binary_string") +{ + test_string_all(); +} + +TEST_CASE("xml_string_basic") +{ + test_string_basic(); +} + +TEST_CASE("json_string_basic") +{ + test_string_basic(); +} + +template +void test_ws_in_out(Out const & o_value_with_ws) +{ + std::ostringstream os; + { + OArchive oar(os); + oar(o_value_with_ws); + } + + In i_value_with_ws; + + std::istringstream is(os.str()); + { + IArchive iar(is); + iar(i_value_with_ws); + } + + CHECK(i_value_with_ws == o_value_with_ws); +} + +TEST_CASE("xml_string_issue109") +{ + char strings[][20] = { + "some text", + "some text ", + " some text", + " some text ", + " ", + " text ", + " ]]> ", + " > > ]]> ", + " < <]>] < ", + " & & " + }; + + for( size_t i=0; i<( sizeof( strings ) / sizeof( strings[0] ) ); ++i ) + { + std::basic_string o_string = strings[i]; + + test_ws_in_out( o_string ); + } +} + +TEST_CASE("xml_char_issue109") +{ + uint8_t chars[] = { + ' ', + '\t', + '\n', + '\r', + '&', + '>', + '<', + '\'', + '"', + '!', + '|' + }; + + for( size_t i=0; i<( sizeof( chars ) / sizeof( chars[0] ) ); ++i ) + { + test_ws_in_out( chars[i] ); + } + + for( size_t i=0; i<( sizeof( chars ) / sizeof( chars[0] ) ); ++i ) + { + test_ws_in_out( int8_t( chars[i] ) ); + } + + for( size_t i=0; i<( sizeof( chars ) / sizeof( chars[0] ) ); ++i ) + { + test_ws_in_out( char( chars[i] ) ); + } +} + +template +void test_ws_in_out_array(Out const (&o_a_value_with_ws)[Nb]) +{ + std::ostringstream os; + { + OArchive oar(os); + for (const auto& o_value_with_ws : o_a_value_with_ws) + { + oar(o_value_with_ws); + } + } + + In i_a_value_with_ws[Nb]; + + std::istringstream is(os.str()); + { + IArchive iar(is); + for (In& i_value_with_ws : i_a_value_with_ws) + { + iar(i_value_with_ws); + } + } + + for (size_t uiIndex = 0; uiIndex < Nb; ++uiIndex) + { + CHECK(i_a_value_with_ws[uiIndex] == o_a_value_with_ws[uiIndex]); + } +} + +TEST_CASE("xml_string_issue_consecutive_calls") +{ + std::string strings[] = { + "some text", + " some text", + " some text ", + "Long text without ws at the end", + "some text ", + " some text", + " some text ", + }; + + test_ws_in_out_array(strings); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/basic_string.hpp b/tpl/cereal/unittests/basic_string.hpp new file mode 100644 index 0000000..5ee37aa --- /dev/null +++ b/tpl/cereal/unittests/basic_string.hpp @@ -0,0 +1,114 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_BASIC_STRING_H_ +#define CEREAL_TEST_BASIC_STRING_H_ +#include "common.hpp" + +template inline +void test_string_basic() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(size_t i=0; i<100; ++i) + { + std::basic_string o_string = random_basic_string(gen); + std::basic_string o_string2 = ""; + std::basic_string o_string3; + + std::ostringstream os; + { + OArchive oar(os); + oar(o_string); + oar(o_string2); + oar(o_string3); + } + + std::basic_string i_string; + std::basic_string i_string2; + std::basic_string i_string3; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_string); + iar(i_string2); + iar(i_string3); + } + + CHECK_EQ(i_string, o_string); + CHECK_EQ(i_string2, o_string2); + CHECK_EQ(i_string3, o_string3); + } +} + +template inline +void test_string_all() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(size_t i=0; i<100; ++i) + { + std::basic_string o_string = random_basic_string(gen); + std::basic_string o_wstring = random_basic_string(gen); + std::basic_string o_u16string = random_basic_string(gen); + std::basic_string o_u32string = random_basic_string(gen); + + std::ostringstream os; + { + OArchive oar(os); + oar(o_string); + oar(o_wstring); + oar(o_u16string); + oar(o_u32string); + } + + std::basic_string i_string; + std::basic_string i_wstring; + std::basic_string i_u16string; + std::basic_string i_u32string; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_string); + iar(i_wstring); + iar(i_u16string); + iar(i_u32string); + } + + CHECK_EQ(i_string, o_string); + check_collection( i_wstring, o_wstring ); + check_collection( i_u16string, o_u16string ); + check_collection( i_u32string, o_u32string ); + } +} + +#endif // CEREAL_TEST_BASIC_STRING_H_ diff --git a/tpl/cereal/unittests/bitset.cpp b/tpl/cereal/unittests/bitset.cpp new file mode 100644 index 0000000..912c22c --- /dev/null +++ b/tpl/cereal/unittests/bitset.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "bitset.hpp" + +TEST_SUITE("bitset"); + +TEST_CASE("binary_bitset") +{ + test_bitset(); +} + +TEST_CASE("portable_binary_bitset") +{ + test_bitset(); +} + +TEST_CASE("xml_bitset") +{ + test_bitset(); +} + +TEST_CASE("json_bitset") +{ + test_bitset(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/bitset.hpp b/tpl/cereal/unittests/bitset.hpp new file mode 100644 index 0000000..09f597c --- /dev/null +++ b/tpl/cereal/unittests/bitset.hpp @@ -0,0 +1,87 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_BITSET_H_ +#define CEREAL_TEST_BITSET_H_ +#include "common.hpp" + +template inline +void test_bitset() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rng32 = [&](){ return random_binary_string<32>( gen ); }; + auto rng65 = [&](){ return random_binary_string<65>( gen ); }; + auto rng256 = [&](){ return random_binary_string<256>( gen ); }; + auto rng512 = [&](){ return random_binary_string<512>( gen ); }; + + for(int ii=0; ii<100; ++ii) + { + std::bitset<32> o_bit32( rng32() ); + std::bitset<65> o_bit65( rng65() ); + std::bitset<256> o_bit256( rng256() ); + std::bitset<512> o_bit512( rng512() ); + std::bitset<32> o_bit32_low( 0 ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_bit32); + oar(o_bit65); + oar(o_bit256); + oar(o_bit512); + oar(o_bit32_low); + } + + std::bitset<32> i_bit32; + std::bitset<65> i_bit65; + std::bitset<256> i_bit256; + std::bitset<512> i_bit512; + std::bitset<32> i_bit32_low( 0xffffffff ); + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_bit32); + iar(i_bit65); + iar(i_bit256); + iar(i_bit512); + iar(i_bit32_low); + } + + CHECK_EQ( o_bit32, i_bit32 ); + CHECK_EQ( o_bit65, i_bit65 ); + CHECK_EQ( o_bit256, i_bit256 ); + CHECK_EQ( o_bit512, i_bit512 ); + + CHECK_EQ( o_bit32_low, i_bit32_low ); + } +} + +#endif // CEREAL_TEST_BITSET_H_ diff --git a/tpl/cereal/unittests/boost_variant.cpp b/tpl/cereal/unittests/boost_variant.cpp new file mode 100644 index 0000000..7e073b8 --- /dev/null +++ b/tpl/cereal/unittests/boost_variant.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2015, Kyle Fleming + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "boost_variant.hpp" + +TEST_SUITE("boost_variant"); + +TEST_CASE("binary_boost_variant") +{ + test_boost_variant(); +} + +TEST_CASE("portable_binary_boost_variant") +{ + test_boost_variant(); +} + +TEST_CASE("xml_boost_variant") +{ + test_boost_variant(); +} + +TEST_CASE("json_boost_variant") +{ + test_boost_variant(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/boost_variant.hpp b/tpl/cereal/unittests/boost_variant.hpp new file mode 100644 index 0000000..bedb6a8 --- /dev/null +++ b/tpl/cereal/unittests/boost_variant.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2015, Kyle Fleming + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_BOOST_VARIANT_H_ +#define CEREAL_TEST_BOOST_VARIANT_H_ + +#include "common.hpp" +#include + +template inline +void test_boost_variant() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + boost::variant o_bv1 = random_value(gen); + boost::variant o_bv2 = random_value(gen); + boost::variant o_bv3 = random_basic_string(gen); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_bv1); + oar(o_bv2); + oar(o_bv3); + } + + decltype(o_bv1) i_bv1; + decltype(o_bv2) i_bv2; + decltype(o_bv3) i_bv3; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_bv1); + iar(i_bv2); + iar(i_bv3); + } + + CHECK_EQ( boost::get(i_bv1), boost::get(o_bv1) ); + CHECK_EQ( boost::get(i_bv2), doctest::Approx(boost::get(o_bv2)).epsilon(1e-5) ); + CHECK_EQ( boost::get(i_bv3), boost::get(o_bv3) ); +} + +#endif // CEREAL_TEST_BOOST_VARIANT_H_ diff --git a/tpl/cereal/unittests/chrono.cpp b/tpl/cereal/unittests/chrono.cpp new file mode 100644 index 0000000..b175dec --- /dev/null +++ b/tpl/cereal/unittests/chrono.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "chrono.hpp" + +TEST_SUITE("chrono"); + +TEST_CASE("binary_chrono") +{ + test_chrono(); +} + +TEST_CASE("portable_binary_chrono") +{ + test_chrono(); +} + +TEST_CASE("xml_chrono") +{ + test_chrono(); +} + +TEST_CASE("json_chrono") +{ + test_chrono(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/chrono.hpp b/tpl/cereal/unittests/chrono.hpp new file mode 100644 index 0000000..3e66623 --- /dev/null +++ b/tpl/cereal/unittests/chrono.hpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_CHRONO_H_ +#define CEREAL_TEST_CHRONO_H_ + +#include "common.hpp" + +template inline +void test_chrono() +{ + for(int ii=0; ii<100; ++ii) + { + auto o_timePoint1 = std::chrono::system_clock::now(); + #ifndef CEREAL_OLDER_GCC + auto o_timePoint2 = std::chrono::steady_clock::now(); + #endif // CEREAL_OLDER_GCC + auto o_timePoint3 = std::chrono::high_resolution_clock::now(); + + auto o_duration1 = std::chrono::system_clock::now() - o_timePoint1; + #ifndef CEREAL_OLDER_GCC + auto o_duration2 = std::chrono::steady_clock::now() - o_timePoint2; + #endif // CEREAL_OLDER_GCC + auto o_duration3 = std::chrono::high_resolution_clock::now() - o_timePoint3; + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_timePoint1); + #ifndef CEREAL_OLDER_GCC + oar(o_timePoint2); + #endif // CEREAL_OLDER_GCC + oar(o_timePoint3); + oar(o_duration1); + #ifndef CEREAL_OLDER_GCC + oar(o_duration2); + #endif // CEREAL_OLDER_GCC + oar(o_duration3); + } + + decltype(o_timePoint1) i_timePoint1; + #ifndef CEREAL_OLDER_GCC + decltype(o_timePoint2) i_timePoint2; + #endif // CEREAL_OLDER_GCC + decltype(o_timePoint3) i_timePoint3; + decltype(o_duration1) i_duration1; + #ifndef CEREAL_OLDER_GCC + decltype(o_duration2) i_duration2; + #endif // CEREAL_OLDER_GCC + decltype(o_duration3) i_duration3; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_timePoint1); + #ifndef CEREAL_OLDER_GCC + iar(i_timePoint2); + #endif // CEREAL_OLDER_GCC + iar(i_timePoint3); + iar(i_duration1); + #ifndef CEREAL_OLDER_GCC + iar(i_duration2); + #endif // CEREAL_OLDER_GCC + iar(i_duration3); + } + + CHECK_EQ( o_timePoint1, i_timePoint1 ); + #ifndef CEREAL_OLDER_GCC + CHECK_EQ( o_timePoint2, i_timePoint2 ); + #endif // CEREAL_OLDER_GCC + CHECK_EQ( o_timePoint3, i_timePoint3 ); + CHECK_EQ( o_duration1, i_duration1 ); + #ifndef CEREAL_OLDER_GCC + CHECK_EQ( o_duration2, i_duration2 ); + #endif // CEREAL_OLDER_GCC + CHECK_EQ( o_duration3, i_duration3 ); + } +} + +#endif // CEREAL_TEST_CHRONO_H_ diff --git a/tpl/cereal/unittests/cmake-config-module.cmake b/tpl/cereal/unittests/cmake-config-module.cmake new file mode 100644 index 0000000..1b2f464 --- /dev/null +++ b/tpl/cereal/unittests/cmake-config-module.cmake @@ -0,0 +1,123 @@ +if(CMAKE_VERSION LESS 3.0) + message(FATAL_ERROR "Cereal can't be installed with CMake < 3.0") +endif() + +get_filename_component(BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}/../build ABSOLUTE) +get_filename_component(INSTALL_DIR ${CMAKE_CURRENT_LIST_DIR}/../out ABSOLUTE) + +# cmake configure step for cereal +file(MAKE_DIRECTORY ${BINARY_DIR}/cereal) +execute_process( + COMMAND ${CMAKE_COMMAND} + -DJUST_INSTALL_CEREAL=1 + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. + WORKING_DIRECTORY ${BINARY_DIR}/cereal + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Cereal cmake configure-step failed") +endif() + +# cmake install cereal +execute_process( + COMMAND ${CMAKE_COMMAND} + --build ${BINARY_DIR}/cereal + --target install + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Cereal cmake install-step failed") +endif() + +# create test project sources +file(WRITE ${BINARY_DIR}/test_source/CMakeLists.txt " + cmake_minimum_required(VERSION ${CMAKE_VERSION}) + project(cereal-test-config-module) + if(NOT MSVC) + if(CMAKE_VERSION VERSION_LESS 3.1) + set(CMAKE_CXX_FLAGS \"-std=c++11 \${CMAKE_CXX_FLAGS}\") + else() + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() + endif() + find_package(cereal REQUIRED) + add_executable(cereal-test-config-module main.cpp) + target_link_libraries(cereal-test-config-module cereal) + enable_testing() + add_test(test-cereal-test-config-module cereal-test-config-module) +") +file(WRITE ${BINARY_DIR}/test_source/main.cpp " + #include + #include + #include + struct MyData + { + int x = 0, y = 0, z = 0; + void set() { x = 1; y = 2; z = 3; } + bool is_set() const { return x == 1 && y == 2 && z == 3; } + + // This method lets cereal know which data members to serialize + template + void serialize(Archive & archive) + { + archive( x, y, z ); // serialize things by passing them to the archive + } + }; + int main() + { + std::stringstream ss; // any stream can be used + + { + cereal::BinaryOutputArchive oarchive(ss); // Create an output archive + + MyData m1, m2, m3; + m1.set(); + m2.set(); + m3.set(); + oarchive(m1, m2, m3); // Write the data to the archive + } + + { + cereal::BinaryInputArchive iarchive(ss); // Create an input archive + + MyData m1, m2, m3; + iarchive(m1, m2, m3); // Read the data from the archive + + return (m1.is_set() && m2.is_set() && m3.is_set()) + ? EXIT_SUCCESS : EXIT_FAILURE; + } + }" +) +file(MAKE_DIRECTORY ${BINARY_DIR}/test) +execute_process( + COMMAND ${CMAKE_COMMAND} + -DCMAKE_PREFIX_PATH=${INSTALL_DIR} + ${BINARY_DIR}/test_source + WORKING_DIRECTORY ${BINARY_DIR}/test + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Test cmake configure-step failed") +endif() + +# cmake install cereal +execute_process( + COMMAND ${CMAKE_COMMAND} + --build ${BINARY_DIR}/test + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Test cmake build-step failed") +endif() + +execute_process( + COMMAND ${CMAKE_CTEST_COMMAND} + WORKING_DIRECTORY ${BINARY_DIR}/test + RESULT_VARIABLE result +) + +if(result) + message(FATAL_ERROR "Test run failed") +endif() diff --git a/tpl/cereal/unittests/common.hpp b/tpl/cereal/unittests/common.hpp new file mode 100644 index 0000000..5376d0a --- /dev/null +++ b/tpl/cereal/unittests/common.hpp @@ -0,0 +1,223 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_COMMON_H_ +#define CEREAL_TEST_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "doctest.h" + +namespace std +{ + // Ostream overload for std::pair + template inline + ::std::ostream & operator<<(::std::ostream & os, ::std::pair const & p) + { + os << "([" << p.first << "], [" << p.second << "])"; + return os; + } +} + +// Checks that collections have equal size and all elements are the same +template inline +void check_collection( T const & a, T const & b ) +{ + auto aIter = std::begin(a); + auto aEnd = std::end(a); + auto bIter = std::begin(b); + auto bEnd = std::end(b); + + CHECK_EQ( std::distance(aIter, aEnd), std::distance(bIter, bEnd) ); + + for( ; aIter != aEnd; ++aIter, ++bIter ) + CHECK_EQ( *aIter, *bIter ); +} + +// Random Number Generation =============================================== +template inline +typename std::enable_if::value, T>::type +random_value(std::mt19937 & gen) +{ return std::uniform_real_distribution(-10000.0, 10000.0)(gen); } + +template inline +typename std::enable_if::value && sizeof(T) != sizeof(char), T>::type +random_value(std::mt19937 & gen) +{ return std::uniform_int_distribution(std::numeric_limits::lowest(), std::numeric_limits::max())(gen); } + +template inline +typename std::enable_if::value && sizeof(T) == sizeof(char), T>::type +random_value(std::mt19937 & gen) +{ return static_cast( std::uniform_int_distribution(std::numeric_limits::lowest(), std::numeric_limits::max())(gen) ); } + +template inline +typename std::enable_if::value, std::string>::type +random_value(std::mt19937 & gen) +{ + std::string s(std::uniform_int_distribution(3, 30)(gen), ' '); + for(char & c : s) + c = static_cast( std::uniform_int_distribution( 'A', 'Z' )(gen) ); + return s; +} + +template inline +std::basic_string random_basic_string(std::mt19937 & gen) +{ + std::basic_string s(std::uniform_int_distribution(3, 30)(gen), ' '); + for(C & c : s) + c = static_cast( std::uniform_int_distribution( 'A', 'Z' )(gen) ); + return s; +} + +template inline +std::string random_binary_string(std::mt19937 & gen) +{ + std::string s(N, ' '); + for(auto & c : s ) + c = static_cast( std::uniform_int_distribution( '0', '1' )(gen) ); + return s; +} + +// Generic struct useful for testing many serialization functions +struct StructBase +{ + StructBase() {} + StructBase( int xx, int yy ) : x( xx ), y( yy ) {} + int x, y; + bool operator==(StructBase const & other) const + { return x == other.x && y == other.y; } + bool operator!=(StructBase const & other) const + { return x != other.x || y != other.y; } + bool operator<(StructBase const & other) const + { + if (x < other.x) return true; + else if(other.x < x) return false; + else return (y < other.y); + } +}; + +inline std::ostream& operator<<(std::ostream& os, StructBase const & s) +{ + os << "[x: " << s.x << " y: " << s.y << "]"; + return os; +} + +struct StructInternalSerialize : StructBase +{ + StructInternalSerialize() : StructBase{0,0} {} + StructInternalSerialize(int x_, int y_) : StructBase{x_,y_} {} + template + void serialize(Archive & ar) + { + ar(x, y); + } +}; + +struct StructInternalSplit : StructBase +{ + StructInternalSplit() : StructBase{0,0} {} + StructInternalSplit(int x_, int y_) : StructBase{x_,y_} {} + template + void save(Archive & ar) const + { + ar(x, y); + } + + template + void load(Archive & ar) + { + ar(x, y); + } +}; + +struct StructExternalSerialize : StructBase +{ + StructExternalSerialize() : StructBase{0,0} {} + StructExternalSerialize(int x_, int y_) : StructBase{x_,y_} {} +}; + +template +void serialize(Archive & ar, StructExternalSerialize & s) +{ + ar(s.x, s.y); +} + +struct StructExternalSplit : StructBase +{ + StructExternalSplit() : StructBase{0,0} {} + StructExternalSplit(int x_, int y_) : StructBase{x_,y_} {} +}; + +template inline +void save(Archive & ar, StructExternalSplit const & s) +{ + ar(s.x, s.y); +} + +template inline +void load(Archive & ar, StructExternalSplit & s) +{ + ar(s.x, s.y); +} + +template +struct StructHash { + public: + size_t operator()(const T & s) const + { + size_t h1 = std::hash()(s.x); + size_t h2 = std::hash()(s.y); + return h1 ^ ( h2 << 1 ); + } +}; + +#endif // CEREAL_TEST_COMMON_H_ diff --git a/tpl/cereal/unittests/complex.cpp b/tpl/cereal/unittests/complex.cpp new file mode 100644 index 0000000..168d103 --- /dev/null +++ b/tpl/cereal/unittests/complex.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "complex.hpp" + +TEST_SUITE("complex"); + +TEST_CASE("binary_complex") +{ + test_complex(); +} + +TEST_CASE("portable_binary_complex") +{ + test_complex(); +} + +TEST_CASE("xml_complex") +{ + test_complex(); +} + +TEST_CASE("json_complex") +{ + test_complex(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/complex.hpp b/tpl/cereal/unittests/complex.hpp new file mode 100644 index 0000000..ebd7e35 --- /dev/null +++ b/tpl/cereal/unittests/complex.hpp @@ -0,0 +1,77 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_COMPLEX_H_ +#define CEREAL_TEST_COMPLEX_H_ +#include "common.hpp" + +template inline +void test_complex() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rngF = [&](){ return random_value(gen); }; + auto rngD = [&](){ return random_value(gen); }; + auto rngLD = [&](){ return random_value(gen); }; + + for(int ii=0; ii<100; ++ii) + { + std::complex o_float( rngF(), rngF() ); + std::complex o_double( rngD(), rngD() ); + std::complex o_ldouble( rngLD(), rngLD() ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_float); + oar(o_double); + oar(o_ldouble); + } + + std::complex i_float; + std::complex i_double; + std::complex i_ldouble; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_float); + iar(i_double); + iar(i_ldouble); + } + + CHECK_EQ( o_float, i_float ); + CHECK_EQ( o_double.real(), doctest::Approx(i_double.real()).epsilon(1e-5) ); + CHECK_EQ( o_double.imag(), doctest::Approx(i_double.imag()).epsilon(1e-5) ); + CHECK_EQ( o_ldouble.real(), doctest::Approx(i_ldouble.real()).epsilon(1e-5L) ); + CHECK_EQ( o_ldouble.imag(), doctest::Approx(i_ldouble.imag()).epsilon(1e-5L) ); + } +} + +#endif // CEREAL_TEST_COMPLEX_H_ diff --git a/tpl/cereal/unittests/deque.cpp b/tpl/cereal/unittests/deque.cpp new file mode 100644 index 0000000..cbfaea1 --- /dev/null +++ b/tpl/cereal/unittests/deque.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "deque.hpp" + +TEST_SUITE("deque"); + +TEST_CASE("binary_dequeue") +{ + test_deque(); +} + +TEST_CASE("portable_binary_dequeue") +{ + test_deque(); +} + +TEST_CASE("xml_dequeue") +{ + test_deque(); +} + +TEST_CASE("json_dequeue") +{ + test_deque(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/deque.hpp b/tpl/cereal/unittests/deque.hpp new file mode 100644 index 0000000..b129a1e --- /dev/null +++ b/tpl/cereal/unittests/deque.hpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_DEQUE_H_ +#define CEREAL_TEST_DEQUE_H_ +#include "common.hpp" + +template +void test_deque() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::deque o_poddeque(100); + for(auto & elem : o_poddeque) + elem = random_value(gen); + + std::deque o_iserdeque(100); + for(auto & elem : o_iserdeque) + elem = StructInternalSerialize( random_value(gen), random_value(gen) ); + + std::deque o_ispldeque(100); + for(auto & elem : o_ispldeque) + elem = StructInternalSplit( random_value(gen), random_value(gen) ); + + std::deque o_eserdeque(100); + for(auto & elem : o_eserdeque) + elem = StructExternalSerialize( random_value(gen), random_value(gen) ); + + std::deque o_espldeque(100); + for(auto & elem : o_espldeque) + elem = StructExternalSplit( random_value(gen), random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_poddeque); + oar(o_iserdeque); + oar(o_ispldeque); + oar(o_eserdeque); + oar(o_espldeque); + } + + std::deque i_poddeque; + std::deque i_iserdeque; + std::deque i_ispldeque; + std::deque i_eserdeque; + std::deque i_espldeque; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_poddeque); + iar(i_iserdeque); + iar(i_ispldeque); + iar(i_eserdeque); + iar(i_espldeque); + } + + CHECK_EQ(i_poddeque.size(), o_poddeque.size()); + CHECK_EQ(i_iserdeque.size(), o_iserdeque.size()); + CHECK_EQ(i_ispldeque.size(), o_ispldeque.size()); + CHECK_EQ(i_eserdeque.size(), o_eserdeque.size()); + CHECK_EQ(i_espldeque.size(), o_espldeque.size()); + + check_collection(i_poddeque, o_poddeque ); + check_collection(i_iserdeque, o_iserdeque); + check_collection(i_ispldeque, o_ispldeque); + check_collection(i_eserdeque, o_eserdeque); + check_collection(i_espldeque, o_espldeque); + } +} + +#endif // CEREAL_TEST_DEQUE_H_ diff --git a/tpl/cereal/unittests/doctest.h b/tpl/cereal/unittests/doctest.h new file mode 100644 index 0000000..2edf604 --- /dev/null +++ b/tpl/cereal/unittests/doctest.h @@ -0,0 +1,3374 @@ +// ====================================================================== +// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == +// ====================================================================== +// +// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD +// +// Copyright (c) 2016 Viktor Kirilov +// +// Distributed under the MIT Software License +// See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/MIT +// +// The documentation can be found at the library's page: +// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= +// +// The library is heavily influenced by Catch - https://github.com/philsquared/Catch +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/philsquared/Catch/blob/master/LICENSE_1_0.txt +// +// The concept of subcases (sections in Catch) and expression decomposition are from there. +// Some parts of the code are taken directly: +// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> +// - the Approx() helper class for floating point comparison +// - colors in the console +// - breaking into a debugger +// +// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/martinmoene/lest/blob/master/LICENSE_1_0.txt +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= + +// Suppress this globally (without push/pop) - there is no way to silence it in the +// expression decomposition macros _Pragma() in macros doesn't work for the c++ front-end of g++ +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543 +// Also the warning is completely worthless nowadays - http://stackoverflow.com/questions/14016993 +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Waggregate-return" +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic push +#endif // > gcc 4.6 +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Weffc++" +#pragma GCC diagnostic ignored "-Wstrict-overflow" +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#pragma GCC diagnostic ignored "-Winline" +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif // > gcc 4.6 +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#endif // > gcc 4.7 +#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3) +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif // > gcc 5.3 +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration +#pragma warning(disable : 4706) // assignment within conditional expression +#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated +#pragma warning(disable : 4127) // conditional expression is constant +#endif // _MSC_VER + +#ifndef DOCTEST_LIBRARY_INCLUDED +#define DOCTEST_LIBRARY_INCLUDED + +#define DOCTEST_VERSION_MAJOR 1 +#define DOCTEST_VERSION_MINOR 1 +#define DOCTEST_VERSION_PATCH 2 +#define DOCTEST_VERSION_STR "1.1.2" + +#define DOCTEST_VERSION \ + (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) + +// ================================================================================================= +// == MODERN C++ FEATURE DETECTION ================================================================= +// ================================================================================================= + +#if __cplusplus >= 201103L +#ifndef DOCTEST_CONFIG_WITH_NULLPTR +#define DOCTEST_CONFIG_WITH_NULLPTR +#endif // DOCTEST_CONFIG_WITH_NULLPTR +#ifndef DOCTEST_CONFIG_WITH_LONG_LONG +#define DOCTEST_CONFIG_WITH_LONG_LONG +#endif // DOCTEST_CONFIG_WITH_LONG_LONG +#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT +#define DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // __cplusplus >= 201103L + +// nullptr + +#ifndef DOCTEST_CONFIG_WITH_NULLPTR +#ifdef __clang__ +#if __has_feature(cxx_nullptr) +#define DOCTEST_CONFIG_WITH_NULLPTR +#endif // __has_feature(cxx_nullptr) +#endif // __clang__ + +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define DOCTEST_CONFIG_WITH_NULLPTR +#endif // __GNUC__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) // MSVC 2010 +#define DOCTEST_CONFIG_WITH_NULLPTR +#endif // _MSC_VER +#endif // DOCTEST_CONFIG_WITH_NULLPTR + +#if defined(DOCTEST_CONFIG_NO_NULLPTR) && defined(DOCTEST_CONFIG_WITH_NULLPTR) +#undef DOCTEST_CONFIG_WITH_NULLPTR +#endif // DOCTEST_CONFIG_NO_NULLPTR + +// long long + +#ifndef DOCTEST_CONFIG_WITH_LONG_LONG +#if !defined(DOCTEST_CONFIG_WITH_LONG_LONG) && defined(_MSC_VER) && (_MSC_VER >= 1400) +#define DOCTEST_CONFIG_WITH_LONG_LONG +#endif // _MSC_VER +#endif // DOCTEST_CONFIG_WITH_LONG_LONG + +#if defined(DOCTEST_CONFIG_NO_LONG_LONG) && defined(DOCTEST_CONFIG_WITH_LONG_LONG) +#undef DOCTEST_CONFIG_WITH_LONG_LONG +#endif // DOCTEST_CONFIG_NO_LONG_LONG + +// static_assert + +#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT +#ifdef __clang__ +#if __has_feature(cxx_static_assert) +#define DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // __has_feature(cxx_static_assert) +#endif // __clang__ + +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 3 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // __GNUC__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) // MSVC 2010 +#define DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // _MSC_VER +#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT + +#if defined(DOCTEST_CONFIG_NO_STATIC_ASSERT) && defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) +#undef DOCTEST_CONFIG_WITH_STATIC_ASSERT +#endif // DOCTEST_CONFIG_NO_STATIC_ASSERT + +#if defined(DOCTEST_CONFIG_WITH_NULLPTR) || defined(DOCTEST_CONFIG_WITH_LONG_LONG) || \ + defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) +#define DOCTEST_NO_CPP11_COMPAT +#endif // c++11 stuff + +#if defined(__clang__) && defined(DOCTEST_NO_CPP11_COMPAT) +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif // __clang__ && DOCTEST_NO_CPP11_COMPAT + +// ================================================================================================= +// == MODERN C++ FEATURE DETECTION END ============================================================= +// ================================================================================================= + +// internal macros for string concatenation and anonymous variable name generation +#define DOCTEST_CAT_IMPL(s1, s2) s1##s2 +#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) +#ifdef __COUNTER__ // not standard and may be missing for some compilers +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) +#else // __COUNTER__ +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) +#endif // __COUNTER__ + +// macro for making a string out of an identifier +#define DOCTEST_TOSTR_IMPL(x) #x +#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) + +// for concatenating literals and making the result a string +#define DOCTEST_STR_CONCAT_TOSTR(s1, s2) DOCTEST_TOSTR(s1) DOCTEST_TOSTR(s2) + +// counts the number of elements in a C string +#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) + +#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x& +#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x +#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE + +// not using __APPLE__ because... this is how Catch does it +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define DOCTEST_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define DOCTEST_PLATFORM_IPHONE +#elif defined(_WIN32) || defined(_MSC_VER) +#define DOCTEST_PLATFORM_WINDOWS +#else +#define DOCTEST_PLATFORM_LINUX +#endif + +#define DOCTEST_GCS() (*doctest::detail::getTestsContextState()) + +// should probably take a look at https://github.com/scottt/debugbreak +#ifdef DOCTEST_PLATFORM_MAC +// The following code snippet based on: +// http://cocoawithlove.com/2008/03/break-into-debugger.html +#if defined(__ppc64__) || defined(__ppc__) +#define DOCTEST_BREAK_INTO_DEBUGGER() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" : : : "memory", "r0", "r3", "r4") +#else // __ppc64__ || __ppc__ +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) +#endif // __ppc64__ || __ppc__ +#elif defined(_MSC_VER) +#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() +#else // linux +#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) +#endif // linux + +#define DOCTEST_BREAK_INTO_DEBUGGER_CHECKED() \ + if(doctest::detail::isDebuggerActive() && !DOCTEST_GCS().no_breaks) \ + DOCTEST_BREAK_INTO_DEBUGGER(); + +#ifdef __clang__ +// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) +#include +#endif // __clang__ + +#ifdef _LIBCPP_VERSION +// not forward declaring ostream for libc++ because I had some problems (inline namespaces vs c++98) +// so the header is used - also it is very light and doesn't drag a ton of stuff +#include +#else // _LIBCPP_VERSION +#ifndef DOCTEST_CONFIG_USE_IOSFWD +namespace std +{ +template +struct char_traits; +template <> +struct char_traits; +template +class basic_ostream; +typedef basic_ostream > ostream; +} +#else // DOCTEST_CONFIG_USE_IOSFWD +#include +#endif // DOCTEST_CONFIG_USE_IOSFWD +#endif // _LIBCPP_VERSION + +// static assert macro - because of the c++98 support requires that the message is an +// identifier (no spaces and not a C string) - example without quotes: I_am_a_message +// taken from here: http://stackoverflow.com/a/1980156/3162383 +#ifdef DOCTEST_CONFIG_WITH_STATIC_ASSERT +#define DOCTEST_STATIC_ASSERT(expression, message) static_assert(expression, #message) +#else // DOCTEST_CONFIG_WITH_STATIC_ASSERT +#define DOCTEST_STATIC_ASSERT(expression, message) \ + struct DOCTEST_CAT(__static_assertion_at_line_, __LINE__) \ + { \ + doctest::detail::static_assert_impl::StaticAssertion((expression))> \ + DOCTEST_CAT(DOCTEST_CAT(DOCTEST_CAT(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), \ + _), \ + message); \ + }; \ + typedef doctest::detail::static_assert_impl::StaticAssertionTest \ + DOCTEST_CAT(__static_assertion_test_at_line_, __LINE__) +#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT + +#ifdef DOCTEST_CONFIG_WITH_NULLPTR +#ifdef _LIBCPP_VERSION +#include +#else // _LIBCPP_VERSION +namespace std +{ typedef decltype(nullptr) nullptr_t; } +#endif // _LIBCPP_VERSION +#endif // DOCTEST_CONFIG_WITH_NULLPTR + +namespace doctest +{ +class String +{ + char* m_str; + + void copy(const String& other); + +public: + String(const char* in = ""); + String(const String& other); + ~String(); + + String& operator=(const String& other); + + String operator+(const String& other) const; + String& operator+=(const String& other); + + char& operator[](unsigned pos) { return m_str[pos]; } + const char& operator[](unsigned pos) const { return m_str[pos]; } + + char* c_str() { return m_str; } + const char* c_str() const { return m_str; } + + unsigned size() const; + unsigned length() const; + + int compare(const char* other, bool no_case = false) const; + int compare(const String& other, bool no_case = false) const; +}; + +// clang-format off +inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } +inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } +inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } +inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } +inline bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } +inline bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } +// clang-format on + +std::ostream& operator<<(std::ostream& stream, const String& in); + +namespace detail +{ +#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT + namespace static_assert_impl + { + template + struct StaticAssertion; + + template <> + struct StaticAssertion + {}; + + template + struct StaticAssertionTest + {}; + } // namespace static_assert_impl +#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT + + template + struct deferred_false + { static const bool value = false; }; + + namespace has_insertion_operator_impl + { + typedef char no; + typedef char yes[2]; + + struct any_t + { + template + any_t(const DOCTEST_REF_WRAP(T)); + }; + + yes& testStreamable(std::ostream&); + no testStreamable(no); + + no operator<<(const std::ostream&, const any_t&); + + template + struct has_insertion_operator + { + static std::ostream& s; + static const DOCTEST_REF_WRAP(T) t; + static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes); + }; + } // namespace has_insertion_operator_impl + + template + struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator + {}; + + std::ostream* createStream(); + String getStreamResult(std::ostream*); + void freeStream(std::ostream*); + + template + struct StringMakerBase + { + template + static String convert(const DOCTEST_REF_WRAP(T)) { + return "{?}"; + } + }; + + template <> + struct StringMakerBase + { + template + static String convert(const DOCTEST_REF_WRAP(T) in) { + std::ostream* stream = createStream(); + *stream << in; + String result = getStreamResult(stream); + freeStream(stream); + return result; + } + }; + + String rawMemoryToString(const void* object, unsigned size); + + template + String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { + return rawMemoryToString(&object, sizeof(object)); + } +} // namespace detail + +template +struct StringMaker : detail::StringMakerBase::value> +{}; + +template +struct StringMaker +{ + template + static String convert(U* p) { + if(!p) + return "NULL"; + else + return detail::rawMemoryToString(p); + } +}; + +template +struct StringMaker +{ + static String convert(R C::*p) { + if(!p) + return "NULL"; + else + return detail::rawMemoryToString(p); + } +}; + +template +String toString(const DOCTEST_REF_WRAP(T) value) { + return StringMaker::convert(value); +} + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(char* in); +String toString(const char* in); +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(bool in); +String toString(float in); +String toString(double in); +String toString(double long in); + +String toString(char in); +String toString(char unsigned in); +String toString(int short in); +String toString(int short unsigned in); +String toString(int in); +String toString(int unsigned in); +String toString(int long in); +String toString(int long unsigned in); + +#ifdef DOCTEST_CONFIG_WITH_LONG_LONG +String toString(int long long in); +String toString(int long long unsigned in); +#endif // DOCTEST_CONFIG_WITH_LONG_LONG + +#ifdef DOCTEST_CONFIG_WITH_NULLPTR +String toString(std::nullptr_t in); +#endif // DOCTEST_CONFIG_WITH_NULLPTR + +class Approx +{ +public: + explicit Approx(double value); + + Approx(Approx const& other) + : m_epsilon(other.m_epsilon) + , m_scale(other.m_scale) + , m_value(other.m_value) {} + + Approx operator()(double value) { + Approx approx(value); + approx.epsilon(m_epsilon); + approx.scale(m_scale); + return approx; + } + + friend bool operator==(double lhs, Approx const& rhs); + friend bool operator==(Approx const& lhs, double rhs) { return operator==(rhs, lhs); } + friend bool operator!=(double lhs, Approx const& rhs) { return !operator==(lhs, rhs); } + friend bool operator!=(Approx const& lhs, double rhs) { return !operator==(rhs, lhs); } + + Approx& epsilon(double newEpsilon) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale(double newScale) { + m_scale = newScale; + return *this; + } + + String toString() const; + +private: + double m_epsilon; + double m_scale; + double m_value; +}; + +template <> +inline String toString(const DOCTEST_REF_WRAP(Approx) value) { + return value.toString(); +} + +#if !defined(DOCTEST_CONFIG_DISABLE) + +namespace detail +{ + // the function type this library works with + typedef void (*funcType)(void); + + namespace assertType + { + enum Enum + { + // macro traits + + is_warn = 1, + is_check = 2, + is_require = 4, + + is_throws = 8, + is_throws_as = 16, + is_nothrow = 32, + + is_fast = 64, // not checked anywhere - used just to distinguish the types + is_false = 128, + is_unary = 256, + + is_eq = 512, + is_ne = 1024, + + is_lt = 2048, + is_gt = 4096, + + is_ge = 8192, + is_le = 16384, + + // macro types + + DT_WARN = is_warn, + DT_CHECK = is_check, + DT_REQUIRE = is_require, + + DT_WARN_FALSE = is_false | is_warn, + DT_CHECK_FALSE = is_false | is_check, + DT_REQUIRE_FALSE = is_false | is_require, + + DT_WARN_THROWS = is_throws | is_warn, + DT_CHECK_THROWS = is_throws | is_check, + DT_REQUIRE_THROWS = is_throws | is_require, + + DT_WARN_THROWS_AS = is_throws_as | is_warn, + DT_CHECK_THROWS_AS = is_throws_as | is_check, + DT_REQUIRE_THROWS_AS = is_throws_as | is_require, + + DT_WARN_NOTHROW = is_nothrow | is_warn, + DT_CHECK_NOTHROW = is_nothrow | is_check, + DT_REQUIRE_NOTHROW = is_nothrow | is_require, + + DT_WARN_EQ = is_eq | is_warn, + DT_CHECK_EQ = is_eq | is_check, + DT_REQUIRE_EQ = is_eq | is_require, + + DT_WARN_NE = is_ne | is_warn, + DT_CHECK_NE = is_ne | is_check, + DT_REQUIRE_NE = is_ne | is_require, + + DT_WARN_GT = is_gt | is_warn, + DT_CHECK_GT = is_gt | is_check, + DT_REQUIRE_GT = is_gt | is_require, + + DT_WARN_LT = is_lt | is_warn, + DT_CHECK_LT = is_lt | is_check, + DT_REQUIRE_LT = is_lt | is_require, + + DT_WARN_GE = is_ge | is_warn, + DT_CHECK_GE = is_ge | is_check, + DT_REQUIRE_GE = is_ge | is_require, + + DT_WARN_LE = is_le | is_warn, + DT_CHECK_LE = is_le | is_check, + DT_REQUIRE_LE = is_le | is_require, + + DT_WARN_UNARY = is_unary | is_warn, + DT_CHECK_UNARY = is_unary | is_check, + DT_REQUIRE_UNARY = is_unary | is_require, + + DT_WARN_UNARY_FALSE = is_false | is_unary | is_warn, + DT_CHECK_UNARY_FALSE = is_false | is_unary | is_check, + DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require, + + DT_FAST_WARN_EQ = is_fast | is_eq | is_warn, + DT_FAST_CHECK_EQ = is_fast | is_eq | is_check, + DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require, + + DT_FAST_WARN_NE = is_fast | is_ne | is_warn, + DT_FAST_CHECK_NE = is_fast | is_ne | is_check, + DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require, + + DT_FAST_WARN_GT = is_fast | is_gt | is_warn, + DT_FAST_CHECK_GT = is_fast | is_gt | is_check, + DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require, + + DT_FAST_WARN_LT = is_fast | is_lt | is_warn, + DT_FAST_CHECK_LT = is_fast | is_lt | is_check, + DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require, + + DT_FAST_WARN_GE = is_fast | is_ge | is_warn, + DT_FAST_CHECK_GE = is_fast | is_ge | is_check, + DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require, + + DT_FAST_WARN_LE = is_fast | is_le | is_warn, + DT_FAST_CHECK_LE = is_fast | is_le | is_check, + DT_FAST_REQUIRE_LE = is_fast | is_le | is_require, + + DT_FAST_WARN_UNARY = is_fast | is_unary | is_warn, + DT_FAST_CHECK_UNARY = is_fast | is_unary | is_check, + DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require, + + DT_FAST_WARN_UNARY_FALSE = is_fast | is_false | is_unary | is_warn, + DT_FAST_CHECK_UNARY_FALSE = is_fast | is_false | is_unary | is_check, + DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require + }; + } // namespace assertType + + const char* getAssertString(assertType::Enum val); + + // clang-format off + template struct decay_array { typedef T type; }; + template struct decay_array { typedef T* type; }; + template struct decay_array { typedef T* type; }; + + template struct not_char_pointer { enum { value = true }; }; + template<> struct not_char_pointer { enum { value = false }; }; + template<> struct not_char_pointer { enum { value = false }; }; + + template struct can_use_op : not_char_pointer::type> {}; + + template struct enable_if {}; + template struct enable_if { typedef T type; }; + // clang-format on + + struct TestFailureException + {}; + + bool checkIfShouldThrow(assertType::Enum assert_type); + void fastAssertThrowIfFlagSet(int flags); + void throwException(); + bool always_false(); + + // a struct defining a registered test callback + struct TestData + { + // not used for determining uniqueness + const char* m_suite; // the test suite in which the test was added + const char* m_name; // name of the test function + funcType m_f; // a function pointer to the test function + + // fields by which uniqueness of test cases shall be determined + const char* m_file; // the file in which the test was registered + unsigned m_line; // the line where the test was registered + + TestData(const char* suite, const char* name, funcType f, const char* file, unsigned line) + : m_suite(suite) + , m_name(name) + , m_f(f) + , m_file(file) + , m_line(line) {} + + bool operator<(const TestData& other) const; + }; + + struct SubcaseSignature + { + const char* m_name; + const char* m_file; + int m_line; + + SubcaseSignature(const char* name, const char* file, int line) + : m_name(name) + , m_file(file) + , m_line(line) {} + + bool operator<(const SubcaseSignature& other) const; + }; + + struct Subcase + { + SubcaseSignature m_signature; + bool m_entered; + + Subcase(const char* name, const char* file, int line); + Subcase(const Subcase& other); + ~Subcase(); + + operator bool() const { return m_entered; } + }; + + template + String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, + const DOCTEST_REF_WRAP(R) rhs) { + return toString(lhs) + op + toString(rhs); + } + + struct Result + { + bool m_passed; + String m_decomposition; + +// to fix gcc 4.7 "-Winline" warnings +#if defined(__GNUC__) && !defined(__clang__) + __attribute__((noinline)) +#endif + ~Result() { + } + + Result(bool passed = false, const String& decomposition = String()) + : m_passed(passed) + , m_decomposition(decomposition) {} + + Result(const Result& other) + : m_passed(other.m_passed) + , m_decomposition(other.m_decomposition) {} + +// to fix gcc 4.7 "-Winline" warnings +#if defined(__GNUC__) && !defined(__clang__) + __attribute__((noinline)) +#endif + Result& + operator=(const Result& other) { + m_passed = other.m_passed; + m_decomposition = other.m_decomposition; + + return *this; + } + + operator bool() { return !m_passed; } + + void invert() { m_passed = !m_passed; } + + // clang-format off + // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence + template Result operator& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator^ (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator&& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator|| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator== (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator!= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator< (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator> (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator<= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator>= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator+= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator-= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator*= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator/= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator%= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator<<=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator>>=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator&= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator^= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + template Result operator|= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return Result(); } + // clang-format on + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wdouble-promotion" +//#pragma clang diagnostic ignored "-Wconversion" +//#pragma clang diagnostic ignored "-Wfloat-equal" +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic push +#endif // > gcc 4.6 +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wsign-compare" +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) +#pragma GCC diagnostic ignored "-Wdouble-promotion" +#endif // > gcc 4.5 +//#pragma GCC diagnostic ignored "-Wconversion" +//#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(push) +// http://stackoverflow.com/questions/39479163 what's the difference between C4018 and C4389 +#pragma warning(disable : 4389) // 'operator' : signed/unsigned mismatch +#pragma warning(disable : 4018) // 'expression' : signed/unsigned mismatch +//#pragma warning(disable : 4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation +#endif // _MSC_VER + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + +// clang-format off +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE bool +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type + inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } + inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } + inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } + inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } + inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } + inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + + template DOCTEST_COMPARISON_RETURN_TYPE eq(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs == rhs; } + template DOCTEST_COMPARISON_RETURN_TYPE ne(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs != rhs; } + template DOCTEST_COMPARISON_RETURN_TYPE lt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs < rhs; } + template DOCTEST_COMPARISON_RETURN_TYPE gt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs > rhs; } + template DOCTEST_COMPARISON_RETURN_TYPE le(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs <= rhs; } + template DOCTEST_COMPARISON_RETURN_TYPE ge(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs >= rhs; } + // clang-format on + + template + struct Expression_lhs + { + L lhs; + + Expression_lhs(L in) + : lhs(in) {} + + Expression_lhs(const Expression_lhs& other) + : lhs(other.lhs) {} + + operator Result() { return Result(!!lhs, toString(lhs)); } + +// clang-format off +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + template Result operator==(const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs == rhs, stringifyBinaryExpr(lhs, " == ", rhs)); } + template Result operator!=(const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs != rhs, stringifyBinaryExpr(lhs, " != ", rhs)); } + template Result operator< (const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs < rhs, stringifyBinaryExpr(lhs, " < " , rhs)); } + template Result operator<=(const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs <= rhs, stringifyBinaryExpr(lhs, " <= ", rhs)); } + template Result operator> (const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs > rhs, stringifyBinaryExpr(lhs, " > " , rhs)); } + template Result operator>=(const DOCTEST_REF_WRAP(R) rhs) { return Result(lhs >= rhs, stringifyBinaryExpr(lhs, " >= ", rhs)); } +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + template Result operator==(const DOCTEST_REF_WRAP(R) rhs) { return Result(eq(lhs, rhs), stringifyBinaryExpr(lhs, " == ", rhs)); } + template Result operator!=(const DOCTEST_REF_WRAP(R) rhs) { return Result(ne(lhs, rhs), stringifyBinaryExpr(lhs, " != ", rhs)); } + template Result operator< (const DOCTEST_REF_WRAP(R) rhs) { return Result(lt(lhs, rhs), stringifyBinaryExpr(lhs, " < " , rhs)); } + template Result operator<=(const DOCTEST_REF_WRAP(R) rhs) { return Result(le(lhs, rhs), stringifyBinaryExpr(lhs, " <= ", rhs)); } + template Result operator> (const DOCTEST_REF_WRAP(R) rhs) { return Result(gt(lhs, rhs), stringifyBinaryExpr(lhs, " > " , rhs)); } + template Result operator>=(const DOCTEST_REF_WRAP(R) rhs) { return Result(ge(lhs, rhs), stringifyBinaryExpr(lhs, " >= ", rhs)); } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + // clang-format on + + // clang-format off + // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence + template int operator& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator^ (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator&& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator|| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator+= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator-= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator*= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator/= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator%= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator<<=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator>>=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator&= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator^= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + template int operator|= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return int(); } + // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the + // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... + template int operator<< (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Please_Surround_The_Left_Shift_Operation_With_Parenthesis); return int(); } + template int operator>> (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Please_Surround_The_Right_Shift_Operation_With_Parenthesis); return int(); } + // clang-format on + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic pop +#endif // > gcc 4.6 +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + struct ExpressionDecomposer + { + template + Expression_lhs operator<<(const DOCTEST_REF_WRAP(L) operand) { + return Expression_lhs(operand); + } + }; + + // forward declarations of functions used by the macros + int regTest(void (*f)(void), unsigned line, const char* file, const char* name); + int setTestSuiteName(const char* name); + + void addFailedAssert(assertType::Enum assert_type); + + void logTestStart(const char* name, const char* file, unsigned line); + void logTestEnd(); + + void logTestCrashed(); + + void logAssert(bool passed, const char* decomposition, bool threw, const char* expr, + assertType::Enum assert_type, const char* file, int line); + + void logAssertThrows(bool threw, const char* expr, assertType::Enum assert_type, + const char* file, int line); + + void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const char* expr, + assertType::Enum assert_type, const char* file, int line); + + void logAssertNothrow(bool threw, const char* expr, assertType::Enum assert_type, + const char* file, int line); + + bool isDebuggerActive(); + void writeToDebugConsole(const String&); + + struct TestAccessibleContextState + { + bool success; // include successful assertions in output + bool no_throw; // to skip exceptions-related assertion macros + bool no_breaks; // to not break into the debugger + const TestData* currentTest; + bool hasLoggedCurrentTestStart; + int numAssertionsForCurrentTestcase; + }; + + struct ContextState; + + TestAccessibleContextState* getTestsContextState(); + + namespace binaryAssertComparison + { + enum Enum + { + eq = 0, + ne, + gt, + lt, + ge, + le + }; + } // namespace binaryAssertComparison + + // clang-format off + template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; + template struct RelationalComparator<0, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return eq(lhs, rhs); } }; + template struct RelationalComparator<1, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ne(lhs, rhs); } }; + template struct RelationalComparator<2, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return gt(lhs, rhs); } }; + template struct RelationalComparator<3, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return lt(lhs, rhs); } }; + template struct RelationalComparator<4, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ge(lhs, rhs); } }; + template struct RelationalComparator<5, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return le(lhs, rhs); } }; + // clang-format on + + struct ResultBuilder + { + assertType::Enum m_assert_type; + const char* m_file; + int m_line; + const char* m_expr; + const char* m_exception_type; + + Result m_result; + bool m_threw; + bool m_threw_as; + bool m_failed; + + ResultBuilder(assertType::Enum assert_type, const char* file, int line, const char* expr, + const char* exception_type = ""); + +// to fix gcc 4.7 "-Winline" warnings +#if defined(__GNUC__) && !defined(__clang__) + __attribute__((noinline)) +#endif + ~ResultBuilder() { + } + + void setResult(const Result& res) { m_result = res; } + + template + void binary_assert(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { + m_result.m_passed = RelationalComparator()(lhs, rhs); + m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); + } + + template + void unary_assert(const DOCTEST_REF_WRAP(L) val) { + m_result.m_passed = !!val; + m_result.m_decomposition = toString(val); + } + + bool log(); + void react() const; + }; + + namespace assertAction + { + enum Enum + { + nothing = 0, + dbgbreak = 1, + shouldthrow = 2 + }; + } // namespace assertAction + + template + int fast_binary_assert(assertType::Enum assert_type, const char* file, int line, + const char* lhs_str, const char* rhs_str, const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + String expr = String(lhs_str) + ", " + rhs_str; + const char* expr_str = expr.c_str(); + ResultBuilder rb(assert_type, file, line, expr_str); + + rb.m_result.m_passed = RelationalComparator()(lhs, rhs); + rb.m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); + + int res = 0; + + if(rb.log()) + res |= assertAction::dbgbreak; + + if(rb.m_failed && checkIfShouldThrow(assert_type)) + res |= assertAction::shouldthrow; + +#ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + // ######################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ######################################################################################### + if(res & assertAction::dbgbreak) + DOCTEST_BREAK_INTO_DEBUGGER(); + fastAssertThrowIfFlagSet(res); +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + + return res; + } + + template + int fast_unary_assert(assertType::Enum assert_type, const char* file, int line, + const char* val_str, const DOCTEST_REF_WRAP(L) val) { + ResultBuilder rb(assert_type, file, line, val_str); + + rb.m_result.m_passed = !!val; + rb.m_result.m_decomposition = toString(val); + + int res = 0; + + if(rb.log()) + res |= assertAction::dbgbreak; + + if(rb.m_failed && checkIfShouldThrow(assert_type)) + res |= assertAction::shouldthrow; + +#ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + // ######################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ######################################################################################### + if(res & assertAction::dbgbreak) + DOCTEST_BREAK_INTO_DEBUGGER(); + fastAssertThrowIfFlagSet(res); +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + + return res; + } +} // namespace detail + +#endif // DOCTEST_CONFIG_DISABLE + +class Context +{ +#if !defined(DOCTEST_CONFIG_DISABLE) + detail::ContextState* p; + + void parseArgs(int argc, const char* const* argv, bool withDefaults = false); + +#endif // DOCTEST_CONFIG_DISABLE + +public: + Context(int argc = 0, const char* const* argv = 0); + +// to fix gcc 4.7 "-Winline" warnings +#if defined(__GNUC__) && !defined(__clang__) + __attribute__((noinline)) +#endif + ~Context(); + + void applyCommandLine(int argc, const char* const* argv); + + void addFilter(const char* filter, const char* value); + void clearFilters(); + void setOption(const char* option, int value); + void setOption(const char* option, const char* value); + + bool shouldExit(); + + int run(); +}; + +} // namespace doctest + +// if registering is not disabled +#if !defined(DOCTEST_CONFIG_DISABLE) + +// registers the test by initializing a dummy var with a function +#if defined(__GNUC__) && !defined(__clang__) +#define DOCTEST_REGISTER_FUNCTION(f, name) \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) __attribute__((unused)) = \ + doctest::detail::regTest(f, __LINE__, __FILE__, name); +#elif defined(__clang__) +#define DOCTEST_REGISTER_FUNCTION(f, name) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = \ + doctest::detail::regTest(f, __LINE__, __FILE__, name); \ + _Pragma("clang diagnostic pop") +#else // MSVC +#define DOCTEST_REGISTER_FUNCTION(f, name) \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = \ + doctest::detail::regTest(f, __LINE__, __FILE__, name); +#endif // MSVC + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ + namespace \ + { \ + struct der : base \ + { void f(); }; \ + static void func() { \ + der v; \ + v.f(); \ + } \ + DOCTEST_REGISTER_FUNCTION(func, name) \ + } \ + inline void der::f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ + static void f(); \ + DOCTEST_REGISTER_FUNCTION(f, name) \ + inline void f() + +// for registering tests +#define DOCTEST_TEST_CASE(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(c, name) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for subcases +#if defined(__GNUC__) +#define DOCTEST_SUBCASE(name) \ + if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) \ + __attribute__((unused)) = \ + doctest::detail::Subcase(name, __FILE__, __LINE__)) +#else // __GNUC__ +#define DOCTEST_SUBCASE(name) \ + if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) = \ + doctest::detail::Subcase(name, __FILE__, __LINE__)) +#endif // __GNUC__ + +// for starting a testsuite block +#if defined(__GNUC__) && !defined(__clang__) +#define DOCTEST_TEST_SUITE(name) \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) __attribute__((unused)) = \ + doctest::detail::setTestSuiteName(name); \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#elif defined(__clang__) +#define DOCTEST_TEST_SUITE(name) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = \ + doctest::detail::setTestSuiteName(name); \ + _Pragma("clang diagnostic pop") typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#else // MSVC +#define DOCTEST_TEST_SUITE(name) \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = doctest::detail::setTestSuiteName(name); \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#endif // MSVC + +// for ending a testsuite block +#if defined(__GNUC__) && !defined(__clang__) +#define DOCTEST_TEST_SUITE_END \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) __attribute__((unused)) = \ + doctest::detail::setTestSuiteName(""); \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#elif defined(__clang__) +#define DOCTEST_TEST_SUITE_END \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = doctest::detail::setTestSuiteName(""); \ + _Pragma("clang diagnostic pop") typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#else // MSVC +#define DOCTEST_TEST_SUITE_END \ + static int DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_) = doctest::detail::setTestSuiteName(""); \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#endif // MSVC + +#define DOCTEST_ASSERT_LOG_AND_REACT(rb) \ + if(rb.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + rb.react() + +#define DOCTEST_ASSERT_IMPLEMENT(expr, assert_type) \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, __FILE__, \ + __LINE__, #expr); \ + try { \ + _DOCTEST_RB.setResult(doctest::detail::ExpressionDecomposer() << expr); \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); + +#if defined(__clang__) +#define DOCTEST_ASSERT_PROXY(expr, assert_type) \ + do { \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Woverloaded-shift-op-parentheses\"") \ + DOCTEST_ASSERT_IMPLEMENT(expr, assert_type) \ + _Pragma("clang diagnostic pop") \ + } while(doctest::detail::always_false()) +#else // __clang__ +#define DOCTEST_ASSERT_PROXY(expr, assert_type) \ + do { \ + DOCTEST_ASSERT_IMPLEMENT(expr, assert_type) \ + } while(doctest::detail::always_false()) +#endif // __clang__ + +#define DOCTEST_WARN(expr) DOCTEST_ASSERT_PROXY(expr, DT_WARN) +#define DOCTEST_CHECK(expr) DOCTEST_ASSERT_PROXY(expr, DT_CHECK) +#define DOCTEST_REQUIRE(expr) DOCTEST_ASSERT_PROXY(expr, DT_REQUIRE) + +#define DOCTEST_WARN_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, DT_WARN_FALSE) +#define DOCTEST_CHECK_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, DT_CHECK_FALSE) +#define DOCTEST_REQUIRE_FALSE(expr) DOCTEST_ASSERT_PROXY(expr, DT_REQUIRE_FALSE) + +#define DOCTEST_ASSERT_THROWS(expr, assert_type) \ + do { \ + if(!DOCTEST_GCS().no_throw) { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ + __FILE__, __LINE__, #expr); \ + try { \ + expr; \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } \ + } while(doctest::detail::always_false()) + +#define DOCTEST_ASSERT_THROWS_AS(expr, as, assert_type) \ + do { \ + if(!DOCTEST_GCS().no_throw) { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ + __FILE__, __LINE__, #expr, #as); \ + try { \ + expr; \ + } catch(as) { \ + _DOCTEST_RB.m_threw = true; \ + _DOCTEST_RB.m_threw_as = true; \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } \ + } while(doctest::detail::always_false()) + +#define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ + do { \ + if(!DOCTEST_GCS().no_throw) { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ + __FILE__, __LINE__, #expr); \ + try { \ + expr; \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } \ + } while(doctest::detail::always_false()) + +#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_WARN_THROWS) +#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_CHECK_THROWS) +#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_REQUIRE_THROWS) + +#define DOCTEST_WARN_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_WARN_THROWS_AS) +#define DOCTEST_CHECK_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_CHECK_THROWS_AS) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_REQUIRE_THROWS_AS) + +#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW) +#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW) +#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW) + +#define DOCTEST_BINARY_ASSERT(assert_type, lhs, rhs, comp) \ + do { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ + __FILE__, __LINE__, #lhs ", " #rhs); \ + try { \ + _DOCTEST_RB.binary_assert(lhs, rhs); \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } while(doctest::detail::always_false()) + +#define DOCTEST_UNARY_ASSERT(assert_type, val) \ + do { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ + __FILE__, __LINE__, #val); \ + try { \ + _DOCTEST_RB.unary_assert(val); \ + } catch(...) { _DOCTEST_RB.m_threw = true; } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } while(doctest::detail::always_false()) + +#define DOCTEST_WARN_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, lhs, rhs, eq) +#define DOCTEST_CHECK_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, lhs, rhs, eq) +#define DOCTEST_REQUIRE_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, lhs, rhs, eq) +#define DOCTEST_WARN_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_NE, lhs, rhs, ne) +#define DOCTEST_CHECK_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, lhs, rhs, ne) +#define DOCTEST_REQUIRE_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, lhs, rhs, ne) +#define DOCTEST_WARN_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GT, lhs, rhs, gt) +#define DOCTEST_CHECK_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, lhs, rhs, gt) +#define DOCTEST_REQUIRE_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, lhs, rhs, gt) +#define DOCTEST_WARN_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lhs, rhs, lt) +#define DOCTEST_CHECK_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lhs, rhs, lt) +#define DOCTEST_REQUIRE_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lhs, rhs, lt) +#define DOCTEST_WARN_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GE, lhs, rhs, ge) +#define DOCTEST_CHECK_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, lhs, rhs, ge) +#define DOCTEST_REQUIRE_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, lhs, rhs, ge) +#define DOCTEST_WARN_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LE, lhs, rhs, le) +#define DOCTEST_CHECK_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, lhs, rhs, le) +#define DOCTEST_REQUIRE_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, lhs, rhs, le) + +#define DOCTEST_WARN_UNARY(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, v) +#define DOCTEST_CHECK_UNARY(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, v) +#define DOCTEST_REQUIRE_UNARY(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, v) +#define DOCTEST_WARN_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, v) +#define DOCTEST_CHECK_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, v) +#define DOCTEST_REQUIRE_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, v) + +#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ + do { \ + int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert< \ + doctest::detail::binaryAssertComparison::comparison>( \ + doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs, #rhs, lhs, \ + rhs); \ + if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ + } while(doctest::detail::always_false()) + +#define DOCTEST_FAST_UNARY_ASSERT(assert_type, val) \ + do { \ + int _DOCTEST_FAST_RES = doctest::detail::fast_unary_assert( \ + doctest::detail::assertType::assert_type, __FILE__, __LINE__, #val, val); \ + if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ + } while(doctest::detail::always_false()) + +#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ + doctest::detail::fast_binary_assert( \ + doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs, #rhs, lhs, rhs) + +#define DOCTEST_FAST_UNARY_ASSERT(assert_type, val) \ + doctest::detail::fast_unary_assert(doctest::detail::assertType::assert_type, __FILE__, \ + __LINE__, #val, val) + +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_FAST_WARN_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, l, r, eq) +#define DOCTEST_FAST_CHECK_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, l, r, eq) +#define DOCTEST_FAST_REQUIRE_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, l, r, eq) +#define DOCTEST_FAST_WARN_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, l, r, ne) +#define DOCTEST_FAST_CHECK_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, l, r, ne) +#define DOCTEST_FAST_REQUIRE_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, l, r, ne) +#define DOCTEST_FAST_WARN_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, l, r, gt) +#define DOCTEST_FAST_CHECK_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, l, r, gt) +#define DOCTEST_FAST_REQUIRE_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, l, r, gt) +#define DOCTEST_FAST_WARN_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, l, r, lt) +#define DOCTEST_FAST_CHECK_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, l, r, lt) +#define DOCTEST_FAST_REQUIRE_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, l, r, lt) +#define DOCTEST_FAST_WARN_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, l, r, ge) +#define DOCTEST_FAST_CHECK_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, l, r, ge) +#define DOCTEST_FAST_REQUIRE_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, l, r, ge) +#define DOCTEST_FAST_WARN_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, l, r, le) +#define DOCTEST_FAST_CHECK_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, l, r, le) +#define DOCTEST_FAST_REQUIRE_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, l, r, le) + +#define DOCTEST_FAST_WARN_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, v) +#define DOCTEST_FAST_CHECK_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, v) +#define DOCTEST_FAST_REQUIRE_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, v) +#define DOCTEST_FAST_WARN_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, v) +#define DOCTEST_FAST_CHECK_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, v) +#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(v) \ + DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, v) + +// ================================================================================================= +// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == +// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == +// ================================================================================================= +#else // DOCTEST_CONFIG_DISABLE + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ + namespace \ + { \ + template \ + struct der : base \ + { void f(); }; \ + } \ + template \ + inline void der::f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ + template \ + static inline void f() + +// for registering tests +#define DOCTEST_TEST_CASE(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(x, name) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for subcases +#define DOCTEST_SUBCASE(name) + +// for starting a testsuite block +#define DOCTEST_TEST_SUITE(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for ending a testsuite block +#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_WARN(expr) ((void)0) +#define DOCTEST_WARN_FALSE(expr) ((void)0) +#define DOCTEST_WARN_THROWS(expr) ((void)0) +#define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0) +#define DOCTEST_WARN_NOTHROW(expr) ((void)0) +#define DOCTEST_CHECK(expr) ((void)0) +#define DOCTEST_CHECK_FALSE(expr) ((void)0) +#define DOCTEST_CHECK_THROWS(expr) ((void)0) +#define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0) +#define DOCTEST_CHECK_NOTHROW(expr) ((void)0) +#define DOCTEST_REQUIRE(expr) ((void)0) +#define DOCTEST_REQUIRE_FALSE(expr) ((void)0) +#define DOCTEST_REQUIRE_THROWS(expr) ((void)0) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0) +#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) + +#define DOCTEST_WARN_EQ(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_EQ(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_EQ(lhs, rhs) ((void)0) +#define DOCTEST_WARN_NE(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_NE(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_NE(lhs, rhs) ((void)0) +#define DOCTEST_WARN_GT(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_GT(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_GT(lhs, rhs) ((void)0) +#define DOCTEST_WARN_LT(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_LT(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_LT(lhs, rhs) ((void)0) +#define DOCTEST_WARN_GE(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_GE(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_GE(lhs, rhs) ((void)0) +#define DOCTEST_WARN_LE(lhs, rhs) ((void)0) +#define DOCTEST_CHECK_LE(lhs, rhs) ((void)0) +#define DOCTEST_REQUIRE_LE(lhs, rhs) ((void)0) + +#define DOCTEST_WARN_UNARY(val) ((void)0) +#define DOCTEST_CHECK_UNARY(val) ((void)0) +#define DOCTEST_REQUIRE_UNARY(val) ((void)0) +#define DOCTEST_WARN_UNARY_FALSE(val) ((void)0) +#define DOCTEST_CHECK_UNARY_FALSE(val) ((void)0) +#define DOCTEST_REQUIRE_UNARY_FALSE(val) ((void)0) + +#define DOCTEST_FAST_WARN_EQ(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_EQ(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_EQ(lhs, rhs) ((void)0) +#define DOCTEST_FAST_WARN_NE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_NE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_NE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_WARN_GT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_GT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_GT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_WARN_LT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_LT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_LT(lhs, rhs) ((void)0) +#define DOCTEST_FAST_WARN_GE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_GE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_GE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_WARN_LE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_CHECK_LE(lhs, rhs) ((void)0) +#define DOCTEST_FAST_REQUIRE_LE(lhs, rhs) ((void)0) + +#define DOCTEST_FAST_WARN_UNARY(val) ((void)0) +#define DOCTEST_FAST_CHECK_UNARY(val) ((void)0) +#define DOCTEST_FAST_REQUIRE_UNARY(val) ((void)0) +#define DOCTEST_FAST_WARN_UNARY_FALSE(val) ((void)0) +#define DOCTEST_FAST_CHECK_UNARY_FALSE(val) ((void)0) +#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(val) ((void)0) + +#endif // DOCTEST_CONFIG_DISABLE + +// BDD style macros +// clang-format off +#define DOCTEST_SCENARIO(name) TEST_CASE(" Scenario: " name) +#define DOCTEST_GIVEN(name) SUBCASE(" Given: " name) +#define DOCTEST_WHEN(name) SUBCASE(" When: " name) +#define DOCTEST_AND_WHEN(name) SUBCASE("And when: " name) +#define DOCTEST_THEN(name) SUBCASE(" Then: " name) +#define DOCTEST_AND_THEN(name) SUBCASE(" And: " name) +// clang-format on + +// == SHORT VERSIONS OF THE MACROS +#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) + +#define TEST_CASE DOCTEST_TEST_CASE +#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE +#define SUBCASE DOCTEST_SUBCASE +#define TEST_SUITE DOCTEST_TEST_SUITE +#define TEST_SUITE_END DOCTEST_TEST_SUITE_END +#define WARN DOCTEST_WARN +#define WARN_FALSE DOCTEST_WARN_FALSE +#define WARN_THROWS DOCTEST_WARN_THROWS +#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS +#define WARN_NOTHROW DOCTEST_WARN_NOTHROW +#define CHECK DOCTEST_CHECK +#define CHECK_FALSE DOCTEST_CHECK_FALSE +#define CHECK_THROWS DOCTEST_CHECK_THROWS +#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS +#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW +#define REQUIRE DOCTEST_REQUIRE +#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE +#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS +#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS +#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW + +#define SCENARIO DOCTEST_SCENARIO +#define GIVEN DOCTEST_GIVEN +#define WHEN DOCTEST_WHEN +#define AND_WHEN DOCTEST_AND_WHEN +#define THEN DOCTEST_THEN +#define AND_THEN DOCTEST_AND_THEN + +#define WARN_EQ DOCTEST_WARN_EQ +#define CHECK_EQ DOCTEST_CHECK_EQ +#define REQUIRE_EQ DOCTEST_REQUIRE_EQ +#define WARN_NE DOCTEST_WARN_NE +#define CHECK_NE DOCTEST_CHECK_NE +#define REQUIRE_NE DOCTEST_REQUIRE_NE +#define WARN_GT DOCTEST_WARN_GT +#define CHECK_GT DOCTEST_CHECK_GT +#define REQUIRE_GT DOCTEST_REQUIRE_GT +#define WARN_LT DOCTEST_WARN_LT +#define CHECK_LT DOCTEST_CHECK_LT +#define REQUIRE_LT DOCTEST_REQUIRE_LT +#define WARN_GE DOCTEST_WARN_GE +#define CHECK_GE DOCTEST_CHECK_GE +#define REQUIRE_GE DOCTEST_REQUIRE_GE +#define WARN_LE DOCTEST_WARN_LE +#define CHECK_LE DOCTEST_CHECK_LE +#define REQUIRE_LE DOCTEST_REQUIRE_LE +#define WARN_UNARY DOCTEST_WARN_UNARY +#define CHECK_UNARY DOCTEST_CHECK_UNARY +#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY +#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE +#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE +#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE + +#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ +#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ +#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ +#define FAST_WARN_NE DOCTEST_FAST_WARN_NE +#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE +#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE +#define FAST_WARN_GT DOCTEST_FAST_WARN_GT +#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT +#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT +#define FAST_WARN_LT DOCTEST_FAST_WARN_LT +#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT +#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT +#define FAST_WARN_GE DOCTEST_FAST_WARN_GE +#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE +#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE +#define FAST_WARN_LE DOCTEST_FAST_WARN_LE +#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE +#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE +#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY +#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY +#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY +#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE +#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE +#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE + +#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +// this is here to clear the 'current test suite' for the current translation unit - at the top +DOCTEST_TEST_SUITE_END(); + +#endif // DOCTEST_LIBRARY_INCLUDED + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic pop +#endif // > gcc 4.6 +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#ifndef DOCTEST_SINGLE_HEADER +#define DOCTEST_SINGLE_HEADER +#endif // DOCTEST_SINGLE_HEADER + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#pragma clang diagnostic ignored "-Wmissing-variable-declarations" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wmissing-noreturn" +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic push +#endif // > gcc 4.6 +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Weffc++" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wstrict-overflow" +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#pragma GCC diagnostic ignored "-Winline" +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wswitch-enum" +#pragma GCC diagnostic ignored "-Wswitch-default" +#pragma GCC diagnostic ignored "-Wunsafe-loop-optimizations" +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif // > gcc 4.6 +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#endif // > gcc 4.7 +#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3) +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif // > gcc 5.3 +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration +#pragma warning(disable : 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data +#pragma warning(disable : 4706) // assignment within conditional expression +#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated +#pragma warning(disable : 4127) // conditional expression is constant +#endif // _MSC_VER + +#if defined(DOCTEST_CONFIG_IMPLEMENT) || defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) || \ + !defined(DOCTEST_SINGLE_HEADER) +#ifndef DOCTEST_LIBRARY_IMPLEMENTATION +#define DOCTEST_LIBRARY_IMPLEMENTATION + +#ifndef DOCTEST_SINGLE_HEADER +#include "doctest_fwd.h" +#endif // DOCTEST_SINGLE_HEADER + +#if defined(__clang__) && defined(DOCTEST_NO_CPP11_COMPAT) +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif // __clang__ && DOCTEST_NO_CPP11_COMPAT + +// snprintf() not in the C++98 standard +#ifdef _MSC_VER +#define DOCTEST_SNPRINTF _snprintf +#else +#define DOCTEST_SNPRINTF snprintf +#endif + +#define DOCTEST_LOG_START() \ + do { \ + if(!DOCTEST_GCS().hasLoggedCurrentTestStart) { \ + doctest::detail::logTestStart(DOCTEST_GCS().currentTest->m_name, \ + DOCTEST_GCS().currentTest->m_file, \ + DOCTEST_GCS().currentTest->m_line); \ + DOCTEST_GCS().hasLoggedCurrentTestStart = true; \ + } \ + } while(doctest::detail::always_false()) + +// required includes - will go only in one translation unit! +#include +#include +// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 +#ifdef __BORLANDC__ +#include +#endif // __BORLANDC__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace doctest +{ +namespace detail +{ + // not using std::strlen() because of valgrind errors when optimizations are turned on + // 'Invalid read of size 4' when the test suite len (with '\0') is not a multiple of 4 + // for details see http://stackoverflow.com/questions/35671155 + size_t my_strlen(const char* in) { + const char* temp = in; + while(temp && *temp) + ++temp; + return temp - in; + } + + template + T my_max(const T& lhs, const T& rhs) { + return lhs > rhs ? lhs : rhs; + } + + // case insensitive strcmp + int stricmp(char const* a, char const* b) { + for(;; a++, b++) { + int d = tolower(*a) - tolower(*b); + if(d != 0 || !*a) + return d; + } + } + + template + String fpToString(T value, int precision) { + std::ostringstream oss; + oss << std::setprecision(precision) << std::fixed << value; + std::string d = oss.str(); + size_t i = d.find_last_not_of('0'); + if(i != std::string::npos && i != d.size() - 1) { + if(d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d.c_str(); + } + + struct Endianness + { + enum Arch + { + Big, + Little + }; + + static Arch which() { + union _ + { + int asInt; + char asChar[sizeof(int)]; + } u; + + u.asInt = 1; + return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; + } + }; + + String rawMemoryToString(const void* object, unsigned size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if(Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned char const* bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for(; i != end; i += inc) + os << std::setw(2) << static_cast(bytes[i]); + return os.str().c_str(); + } + + std::ostream* createStream() { return new std::ostringstream(); } + String getStreamResult(std::ostream* in) { + return static_cast(in)->str().c_str(); + } + void freeStream(std::ostream* in) { delete in; } + +#ifndef DOCTEST_CONFIG_DISABLE + + // this holds both parameters for the command line and runtime data for tests + struct ContextState : TestAccessibleContextState + { + // == parameters from the command line + + std::vector > filters; + + String order_by; // how tests should be ordered + unsigned rand_seed; // the seed for rand ordering + + unsigned first; // the first (matching) test to be executed + unsigned last; // the last (matching) test to be executed + + int abort_after; // stop tests after this many failed assertions + bool case_sensitive; // if filtering should be case sensitive + bool exit; // if the program should be exited after the tests are ran/whatever + bool no_exitcode; // if the framework should return 0 as the exitcode + bool no_run; // to not run the tests at all (can be done with an "*" exclude) + bool no_colors; // if output to the console should be colorized + bool no_path_in_filenames; // if the path to files should be removed from the output + + bool help; // to print the help + bool version; // to print the version + bool count; // if only the count of matching tests is to be retreived + bool list_test_cases; // to list all tests matching the filters + bool list_test_suites; // to list all suites matching the filters + + // == data for the tests being ran + + int numAssertions; + int numFailedAssertions; + int numFailedAssertionsForCurrentTestcase; + + // stuff for subcases + std::set subcasesPassed; + std::set subcasesEnteredLevels; + std::vector subcasesStack; + int subcasesCurrentLevel; + bool subcasesHasSkipped; + + void resetRunData() { + numAssertions = 0; + numFailedAssertions = 0; + } + + ContextState() + : filters(6) // 6 different filters total + { + resetRunData(); + } + }; + + ContextState*& getContextState(); +#endif // DOCTEST_CONFIG_DISABLE +} // namespace detail + +String::String(const char* in) + : m_str(static_cast(malloc(detail::my_strlen(in) + 1))) { + if(in) + strcpy(m_str, in); + else + m_str[0] = '\0'; +} + +String::String(const String& other) + : m_str(0) { + copy(other); +} + +void String::copy(const String& other) { + if(m_str) + free(m_str); + m_str = static_cast(malloc(detail::my_strlen(other.m_str) + 1)); + strcpy(m_str, other.m_str); +} + +String::~String() { free(m_str); } + +String& String::operator=(const String& other) { + if(this != &other) + copy(other); + return *this; +} + +String String::operator+(const String& other) const { return String(m_str) += other; } + +String& String::operator+=(const String& other) { + using namespace detail; + if(other.m_str != 0) { + char* newStr = static_cast(malloc(my_strlen(m_str) + my_strlen(other.m_str) + 1)); + strcpy(newStr, m_str); + strcpy(newStr + my_strlen(m_str), other.m_str); + free(m_str); + m_str = newStr; + } + return *this; +} + +unsigned String::size() const { return m_str ? detail::my_strlen(m_str) : 0; } +unsigned String::length() const { return size(); } + +int String::compare(const char* other, bool no_case) const { + if(no_case) + return detail::stricmp(m_str, other); + return strcmp(m_str, other); +} + +int String::compare(const String& other, bool no_case) const { + if(no_case) + return detail::stricmp(m_str, other.m_str); + return strcmp(m_str, other.m_str); +} + +std::ostream& operator<<(std::ostream& stream, const String& in) { + stream << in.c_str(); + return stream; +} + +Approx::Approx(double value) + : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) + , m_scale(1.0) + , m_value(value) {} + +bool operator==(double lhs, Approx const& rhs) { + // Thanks to Richard Harris for his help refining this formula + return fabs(lhs - rhs.m_value) < + rhs.m_epsilon * (rhs.m_scale + detail::my_max(fabs(lhs), fabs(rhs.m_value))); +} + +String Approx::toString() const { return String("Approx( ") + doctest::toString(m_value) + " )"; } + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(char* in) { return toString(static_cast(in)); } +String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(bool in) { return in ? "true" : "false"; } +String toString(float in) { return detail::fpToString(in, 5) + "f"; } +String toString(double in) { return detail::fpToString(in, 10); } +String toString(double long in) { return detail::fpToString(in, 15); } + +String toString(char in) { + char buf[64]; + sprintf(buf, "%d", in); + return buf; +} + +String toString(char unsigned in) { + char buf[64]; + sprintf(buf, "%ud", in); + return buf; +} + +String toString(int short in) { + char buf[64]; + sprintf(buf, "%d", in); + return buf; +} + +String toString(int short unsigned in) { + char buf[64]; + sprintf(buf, "%u", in); + return buf; +} + +String toString(int in) { + char buf[64]; + sprintf(buf, "%d", in); + return buf; +} + +String toString(int unsigned in) { + char buf[64]; + sprintf(buf, "%u", in); + return buf; +} + +String toString(int long in) { + char buf[64]; + sprintf(buf, "%ld", in); + return buf; +} + +String toString(int long unsigned in) { + char buf[64]; + sprintf(buf, "%lu", in); + return buf; +} + +#ifdef DOCTEST_CONFIG_WITH_LONG_LONG +String toString(int long long in) { + char buf[64]; + sprintf(buf, "%lld", in); + return buf; +} +String toString(int long long unsigned in) { + char buf[64]; + sprintf(buf, "%llu", in); + return buf; +} +#endif // DOCTEST_CONFIG_WITH_LONG_LONG + +#ifdef DOCTEST_CONFIG_WITH_NULLPTR +String toString(std::nullptr_t) { return "nullptr"; } +#endif // DOCTEST_CONFIG_WITH_NULLPTR + +} // namespace doctest + +#if defined(DOCTEST_CONFIG_DISABLE) +namespace doctest +{ +Context::Context(int, const char* const*) {} +Context::~Context() {} +void Context::applyCommandLine(int, const char* const*) {} +void Context::addFilter(const char*, const char*) {} +void Context::clearFilters() {} +void Context::setOption(const char*, int) {} +void Context::setOption(const char*, const char*) {} +bool Context::shouldExit() { return false; } +int Context::run() { return 0; } +} // namespace doctest +#else // DOCTEST_CONFIG_DISABLE + +#if !defined(DOCTEST_CONFIG_COLORS_NONE) +#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) +#ifdef DOCTEST_PLATFORM_WINDOWS +#define DOCTEST_CONFIG_COLORS_WINDOWS +#else // linux +#define DOCTEST_CONFIG_COLORS_ANSI +#endif // platform +#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI +#endif // DOCTEST_CONFIG_COLORS_NONE + +#define DOCTEST_PRINTF_COLORED(buffer, color) \ + do { \ + if(buffer[0] != 0) { \ + doctest::detail::Color col(color); \ + printf("%s", buffer); \ + } \ + } while(doctest::detail::always_false()) + +// the buffer size used for snprintf() calls +#if !defined(DOCTEST_SNPRINTF_BUFFER_LENGTH) +#define DOCTEST_SNPRINTF_BUFFER_LENGTH 1024 +#endif // DOCTEST_SNPRINTF_BUFFER_LENGTH + +#if defined(_MSC_VER) || defined(__MINGW32__) +#if defined(_MSC_VER) && _MSC_VER >= 1700 +#define DOCTEST_WINDOWS_SAL_IN_OPT _In_opt_ +#else // _MSC_VER +#define DOCTEST_WINDOWS_SAL_IN_OPT +#endif // _MSC_VER +extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( + DOCTEST_WINDOWS_SAL_IN_OPT const char*); +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +#endif // _MSC_VER || __MINGW32__ + +#ifdef DOCTEST_CONFIG_COLORS_ANSI +#include +#endif // DOCTEST_CONFIG_COLORS_ANSI + +#ifdef DOCTEST_CONFIG_COLORS_WINDOWS + +// defines for a leaner windows.h +#ifndef WIN32_MEAN_AND_LEAN +#define WIN32_MEAN_AND_LEAN +#endif // WIN32_MEAN_AND_LEAN +#ifndef VC_EXTRA_LEAN +#define VC_EXTRA_LEAN +#endif // VC_EXTRA_LEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +// not sure what AfxWin.h is for - here I do what Catch does +#ifdef __AFXDLL +#include +#else +#include +#endif + +#endif // DOCTEST_CONFIG_COLORS_WINDOWS + +namespace doctest +{ +namespace detail +{ + bool TestData::operator<(const TestData& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + return strcmp(m_file, other.m_file) < 0; + } + + const char* getAssertString(assertType::Enum val) { + switch(val) { + // clang-format off + case assertType::DT_WARN : return "WARN"; + case assertType::DT_CHECK : return "CHECK"; + case assertType::DT_REQUIRE : return "REQUIRE"; + + case assertType::DT_WARN_FALSE : return "WARN_FALSE"; + case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; + case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; + + case assertType::DT_WARN_THROWS : return "WARN_THROWS"; + case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; + case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; + + case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; + case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; + case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; + + case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; + case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; + case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; + + case assertType::DT_WARN_EQ : return "WARN_EQ"; + case assertType::DT_CHECK_EQ : return "CHECK_EQ"; + case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; + case assertType::DT_WARN_NE : return "WARN_NE"; + case assertType::DT_CHECK_NE : return "CHECK_NE"; + case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; + case assertType::DT_WARN_GT : return "WARN_GT"; + case assertType::DT_CHECK_GT : return "CHECK_GT"; + case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; + case assertType::DT_WARN_LT : return "WARN_LT"; + case assertType::DT_CHECK_LT : return "CHECK_LT"; + case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; + case assertType::DT_WARN_GE : return "WARN_GE"; + case assertType::DT_CHECK_GE : return "CHECK_GE"; + case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; + case assertType::DT_WARN_LE : return "WARN_LE"; + case assertType::DT_CHECK_LE : return "CHECK_LE"; + case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; + + case assertType::DT_WARN_UNARY : return "WARN_UNARY"; + case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; + case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; + case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; + case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; + case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; + + case assertType::DT_FAST_WARN_EQ : return "FAST_WARN_EQ"; + case assertType::DT_FAST_CHECK_EQ : return "FAST_CHECK_EQ"; + case assertType::DT_FAST_REQUIRE_EQ : return "FAST_REQUIRE_EQ"; + case assertType::DT_FAST_WARN_NE : return "FAST_WARN_NE"; + case assertType::DT_FAST_CHECK_NE : return "FAST_CHECK_NE"; + case assertType::DT_FAST_REQUIRE_NE : return "FAST_REQUIRE_NE"; + case assertType::DT_FAST_WARN_GT : return "FAST_WARN_GT"; + case assertType::DT_FAST_CHECK_GT : return "FAST_CHECK_GT"; + case assertType::DT_FAST_REQUIRE_GT : return "FAST_REQUIRE_GT"; + case assertType::DT_FAST_WARN_LT : return "FAST_WARN_LT"; + case assertType::DT_FAST_CHECK_LT : return "FAST_CHECK_LT"; + case assertType::DT_FAST_REQUIRE_LT : return "FAST_REQUIRE_LT"; + case assertType::DT_FAST_WARN_GE : return "FAST_WARN_GE"; + case assertType::DT_FAST_CHECK_GE : return "FAST_CHECK_GE"; + case assertType::DT_FAST_REQUIRE_GE : return "FAST_REQUIRE_GE"; + case assertType::DT_FAST_WARN_LE : return "FAST_WARN_LE"; + case assertType::DT_FAST_CHECK_LE : return "FAST_CHECK_LE"; + case assertType::DT_FAST_REQUIRE_LE : return "FAST_REQUIRE_LE"; + + case assertType::DT_FAST_WARN_UNARY : return "FAST_WARN_UNARY"; + case assertType::DT_FAST_CHECK_UNARY : return "FAST_CHECK_UNARY"; + case assertType::DT_FAST_REQUIRE_UNARY : return "FAST_REQUIRE_UNARY"; + case assertType::DT_FAST_WARN_UNARY_FALSE : return "FAST_WARN_UNARY_FALSE"; + case assertType::DT_FAST_CHECK_UNARY_FALSE : return "FAST_CHECK_UNARY_FALSE"; + case assertType::DT_FAST_REQUIRE_UNARY_FALSE: return "FAST_REQUIRE_UNARY_FALSE"; + // clang-format on + } + return ""; + } + + bool checkIfShouldThrow(assertType::Enum assert_type) { + if(assert_type & assertType::is_require) + return true; + + if((assert_type & assertType::is_check) && getContextState()->abort_after > 0) { + if(getContextState()->numFailedAssertions >= getContextState()->abort_after) + return true; + } + + return false; + } + void fastAssertThrowIfFlagSet(int flags) { + if(flags & assertAction::shouldthrow) + throwException(); + } + void throwException() { throw TestFailureException(); } + bool always_false() { return false; } + + // lowers ascii letters + char tolower(const char c) { return ((c >= 'A' && c <= 'Z') ? static_cast(c + 32) : c); } + + // matching of a string against a wildcard mask (case sensitivity configurable) taken from + // http://www.emoticode.net/c/simple-wildcard-string-compare-globbing-function.html + int wildcmp(const char* str, const char* wild, bool caseSensitive) { + const char* cp = 0; + const char* mp = 0; + + // rolled my own tolower() to not include more headers + while((*str) && (*wild != '*')) { + if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && + (*wild != '?')) { + return 0; + } + wild++; + str++; + } + + while(*str) { + if(*wild == '*') { + if(!*++wild) { + return 1; + } + mp = wild; + cp = str + 1; + } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || + (*wild == '?')) { + wild++; + str++; + } else { + wild = mp; + str = cp++; + } + } + + while(*wild == '*') { + wild++; + } + return !*wild; + } + + //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + //unsigned hashStr(unsigned const char* str) { + // unsigned long hash = 5381; + // char c; + // while((c = *str++)) + // hash = ((hash << 5) + hash) + c; // hash * 33 + c + // return hash; + //} + + // checks if the name matches any of the filters (and can be configured what to do when empty) + int matchesAny(const char* name, std::vector filters, int matchEmpty, + bool caseSensitive) { + if(filters.size() == 0 && matchEmpty) + return 1; + for(unsigned i = 0; i < filters.size(); ++i) + if(wildcmp(name, filters[i].c_str(), caseSensitive)) + return 1; + return 0; + } + + // the current ContextState with which tests are being executed + ContextState*& getContextState() { + static ContextState* data = 0; + return data; + } + + TestAccessibleContextState* getTestsContextState() { return getContextState(); } + + bool SubcaseSignature::operator<(const SubcaseSignature& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + if(strcmp(m_file, other.m_file) != 0) + return strcmp(m_file, other.m_file) < 0; + return strcmp(m_name, other.m_name) < 0; + } + + Subcase::Subcase(const char* name, const char* file, int line) + : m_signature(name, file, line) + , m_entered(false) { + ContextState* s = getContextState(); + + // if we have already completed it + if(s->subcasesPassed.count(m_signature) != 0) + return; + + // if a Subcase on the same level has already been entered + if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) { + s->subcasesHasSkipped = true; + return; + } + + s->subcasesStack.push_back(*this); + if(s->hasLoggedCurrentTestStart) + logTestEnd(); + s->hasLoggedCurrentTestStart = false; + + s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++); + m_entered = true; + } + + Subcase::Subcase(const Subcase& other) + : m_signature(other.m_signature.m_name, other.m_signature.m_file, + other.m_signature.m_line) + , m_entered(other.m_entered) {} + + Subcase::~Subcase() { + if(m_entered) { + ContextState* s = getContextState(); + + s->subcasesCurrentLevel--; + // only mark the subcase as passed if no subcases have been skipped + if(s->subcasesHasSkipped == false) + s->subcasesPassed.insert(m_signature); + + if(s->subcasesStack.size() > 0) + s->subcasesStack.pop_back(); + if(s->hasLoggedCurrentTestStart) + logTestEnd(); + s->hasLoggedCurrentTestStart = false; + } + } + + // for sorting tests by file/line + int fileOrderComparator(const void* a, const void* b) { + const TestData* lhs = *static_cast(a); + const TestData* rhs = *static_cast(b); +#ifdef _MSC_VER + // this is needed because MSVC gives different case for drive letters + // for __FILE__ when evaluated in a header and a source file + int res = stricmp(lhs->m_file, rhs->m_file); +#else // _MSC_VER + int res = strcmp(lhs->m_file, rhs->m_file); +#endif // _MSC_VER + if(res != 0) + return res; + return static_cast(lhs->m_line - rhs->m_line); + } + + // for sorting tests by suite/file/line + int suiteOrderComparator(const void* a, const void* b) { + const TestData* lhs = *static_cast(a); + const TestData* rhs = *static_cast(b); + + int res = strcmp(lhs->m_suite, rhs->m_suite); + if(res != 0) + return res; + return fileOrderComparator(a, b); + } + + // for sorting tests by name/suite/file/line + int nameOrderComparator(const void* a, const void* b) { + const TestData* lhs = *static_cast(a); + const TestData* rhs = *static_cast(b); + + int res = strcmp(lhs->m_name, rhs->m_name); + if(res != 0) + return res; + return suiteOrderComparator(a, b); + } + + // holds the current test suite + const char*& getCurrentTestSuite() { + static const char* data = 0; + return data; + } + + // sets the current test suite + int setTestSuiteName(const char* name) { + getCurrentTestSuite() = name; + return 0; + } + + // all the registered tests + std::set& getRegisteredTests() { + static std::set data; + return data; + } + + // used by the macros for registering tests + int regTest(funcType f, unsigned line, const char* file, const char* name) { + getRegisteredTests().insert(TestData(getCurrentTestSuite(), name, f, file, line)); + return 0; + } + + struct Color + { + enum Code + { + None = 0, + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White + }; + Color(Code code) { use(code); } + ~Color() { use(None); } + + void use(Code code); + + private: + Color(Color const& other); + }; + + void Color::use(Code +#ifndef DOCTEST_CONFIG_COLORS_NONE + code +#endif // DOCTEST_CONFIG_COLORS_NONE + ) { + ContextState* p = getContextState(); + if(p->no_colors) + return; +#ifdef DOCTEST_CONFIG_COLORS_ANSI + if(isatty(STDOUT_FILENO)) { + const char* col = ""; + // clang-format off + switch(code) { + case Color::Red: col = "[0;31m"; break; + case Color::Green: col = "[0;32m"; break; + case Color::Blue: col = "[0;34m"; break; + case Color::Cyan: col = "[0;36m"; break; + case Color::Yellow: col = "[0;33m"; break; + case Color::Grey: col = "[1;30m"; break; + case Color::LightGrey: col = "[0;37m"; break; + case Color::BrightRed: col = "[1;31m"; break; + case Color::BrightGreen: col = "[1;32m"; break; + case Color::BrightWhite: col = "[1;37m"; break; + case Color::Bright: // invalid + case Color::None: + case Color::White: + default: col = "[0m"; + } + // clang-format on + printf("\033%s", col); + } +#endif // DOCTEST_CONFIG_COLORS_ANSI + +#ifdef DOCTEST_CONFIG_COLORS_WINDOWS + static HANDLE stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + static WORD originalForegroundAttributes; + static WORD originalBackgroundAttributes; + static bool attrsInitted = false; + if(!attrsInitted) { + attrsInitted = true; + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); + originalForegroundAttributes = + csbiInfo.wAttributes & + ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + originalBackgroundAttributes = + csbiInfo.wAttributes & + ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + +#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(stdoutHandle, x | originalBackgroundAttributes) + + // clang-format off + switch (code) { + case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; + case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; + case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; + case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; + case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; + case Color::Grey: DOCTEST_SET_ATTR(0); break; + case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; + case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; + case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; + case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::None: + case Color::Bright: // invalid + default: DOCTEST_SET_ATTR(originalForegroundAttributes); + } +// clang-format on +#undef DOCTEST_SET_ATTR +#endif // DOCTEST_CONFIG_COLORS_WINDOWS + } + + // this is needed because MSVC does not permit mixing 2 exception handling schemes in a function + int callTestFunc(funcType f) { + int res = EXIT_SUCCESS; + try { + f(); + if(getContextState()->numFailedAssertionsForCurrentTestcase) + res = EXIT_FAILURE; + } catch(const TestFailureException&) { res = EXIT_FAILURE; } catch(...) { + DOCTEST_LOG_START(); + logTestCrashed(); + res = EXIT_FAILURE; + } + return res; + } + + // depending on the current options this will remove the path of filenames + const char* fileForOutput(const char* file) { + if(getContextState()->no_path_in_filenames) { + const char* back = strrchr(file, '\\'); + const char* forward = strrchr(file, '/'); + if(back || forward) { + if(back > forward) + forward = back; + return forward + 1; + } + } + return file; + } + +#ifdef DOCTEST_PLATFORM_MAC +#include +#include +#include + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + struct kinfo_proc info; + size_t size; + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + // Call sysctl. + size = sizeof(info); + if(sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, 0, 0) != 0) { + fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " + "active **\n\n"); + return false; + } + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#elif defined(_MSC_VER) || defined(__MINGW32__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } +#else + bool isDebuggerActive() { return false; } +#endif // Platform + +#ifdef DOCTEST_PLATFORM_WINDOWS + void myOutputDebugString(const String& text) { ::OutputDebugStringA(text.c_str()); } +#else + // TODO: integration with XCode and other IDEs + void myOutputDebugString(const String&) {} +#endif // Platform + + const char* getSeparator() { + return "===============================================================================\n"; + } + + void printToDebugConsole(const String& text) { + if(isDebuggerActive()) + myOutputDebugString(text.c_str()); + } + + void addFailedAssert(assertType::Enum assert_type) { + if((assert_type & assertType::is_warn) == 0) { + getContextState()->numFailedAssertionsForCurrentTestcase++; + getContextState()->numFailedAssertions++; + } + } + + void logTestStart(const char* name, const char* file, unsigned line) { + const char* newLine = "\n"; + + char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)\n", fileForOutput(file), line); + + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), "%s\n", name); + + DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow); + DOCTEST_PRINTF_COLORED(loc, Color::LightGrey); + DOCTEST_PRINTF_COLORED(msg, Color::None); + + String subcaseStuff = ""; + std::vector& subcasesStack = getContextState()->subcasesStack; + for(unsigned i = 0; i < subcasesStack.size(); ++i) { + char subcase[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(subcase, DOCTEST_COUNTOF(loc), " %s\n", + subcasesStack[i].m_signature.m_name); + DOCTEST_PRINTF_COLORED(subcase, Color::None); + subcaseStuff += subcase; + } + + DOCTEST_PRINTF_COLORED(newLine, Color::None); + + printToDebugConsole(String(getSeparator()) + loc + msg + subcaseStuff.c_str() + newLine); + } + + void logTestEnd() {} + + void logTestCrashed() { + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), "TEST CASE FAILED! (threw exception)\n\n"); + + DOCTEST_PRINTF_COLORED(msg, Color::Red); + + printToDebugConsole(String(msg)); + } + + void logAssert(bool passed, const char* decomposition, bool threw, const char* expr, + assertType::Enum assert_type, const char* file, int line) { + char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line); + + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + if(passed) + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n"); + else + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED! %s\n", + (threw ? "(threw exception)" : "")); + + char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n", + getAssertString(assert_type), expr); + + char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + char info3[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + info2[0] = 0; + info3[0] = 0; + if(!threw) { + DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "with expansion:\n"); + DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), " %s( %s )\n", + getAssertString(assert_type), decomposition); + } + + DOCTEST_PRINTF_COLORED(loc, Color::LightGrey); + DOCTEST_PRINTF_COLORED(msg, passed ? Color::BrightGreen : Color::Red); + DOCTEST_PRINTF_COLORED(info1, Color::Cyan); + DOCTEST_PRINTF_COLORED(info2, Color::None); + DOCTEST_PRINTF_COLORED(info3, Color::Cyan); + DOCTEST_PRINTF_COLORED("\n", Color::None); + + printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + "\n"); + } + + void logAssertThrows(bool threw, const char* expr, assertType::Enum assert_type, + const char* file, int line) { + char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line); + + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + if(threw) + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n"); + else + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED!\n"); + + char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n\n", + getAssertString(assert_type), expr); + + DOCTEST_PRINTF_COLORED(loc, Color::LightGrey); + DOCTEST_PRINTF_COLORED(msg, threw ? Color::BrightGreen : Color::Red); + DOCTEST_PRINTF_COLORED(info1, Color::Cyan); + + printToDebugConsole(String(loc) + msg + info1); + } + + void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const char* expr, + assertType::Enum assert_type, const char* file, int line) { + char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line); + + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + if(threw_as) + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n"); + else + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED! %s\n", + (threw ? "(threw something else)" : "(didn't throw at all)")); + + char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s, %s )\n\n", + getAssertString(assert_type), expr, as); + + DOCTEST_PRINTF_COLORED(loc, Color::LightGrey); + DOCTEST_PRINTF_COLORED(msg, threw_as ? Color::BrightGreen : Color::Red); + DOCTEST_PRINTF_COLORED(info1, Color::Cyan); + + printToDebugConsole(String(loc) + msg + info1); + } + + void logAssertNothrow(bool threw, const char* expr, assertType::Enum assert_type, + const char* file, int line) { + char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file), line); + + char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + if(!threw) + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " PASSED!\n"); + else + DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " FAILED!\n"); + + char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), " %s( %s )\n\n", + getAssertString(assert_type), expr); + + DOCTEST_PRINTF_COLORED(loc, Color::LightGrey); + DOCTEST_PRINTF_COLORED(msg, !threw ? Color::BrightGreen : Color::Red); + DOCTEST_PRINTF_COLORED(info1, Color::Cyan); + + printToDebugConsole(String(loc) + msg + info1); + } + + ResultBuilder::ResultBuilder(assertType::Enum assert_type, const char* file, int line, + const char* expr, const char* exception_type) + : m_assert_type(assert_type) + , m_file(file) + , m_line(line) + , m_expr(expr) + , m_exception_type(exception_type) + , m_threw(false) + , m_threw_as(false) + , m_failed(false) {} + + bool ResultBuilder::log() { + if((m_assert_type & assertType::is_warn) == 0) + DOCTEST_GCS().numAssertionsForCurrentTestcase++; + + if(m_assert_type & assertType::is_false) { + m_result.invert(); + m_failed = m_result; + } else if(m_assert_type & assertType::is_throws) { + m_failed = !m_threw; + } else if(m_assert_type & assertType::is_throws_as) { + m_failed = !m_threw_as; + } else if(m_assert_type & assertType::is_nothrow) { + m_failed = m_threw; + } else { + m_failed = m_result; + } + + if(m_failed || DOCTEST_GCS().success) { + DOCTEST_LOG_START(); + + if(m_assert_type & assertType::is_throws) { + logAssertThrows(m_threw, m_expr, m_assert_type, m_file, m_line); + } else if(m_assert_type & assertType::is_throws_as) { + logAssertThrowsAs(m_threw, m_threw_as, m_exception_type, m_expr, m_assert_type, + m_file, m_line); + } else if(m_assert_type & assertType::is_nothrow) { + logAssertNothrow(m_threw, m_expr, m_assert_type, m_file, m_line); + } else { + logAssert(m_result.m_passed, m_result.m_decomposition.c_str(), m_threw, m_expr, + m_assert_type, m_file, m_line); + } + } + + if(m_failed) { + addFailedAssert(m_assert_type); + if(isDebuggerActive() && !DOCTEST_GCS().no_breaks) + return true; // should break into the debugger + } + return false; + } + + void ResultBuilder::react() const { + if(m_failed && checkIfShouldThrow(m_assert_type)) + throwException(); + } + + // the implementation of parseFlag() + bool parseFlagImpl(int argc, const char* const* argv, const char* pattern) { + for(int i = argc - 1; i >= 0; --i) { + const char* temp = strstr(argv[i], pattern); + if(temp && my_strlen(temp) == my_strlen(pattern)) { + // eliminate strings in which the chars before the option are not '-' + bool noBadCharsFound = true; + while(temp != argv[i]) { + if(*--temp != '-') { + noBadCharsFound = false; + break; + } + } + if(noBadCharsFound && argv[i][0] == '-') + return true; + } + } + return false; + } + + // locates a flag on the command line + bool parseFlag(int argc, const char* const* argv, const char* pattern) { +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + if(!parseFlagImpl(argc, argv, pattern)) + return parseFlagImpl(argc, argv, pattern + 3); // 3 for "dt-" + return true; +#else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + return parseFlagImpl(argc, argv, pattern); +#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + } + + // the implementation of parseOption() + bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String& res) { + for(int i = argc - 1; i >= 0; --i) { + const char* temp = strstr(argv[i], pattern); + if(temp) { + // eliminate matches in which the chars before the option are not '-' + bool noBadCharsFound = true; + const char* curr = argv[i]; + while(curr != temp) { + if(*curr++ != '-') { + noBadCharsFound = false; + break; + } + } + if(noBadCharsFound && argv[i][0] == '-') { + temp += my_strlen(pattern); + unsigned len = my_strlen(temp); + if(len) { + res = temp; + return true; + } + } + } + } + return false; + } + + // parses an option and returns the string after the '=' character + bool parseOption(int argc, const char* const* argv, const char* pattern, String& res, + const String& defaultVal = String()) { + res = defaultVal; +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + if(!parseOptionImpl(argc, argv, pattern, res)) + return parseOptionImpl(argc, argv, pattern + 3, res); // 3 for "dt-" + return true; +#else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + return parseOptionImpl(argc, argv, pattern, res); +#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + } + + // parses a comma separated list of words after a pattern in one of the arguments in argv + bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, + std::vector& res) { + String filtersString; + if(parseOption(argc, argv, pattern, filtersString)) { + // tokenize with "," as a separator + char* pch = strtok(filtersString.c_str(), ","); // modifies the string + while(pch != 0) { + if(my_strlen(pch)) + res.push_back(pch); + pch = strtok(0, ","); // uses the strtok() internal state to go to the next token + } + return true; + } + return false; + } + + enum optionType + { + option_bool, + option_int + }; + + // parses an int/bool option from the command line + bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, + int& res) { + String parsedValue; + if(parseOption(argc, argv, pattern, parsedValue)) { + if(type == 0) { + // boolean + const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 + const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 + + // if the value matches any of the positive/negative possibilities + for(unsigned i = 0; i < 4; i++) { + if(parsedValue.compare(positive[i], true) == 0) { + res = 1; + return true; + } + if(parsedValue.compare(negative[i], true) == 0) { + res = 0; + return true; + } + } + } else { + // integer + int theInt = atoi(parsedValue.c_str()); + if(theInt != 0) { + res = theInt; + return true; + } + } + } + return false; + } + + void printVersion() { + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("doctest version is \"%s\"\n", DOCTEST_VERSION_STR); + } + + void printHelp() { + printVersion(); + DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("filter values: \"str1,str2,str3\" (comma separated strings)\n"); + DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("filters use wildcards for matching strings\n"); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("something passes a filter if any of the strings in a filter matches\n"); + DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"dt-\" PREFIX!!!\n"); + DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("Query flags - the program quits after them. Available:\n\n"); + printf(" -?, --help, -h prints this message\n"); + printf(" -v, --version prints the version\n"); + printf(" -c, --count prints the number of matching tests\n"); + printf(" -ltc, --list-test-cases lists all matching tests by name\n"); + printf(" -lts, --list-test-suites lists all matching test suites\n\n"); + // ==================================================================================== << 79 + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("The available / options/filters are:\n\n"); + printf(" -tc, --test-case= filters tests by their name\n"); + printf(" -tce, --test-case-exclude= filters OUT tests by their name\n"); + printf(" -sf, --source-file= filters tests by their file\n"); + printf(" -sfe, --source-file-exclude= filters OUT tests by their file\n"); + printf(" -ts, --test-suite= filters tests by their test suite\n"); + printf(" -tse, --test-suite-exclude= filters OUT tests by their test suite\n"); + printf(" -ob, --order-by= how the tests should be ordered\n"); + printf(" - by [file/suite/name/rand]\n"); + printf(" -rs, --rand-seed= seed for random ordering\n"); + printf(" -f, --first= the first test passing the filters to\n"); + printf(" execute - for range-based execution\n"); + printf(" -l, --last= the last test passing the filters to\n"); + printf(" execute - for range-based execution\n"); + printf(" -aa, --abort-after= stop after failed assertions\n\n"); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("Bool options - can be used like flags and true is assumed. Available:\n\n"); + printf(" -s, --success= include successful assertions in output\n"); + printf(" -cs, --case-sensitive= filters being treated as case sensitive\n"); + printf(" -e, --exit= exits after the tests finish\n"); + printf(" -nt, --no-throw= skips exceptions-related assert checks\n"); + printf(" -ne, --no-exitcode= returns (or exits) always with success\n"); + printf(" -nr, --no-run= skips all runtime doctest operations\n"); + printf(" -nc, --no-colors= disables colors in output\n"); + printf(" -nb, --no-breaks= disables breakpoints in debuggers\n"); + printf(" -npf, --no-path-filenames= only filenames and no paths in output\n\n"); + // ==================================================================================== << 79 + + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("for more information visit the project documentation\n\n"); + } +} // namespace detail + +Context::Context(int argc, const char* const* argv) + : p(new detail::ContextState) { + parseArgs(argc, argv, true); +} + +Context::~Context() { delete p; } + +void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); } + +// parses args +void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { + using namespace detail; + + // clang-format off + parseCommaSepArgs(argc, argv, "dt-source-file=", p->filters[0]); + parseCommaSepArgs(argc, argv, "dt-sf=", p->filters[0]); + parseCommaSepArgs(argc, argv, "dt-source-file-exclude=",p->filters[1]); + parseCommaSepArgs(argc, argv, "dt-sfe=", p->filters[1]); + parseCommaSepArgs(argc, argv, "dt-test-suite=", p->filters[2]); + parseCommaSepArgs(argc, argv, "dt-ts=", p->filters[2]); + parseCommaSepArgs(argc, argv, "dt-test-suite-exclude=", p->filters[3]); + parseCommaSepArgs(argc, argv, "dt-tse=", p->filters[3]); + parseCommaSepArgs(argc, argv, "dt-test-case=", p->filters[4]); + parseCommaSepArgs(argc, argv, "dt-tc=", p->filters[4]); + parseCommaSepArgs(argc, argv, "dt-test-case-exclude=", p->filters[5]); + parseCommaSepArgs(argc, argv, "dt-tce=", p->filters[5]); + // clang-format on + + int intRes = 0; + String strRes; + +#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_bool, intRes) || \ + parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_bool, intRes)) \ + p->var = !!intRes; \ + else if(parseFlag(argc, argv, #name) || parseFlag(argc, argv, #sname)) \ + p->var = 1; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_int, intRes) || \ + parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_int, intRes)) \ + p->var = intRes; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ + if(parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), strRes, default) || \ + parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), strRes, default) || \ + withDefaults) \ + p->var = strRes + + // clang-format off + DOCTEST_PARSE_STR_OPTION(dt-order-by, dt-ob, order_by, "file"); + DOCTEST_PARSE_INT_OPTION(dt-rand-seed, dt-rs, rand_seed, 0); + + DOCTEST_PARSE_INT_OPTION(dt-first, dt-f, first, 1); + DOCTEST_PARSE_INT_OPTION(dt-last, dt-l, last, 0); + + DOCTEST_PARSE_INT_OPTION(dt-abort-after, dt-aa, abort_after, 0); + + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-success, dt-s, success, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-case-sensitive, dt-cs, case_sensitive, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-exit, dt-e, exit, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-throw, dt-nt, no_throw, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-exitcode, dt-ne, no_exitcode, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-run, dt-nr, no_run, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-colors, dt-nc, no_colors, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-breaks, dt-nb, no_breaks, 0); + DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-path-filenames, dt-npf, no_path_in_filenames, 0); +// clang-format on + +#undef DOCTEST_PARSE_STR_OPTION +#undef DOCTEST_PARSE_INT_OPTION +#undef DOCTEST_PARSE_AS_BOOL_OR_FLAG + + if(withDefaults) { + p->help = false; + p->version = false; + p->count = false; + p->list_test_cases = false; + p->list_test_suites = false; + } + if(parseFlag(argc, argv, "dt-help") || parseFlag(argc, argv, "dt-h") || + parseFlag(argc, argv, "dt-?")) { + p->help = true; + p->exit = true; + } + if(parseFlag(argc, argv, "dt-version") || parseFlag(argc, argv, "dt-v")) { + p->version = true; + p->exit = true; + } + if(parseFlag(argc, argv, "dt-count") || parseFlag(argc, argv, "dt-c")) { + p->count = true; + p->exit = true; + } + if(parseFlag(argc, argv, "dt-list-test-cases") || parseFlag(argc, argv, "dt-ltc")) { + p->list_test_cases = true; + p->exit = true; + } + if(parseFlag(argc, argv, "dt-list-test-suites") || parseFlag(argc, argv, "dt-lts")) { + p->list_test_suites = true; + p->exit = true; + } +} + +// allows the user to add procedurally to the filters from the command line +void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } + +// allows the user to clear all filters from the command line +void Context::clearFilters() { + for(unsigned i = 0; i < p->filters.size(); ++i) + p->filters[i].clear(); +} + +// allows the user to override procedurally the int/bool options from the command line +void Context::setOption(const char* option, int value) { + setOption(option, toString(value).c_str()); +} + +// allows the user to override procedurally the string options from the command line +void Context::setOption(const char* option, const char* value) { + String argv = String("-") + option + "=" + value; + const char* lvalue = argv.c_str(); + parseArgs(1, &lvalue); +} + +// users should query this in their main() and exit the program if true +bool Context::shouldExit() { return p->exit; } + +// the main function that does all the filtering and test running +int Context::run() { + using namespace detail; + + getContextState() = p; + p->resetRunData(); + + // handle version, help and no_run + if(p->no_run || p->version || p->help) { + if(p->version) + printVersion(); + if(p->help) + printHelp(); + + getContextState() = 0; + + return EXIT_SUCCESS; + } + + printVersion(); + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("run with \"--help\" for options\n"); + + unsigned i = 0; // counter used for loops - here for VC6 + + std::set& registeredTests = getRegisteredTests(); + + std::vector testArray; + for(std::set::iterator it = registeredTests.begin(); it != registeredTests.end(); + ++it) + testArray.push_back(&(*it)); + + // sort the collected records + if(testArray.size() > 0) { + if(p->order_by.compare("file", true) == 0) { + qsort(&testArray[0], testArray.size(), sizeof(TestData*), fileOrderComparator); + } else if(p->order_by.compare("suite", true) == 0) { + qsort(&testArray[0], testArray.size(), sizeof(TestData*), suiteOrderComparator); + } else if(p->order_by.compare("name", true) == 0) { + qsort(&testArray[0], testArray.size(), sizeof(TestData*), nameOrderComparator); + } else if(p->order_by.compare("rand", true) == 0) { + srand(p->rand_seed); + + // random_shuffle implementation + const TestData** first = &testArray[0]; + for(i = testArray.size() - 1; i > 0; --i) { + int idxToSwap = rand() % (i + 1); + + const TestData* temp = first[i]; + + first[i] = first[idxToSwap]; + first[idxToSwap] = temp; + } + } + } + + if(p->list_test_cases) { + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("listing all test case names\n"); + } + + std::set testSuitesPassingFilters; + if(p->list_test_suites) { + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("listing all test suites\n"); + } + + unsigned numTestsPassingFilters = 0; + unsigned numFailed = 0; + // invoke the registered functions if they match the filter criteria (or just count them) + for(i = 0; i < testArray.size(); i++) { + const TestData& data = *testArray[i]; + if(!matchesAny(data.m_file, p->filters[0], 1, p->case_sensitive)) + continue; + if(matchesAny(data.m_file, p->filters[1], 0, p->case_sensitive)) + continue; + if(!matchesAny(data.m_suite, p->filters[2], 1, p->case_sensitive)) + continue; + if(matchesAny(data.m_suite, p->filters[3], 0, p->case_sensitive)) + continue; + if(!matchesAny(data.m_name, p->filters[4], 1, p->case_sensitive)) + continue; + if(matchesAny(data.m_name, p->filters[5], 0, p->case_sensitive)) + continue; + + numTestsPassingFilters++; + + // do not execute the test if we are to only count the number of filter passing tests + if(p->count) + continue; + + // print the name of the test and don't execute it + if(p->list_test_cases) { + printf("%s\n", data.m_name); + continue; + } + + // print the name of the test suite if not done already and don't execute it + if(p->list_test_suites) { + if(testSuitesPassingFilters.count(data.m_suite) == 0) { + printf("%s\n", data.m_suite); + testSuitesPassingFilters.insert(data.m_suite); + } + continue; + } + + // skip the test if it is not in the execution range + if((p->last < numTestsPassingFilters && p->first <= p->last) || + (p->first > numTestsPassingFilters)) + continue; + + // execute the test if it passes all the filtering + { +#ifdef _MSC_VER +//__try { +#endif // _MSC_VER + + p->currentTest = &data; + + // if logging successful tests - force the start log + p->hasLoggedCurrentTestStart = false; + if(p->success) + DOCTEST_LOG_START(); + + unsigned didFail = 0; + p->subcasesPassed.clear(); + do { + // reset the assertion state + p->numAssertionsForCurrentTestcase = 0; + p->numFailedAssertionsForCurrentTestcase = 0; + + // reset some of the fields for subcases (except for the set of fully passed ones) + p->subcasesHasSkipped = false; + p->subcasesCurrentLevel = 0; + p->subcasesEnteredLevels.clear(); + + // execute the test + didFail += callTestFunc(data.m_f); + p->numAssertions += p->numAssertionsForCurrentTestcase; + + // exit this loop if enough assertions have failed + if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) + p->subcasesHasSkipped = false; + + // if the start has been logged + if(p->hasLoggedCurrentTestStart) + logTestEnd(); + p->hasLoggedCurrentTestStart = false; + + } while(p->subcasesHasSkipped == true); + + if(didFail > 0) + numFailed++; + + // stop executing tests if enough assertions have failed + if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) + break; + +#ifdef _MSC_VER +//} __except(1) { +// printf("Unknown SEH exception caught!\n"); +// numFailed++; +//} +#endif // _MSC_VER + } + } + + DOCTEST_PRINTF_COLORED(getSeparator(), numFailed > 0 ? Color::Red : Color::Green); + if(p->count || p->list_test_cases || p->list_test_suites) { + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + printf("number of tests passing the current filters: %d\n", numTestsPassingFilters); + } else { + char buff[DOCTEST_SNPRINTF_BUFFER_LENGTH]; + + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "test cases: %4d", numTestsPassingFilters); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | "); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d passed", + numTestsPassingFilters - numFailed); + DOCTEST_PRINTF_COLORED(buff, numFailed > 0 ? Color::None : Color::Green); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | "); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d failed", numFailed); + DOCTEST_PRINTF_COLORED(buff, numFailed > 0 ? Color::Red : Color::None); + + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | "); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d skipped\n", + static_cast(testArray.size()) - numTestsPassingFilters); + DOCTEST_PRINTF_COLORED(buff, Color::None); + + DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan); + + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "assertions: %4d", p->numAssertions); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | "); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d passed", + p->numAssertions - p->numFailedAssertions); + DOCTEST_PRINTF_COLORED(buff, numFailed > 0 ? Color::None : Color::Green); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | "); + DOCTEST_PRINTF_COLORED(buff, Color::None); + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%4d failed", p->numFailedAssertions); + DOCTEST_PRINTF_COLORED(buff, p->numFailedAssertions > 0 ? Color::Red : Color::None); + + DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " |\n"); + DOCTEST_PRINTF_COLORED(buff, Color::None); + } + + // remove any coloring + DOCTEST_PRINTF_COLORED("", Color::None); + + getContextState() = 0; + + if(numFailed && !p->no_exitcode) + return EXIT_FAILURE; + return EXIT_SUCCESS; +} +} // namespace doctest + +#endif // DOCTEST_CONFIG_DISABLE +#endif // DOCTEST_LIBRARY_IMPLEMENTATION +#endif // DOCTEST_CONFIG_IMPLEMENT + +// == THIS SUPPLIES A MAIN FUNCTION AND SHOULD BE DONE ONLY IN ONE TRANSLATION UNIT +#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_MAIN_CONFIGURED) +#define DOCTEST_MAIN_CONFIGURED +int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } +#endif // DOCTEST_MAIN_CONFIGURED + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // __clang__ + +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6) +#pragma GCC diagnostic pop +#endif // > gcc 4.6 +#endif // __GNUC__ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER diff --git a/tpl/cereal/unittests/forward_list.cpp b/tpl/cereal/unittests/forward_list.cpp new file mode 100644 index 0000000..aefef2e --- /dev/null +++ b/tpl/cereal/unittests/forward_list.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "forward_list.hpp" + +TEST_SUITE("forward_list"); + +TEST_CASE("binary_forward_list") +{ + test_forward_list(); +} + +TEST_CASE("portable_binary_forward_list") +{ + test_forward_list(); +} + +TEST_CASE("xml_forward_list") +{ + test_forward_list(); +} + +TEST_CASE("json_forward_list") +{ + test_forward_list(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/forward_list.hpp b/tpl/cereal/unittests/forward_list.hpp new file mode 100644 index 0000000..c0f568b --- /dev/null +++ b/tpl/cereal/unittests/forward_list.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_FORWARD_LIST_H_ +#define CEREAL_TEST_FORWARD_LIST_H_ +#include "common.hpp" + +template inline +void test_forward_list() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::forward_list o_podforward_list(100); + for(auto & elem : o_podforward_list) + elem = random_value(gen); + + std::forward_list o_iserforward_list(100); + for(auto & elem : o_iserforward_list) + elem = StructInternalSerialize( random_value(gen), random_value(gen) ); + + std::forward_list o_isplforward_list(100); + for(auto & elem : o_isplforward_list) + elem = StructInternalSplit( random_value(gen), random_value(gen) ); + + std::forward_list o_eserforward_list(100); + for(auto & elem : o_eserforward_list) + elem = StructExternalSerialize( random_value(gen), random_value(gen) ); + + std::forward_list o_esplforward_list(100); + for(auto & elem : o_esplforward_list) + elem = StructExternalSplit( random_value(gen), random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podforward_list); + oar(o_iserforward_list); + oar(o_isplforward_list); + oar(o_eserforward_list); + oar(o_esplforward_list); + } + + std::forward_list i_podforward_list; + std::forward_list i_iserforward_list; + std::forward_list i_isplforward_list; + std::forward_list i_eserforward_list; + std::forward_list i_esplforward_list; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podforward_list); + iar(i_iserforward_list); + iar(i_isplforward_list); + iar(i_eserforward_list); + iar(i_esplforward_list); + } + + check_collection(i_podforward_list, o_podforward_list ); + check_collection(i_iserforward_list, o_iserforward_list); + check_collection(i_isplforward_list, o_isplforward_list); + check_collection(i_eserforward_list, o_eserforward_list); + check_collection(i_esplforward_list, o_esplforward_list); + } +} + +#endif // CEREAL_TEST_FORWARD_LIST_H_ diff --git a/tpl/cereal/unittests/list.cpp b/tpl/cereal/unittests/list.cpp new file mode 100644 index 0000000..099f530 --- /dev/null +++ b/tpl/cereal/unittests/list.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "list.hpp" + +TEST_SUITE("list"); + +TEST_CASE("binary_list") +{ + test_list(); +} + +TEST_CASE("portable_binary_list") +{ + test_list(); +} + +TEST_CASE("xml_list") +{ + test_list(); +} + +TEST_CASE("json_list") +{ + test_list(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/list.hpp b/tpl/cereal/unittests/list.hpp new file mode 100644 index 0000000..9e8a7dd --- /dev/null +++ b/tpl/cereal/unittests/list.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_LIST_H_ +#define CEREAL_TEST_LIST_H_ +#include "common.hpp" + +template inline +void test_list() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::list o_podlist(100); + for(auto & elem : o_podlist) + elem = random_value(gen); + + std::list o_iserlist(100); + for(auto & elem : o_iserlist) + elem = StructInternalSerialize( random_value(gen), random_value(gen) ); + + std::list o_ispllist(100); + for(auto & elem : o_ispllist) + elem = StructInternalSplit( random_value(gen), random_value(gen) ); + + std::list o_eserlist(100); + for(auto & elem : o_eserlist) + elem = StructExternalSerialize( random_value(gen), random_value(gen) ); + + std::list o_espllist(100); + for(auto & elem : o_espllist) + elem = StructExternalSplit( random_value(gen), random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podlist); + oar(o_iserlist); + oar(o_ispllist); + oar(o_eserlist); + oar(o_espllist); + } + + std::list i_podlist; + std::list i_iserlist; + std::list i_ispllist; + std::list i_eserlist; + std::list i_espllist; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podlist); + iar(i_iserlist); + iar(i_ispllist); + iar(i_eserlist); + iar(i_espllist); + } + + check_collection(i_podlist, o_podlist); + check_collection(i_iserlist, o_iserlist); + check_collection(i_ispllist, o_ispllist); + check_collection(i_eserlist, o_eserlist); + check_collection(i_espllist, o_espllist); + } +} + +#endif // CEREAL_TEST_LIST_H_ diff --git a/tpl/cereal/unittests/load_construct.cpp b/tpl/cereal/unittests/load_construct.cpp new file mode 100644 index 0000000..923e19a --- /dev/null +++ b/tpl/cereal/unittests/load_construct.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "load_construct.hpp" + +TEST_SUITE("load_construct"); + +TEST_CASE("binary_memory_load_construct") +{ + test_memory_load_construct(); +} + +TEST_CASE("portable_binary_memory_load_construct") +{ + test_memory_load_construct(); +} + +TEST_CASE("xml_memory_load_construct") +{ + test_memory_load_construct(); +} + +TEST_CASE("json_memory_load_construct") +{ + test_memory_load_construct(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/load_construct.hpp b/tpl/cereal/unittests/load_construct.hpp new file mode 100644 index 0000000..97b37c1 --- /dev/null +++ b/tpl/cereal/unittests/load_construct.hpp @@ -0,0 +1,260 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_LOAD_CONSTRUCT_H_ +#define CEREAL_TEST_LOAD_CONSTRUCT_H_ +#include "common.hpp" + +struct OneLA +{ + OneLA( int xx ) : x( xx ) {} + + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int xx; + ar( xx ); + construct( xx ); + } + + bool operator==( OneLA const & other ) const + { return x == other.x; } +}; + +std::ostream& operator<<(std::ostream& os, OneLA const & s) +{ + os << "[" << s.x << "]"; + return os; +} + +struct OneLAVersioned +{ + OneLAVersioned( int xx ) : x( xx ), v() {} + OneLAVersioned( int xx, int vv ) : x( xx ), v( vv ) {} + + int x; + std::uint32_t v; + + template + void serialize( Archive & ar, const std::uint32_t version ) + { ar( x ); v = version; } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct, const std::uint32_t version ) + { + int xx; + ar( xx ); + construct( xx, version ); + } + + bool operator==( OneLAVersioned const & other ) const + { return x == other.x; } +}; + +std::ostream& operator<<(std::ostream& os, OneLAVersioned const & s) +{ + os << "[" << s.x << "]"; + return os; +} + +CEREAL_CLASS_VERSION( OneLAVersioned, 13 ) + +struct TwoLA +{ + TwoLA( int xx ) : x( xx ) {} + + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + + bool operator==( TwoLA const & other ) const + { return x == other.x; } +}; + +std::ostream& operator<<(std::ostream& os, TwoLA const & s) +{ + os << "[" << s.x << "]"; + return os; +} + +namespace cereal +{ + template <> + struct LoadAndConstruct + { + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int xx; + ar( xx ); + construct( xx ); + } + }; +} + +struct TwoLAVersioned +{ + TwoLAVersioned( int xx ) : x( xx ), v() {} + TwoLAVersioned( int xx, int vv ) : x( xx ), v( vv ) {} + + int x; + std::uint32_t v; + + template + void serialize( Archive & ar, const std::uint32_t version ) + { ar( x ); v = version; } + + bool operator==( TwoLAVersioned const & other ) const + { return x == other.x; } +}; + +std::ostream& operator<<(std::ostream& os, TwoLAVersioned const & s) +{ + os << "[" << s.x << "]"; + return os; +} + +namespace cereal +{ + template <> + struct LoadAndConstruct + { + template + static void load_and_construct( Archive & ar, cereal::construct & construct, const std::uint32_t version ) + { + int xx; + ar( xx ); + construct( xx, version ); + } + }; +} + +CEREAL_CLASS_VERSION( TwoLAVersioned, 1 ) + +struct ThreeLA : std::enable_shared_from_this +{ + ThreeLA( int xx ) : x( xx ) {} + + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + + bool operator==( ThreeLA const & other ) const + { return x == other.x; } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int xx; + ar( xx ); + construct( xx ); + } +}; + +std::ostream& operator<<(std::ostream& os, ThreeLA const & s) +{ + os << "[" << s.x << "]"; + return os; +} + +template +void test_memory_load_construct() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + auto o_shared1 = std::make_shared( random_value(gen) ); + auto o_shared2 = std::make_shared( random_value(gen) ); + std::unique_ptr o_unique1( new OneLA( random_value(gen) ) ); + std::unique_ptr o_unique2( new TwoLA( random_value(gen) ) ); + auto o_shared3 = std::make_shared( random_value(gen) ); + auto o_shared1v = std::make_shared( random_value(gen) ); + auto o_shared2v = std::make_shared( random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_shared1 ); + oar( o_shared2 ); + oar( o_unique1 ); + oar( o_unique2 ); + oar( o_shared3 ); + oar( o_shared1v ); + oar( o_shared2v ); + } + + o_shared3->shared_from_this(); // tests github issue #68 + + decltype(o_shared1) i_shared1; + decltype(o_shared2) i_shared2; + decltype(o_unique1) i_unique1; + decltype(o_unique2) i_unique2; + decltype(o_shared3) i_shared3; + decltype(o_shared1v) i_shared1v; + decltype(o_shared2v) i_shared2v; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_shared1 ); + iar( i_shared2 ); + iar( i_unique1 ); + iar( i_unique2 ); + iar( i_shared3 ); + iar( i_shared1v ); + iar( i_shared2v ); + } + + CHECK_EQ( *o_shared1, *i_shared1 ); + CHECK_EQ( *o_shared2, *i_shared2 ); + CHECK_EQ( *o_unique1, *i_unique1 ); + CHECK_EQ( *o_unique2, *i_unique2 ); + CHECK_EQ( *o_shared3, *i_shared3 ); + CHECK_EQ( *o_shared1v, *i_shared1v ); + CHECK_EQ(i_shared1v->v, 13u); + CHECK_EQ( *o_shared2v, *i_shared2v ); + CHECK_EQ(i_shared2v->v, 1u); + + auto i_shared3_2 = i_shared3->shared_from_this(); + CHECK_EQ( *o_shared3, *i_shared3_2 ); + } +} + +#endif // CEREAL_TEST_LOAD_CONSTRUCT_H_ diff --git a/tpl/cereal/unittests/map.cpp b/tpl/cereal/unittests/map.cpp new file mode 100644 index 0000000..40e1b84 --- /dev/null +++ b/tpl/cereal/unittests/map.cpp @@ -0,0 +1,72 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "map.hpp" + +TEST_SUITE("map"); + +TEST_CASE("binary_map") +{ + test_map(); +} + +TEST_CASE("portable_binary_map") +{ + test_map(); +} + +TEST_CASE("xml_map") +{ + test_map(); +} + +TEST_CASE("json_map") +{ + test_map(); +} + +TEST_CASE("binary_map_memory") +{ + test_map_memory(); +} + +TEST_CASE("portable_binary_map_memory") +{ + test_map_memory(); +} + +TEST_CASE("xml_map_memory") +{ + test_map_memory(); +} + +TEST_CASE("json_map_memory") +{ + test_map_memory(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/map.hpp b/tpl/cereal/unittests/map.hpp new file mode 100644 index 0000000..20b7c62 --- /dev/null +++ b/tpl/cereal/unittests/map.hpp @@ -0,0 +1,169 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_MAP_H_ +#define CEREAL_TEST_MAP_H_ +#include "common.hpp" + +template inline +void test_map() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::map> o_vectormap; + for(int j=0; j<10; ++j) + { + size_t id = random_value(gen); + for(int k=0; k<100; ++k) + o_vectormap[id].emplace_back(random_value(gen), random_value(gen)); + } + + std::map o_podmap; + for(int j=0; j<100; ++j) + o_podmap.insert({random_value(gen), random_value(gen)}); + + std::map o_isermap; + for(int j=0; j<100; ++j) + o_isermap.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::map o_isplmap; + for(int j=0; j<100; ++j) + o_isplmap.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::map o_esermap; + for(int j=0; j<100; ++j) + o_esermap.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::map o_esplmap; + for(int j=0; j<100; ++j) + o_esplmap.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_vectormap); + oar(o_podmap); + oar(o_isermap); + oar(o_isplmap); + oar(o_esermap); + oar(o_esplmap); + } + + std::map> i_vectormap; + std::map i_podmap; + std::map i_isermap; + std::map i_isplmap; + std::map i_esermap; + std::map i_esplmap; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_vectormap); + iar(i_podmap); + iar(i_isermap); + iar(i_isplmap); + iar(i_esermap); + iar(i_esplmap); + } + + CHECK_EQ(i_vectormap.size(), o_vectormap.size()); + auto o_v_it = o_vectormap.begin(); + auto i_v_it = i_vectormap.begin(); + for(;o_v_it != o_vectormap.end(); ++o_v_it, ++i_v_it) + { + CHECK_EQ(i_v_it->second.size(), o_v_it->second.size()); + check_collection(i_v_it->second, o_v_it->second); + } + + check_collection(i_podmap, o_podmap); + check_collection(i_isermap, o_isermap); + check_collection(i_isplmap, o_isplmap); + check_collection(i_esermap, o_esermap); + check_collection(i_esplmap, o_esplmap); + } +} + +template inline +void test_map_memory() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::map> o_uniqueptrMap; + std::map> o_sharedptrMap; + + for(int j=0; j<100; ++j) + { + #ifdef CEREAL_OLDER_GCC + o_uniqueptrMap.insert( std::make_pair(random_value(gen), std::unique_ptr( new int( random_value(gen) ) )) ); + o_sharedptrMap.insert( std::make_pair(random_value(gen), std::make_shared( random_value(gen) )) ); + #else // NOT CEREAL_OLDER_GCC + o_uniqueptrMap.emplace( random_value(gen), std::unique_ptr( new int( random_value(gen) ) ) ); + o_sharedptrMap.emplace( random_value(gen), std::make_shared( random_value(gen) ) ); + #endif // NOT CEREAL_OLDER_GCC + } + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_uniqueptrMap ); + oar( o_sharedptrMap ); + } + + decltype( o_uniqueptrMap ) i_uniqueptrMap; + decltype( o_sharedptrMap ) i_sharedptrMap; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_uniqueptrMap ); + iar( i_sharedptrMap ); + } + + CHECK_EQ(o_sharedptrMap.size(), i_sharedptrMap.size()); + CHECK_EQ(o_uniqueptrMap.size(), i_uniqueptrMap.size()); + + auto o_v_it = o_uniqueptrMap.begin(); + auto i_v_it = i_uniqueptrMap.begin(); + for(;o_v_it != o_uniqueptrMap.end(); ++o_v_it, ++i_v_it) + { + CHECK_EQ(i_v_it->first, o_v_it->first); + CHECK_EQ(*i_v_it->second, *o_v_it->second); + } + } +} + +#endif // CEREAL_TEST_MAP_H_ diff --git a/tpl/cereal/unittests/memory.cpp b/tpl/cereal/unittests/memory.cpp new file mode 100644 index 0000000..8c62592 --- /dev/null +++ b/tpl/cereal/unittests/memory.cpp @@ -0,0 +1,72 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "memory.hpp" + +TEST_SUITE("memory"); + +TEST_CASE("binary_memory") +{ + test_memory(); +} + +TEST_CASE("portable_binary_memory") +{ + test_memory(); +} + +TEST_CASE("xml_memory") +{ + test_memory(); +} + +TEST_CASE("json_memory") +{ + test_memory(); +} + +TEST_CASE("binary_default_construction") +{ + test_default_construction(); +} + +TEST_CASE("portable_binary_default_construction") +{ + test_default_construction(); +} + +TEST_CASE("xml_default_construction") +{ + test_default_construction(); +} + +TEST_CASE("json_default_construction") +{ + test_default_construction(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/memory.hpp b/tpl/cereal/unittests/memory.hpp new file mode 100644 index 0000000..19ec2bb --- /dev/null +++ b/tpl/cereal/unittests/memory.hpp @@ -0,0 +1,116 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_MEMORY_H_ +#define CEREAL_TEST_MEMORY_H_ +#include "common.hpp" + +template inline +void test_memory() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::shared_ptr o_xptr1 = std::make_shared(random_value(gen)); + std::shared_ptr o_xptr2 = o_xptr1; + std::shared_ptr o_yptr1 = std::make_shared(random_value(gen)); + std::shared_ptr o_yptr2 = o_yptr1; + std::shared_ptr o_nullptr1; + std::shared_ptr o_nullptr2; + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_xptr1, o_xptr2 ); + oar( o_yptr1, o_yptr2 ); + oar( o_nullptr1, o_nullptr2 ); + } + + std::shared_ptr i_xptr1; + std::shared_ptr i_xptr2; + std::shared_ptr i_yptr1; + std::shared_ptr i_yptr2; + std::shared_ptr i_nullptr1; + std::shared_ptr i_nullptr2; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_xptr1, i_xptr2); + iar( i_yptr1, i_yptr2); + iar( i_nullptr1, i_nullptr2 ); + } + + CHECK_EQ(o_xptr1.get(), o_xptr2.get()); + CHECK_EQ(i_xptr1.get(), i_xptr2.get()); + CHECK_EQ(*i_xptr1, *i_xptr2); + + CHECK_EQ(o_yptr1.get(), o_yptr2.get()); + CHECK_EQ(i_yptr1.get(), i_yptr2.get()); + CHECK_EQ(*i_yptr1, *i_yptr2); + CHECK_UNARY_FALSE(i_nullptr1); + CHECK_UNARY_FALSE(i_nullptr2); + } +} + +class TestClass +{ + public: + TestClass(int v) : x(v) { } + int x; + + private: + friend class cereal::access; + TestClass() { }; + + template + void serialize(Archive & ar) { ar(x); } +}; + +template inline +void test_default_construction() +{ + auto o_ptr = std::make_shared(1); + std::shared_ptr i_ptr; + + std::ostringstream os; + { + OArchive oar(os); + oar(o_ptr); + } + { + std::istringstream is(os.str()); + IArchive iar(is); + iar(i_ptr); + } + CHECK_EQ(o_ptr->x, i_ptr->x); +} + +#endif // CEREAL_TEST_LOAD_CONSTRUCT_H_ diff --git a/tpl/cereal/unittests/memory_cycles.cpp b/tpl/cereal/unittests/memory_cycles.cpp new file mode 100644 index 0000000..461decc --- /dev/null +++ b/tpl/cereal/unittests/memory_cycles.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "memory_cycles.hpp" + +TEST_SUITE("memory_cycles"); + +TEST_CASE("binary_memory_cycles") +{ + test_memory_cycles(); +} + +TEST_CASE("portable_binary_memory_cycles") +{ + test_memory_cycles(); +} + +TEST_CASE("xml_memory_cycles") +{ + test_memory_cycles(); +} + +TEST_CASE("json_memory_cycles") +{ + test_memory_cycles(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/memory_cycles.hpp b/tpl/cereal/unittests/memory_cycles.hpp new file mode 100644 index 0000000..9009723 --- /dev/null +++ b/tpl/cereal/unittests/memory_cycles.hpp @@ -0,0 +1,142 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_MEMORY_CYCLES_H_ +#define CEREAL_TEST_MEMORY_CYCLES_H_ +#include "common.hpp" + +struct MemoryCycle +{ + MemoryCycle() = default; + + MemoryCycle( int v ) : + value( v ) + { } + + int value; + std::weak_ptr ptr; + + bool operator==( MemoryCycle const & other ) const + { + return value == other.value && ptr.lock() == other.ptr.lock(); + } + + template + void serialize( Archive & ar ) + { + ar( value, ptr ); + } +}; + +std::ostream& operator<<(std::ostream& os, MemoryCycle const & s) +{ + os << "[value: " << s.value << " ptr: " << s.ptr.lock() << "]"; + return os; +} + +class MemoryCycleLoadAndConstruct +{ + public: + MemoryCycleLoadAndConstruct( int v ) : + value( v ) + { } + + MemoryCycleLoadAndConstruct( int v, + std::weak_ptr p ) : + value( v ), + ptr( p ) + { } + + bool operator==( MemoryCycleLoadAndConstruct const & other ) const + { + return value == other.value && ptr.lock() == other.ptr.lock(); + } + + template + void serialize( Archive & ar ) + { + ar( value, ptr ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int val; + std::weak_ptr p; + + ar( val, p ); + construct( val, p ); + } + + int value; + std::weak_ptr ptr; +}; + +std::ostream& operator<<(std::ostream& os, MemoryCycleLoadAndConstruct const & s) +{ + os << "[value: " << s.value << " ptr: " << s.ptr.lock() << "]"; + return os; +} + +template inline +void test_memory_cycles() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + auto o_ptr1 = std::make_shared( random_value(gen) ); + o_ptr1->ptr = o_ptr1; + auto o_ptr2 = std::make_shared( random_value(gen) ); + o_ptr2->ptr = o_ptr2; + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_ptr1 ); + oar( o_ptr2 ); + } + + decltype(o_ptr1) i_ptr1; + decltype(o_ptr2) i_ptr2; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_ptr1 ); + iar( i_ptr2 ); + } + + CHECK_EQ( o_ptr1->value, i_ptr1->value ); + CHECK_EQ( i_ptr1.get(), i_ptr1->ptr.lock().get() ); + CHECK_EQ( o_ptr2->value, i_ptr2->value ); + CHECK_EQ( i_ptr2.get(), i_ptr2->ptr.lock().get() ); + } +} +#endif // CEREAL_TEST_MEMORY_CYCLES_H_ diff --git a/tpl/cereal/unittests/multimap.cpp b/tpl/cereal/unittests/multimap.cpp new file mode 100644 index 0000000..8df857e --- /dev/null +++ b/tpl/cereal/unittests/multimap.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "multimap.hpp" + +TEST_SUITE("multimap"); + +TEST_CASE("binary_multimap") +{ + test_multimap(); +} + +TEST_CASE("portable_binary_multimap") +{ + test_multimap(); +} + +TEST_CASE("xml_multimap") +{ + test_multimap(); +} + +TEST_CASE("json_multimap") +{ + test_multimap(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/multimap.hpp b/tpl/cereal/unittests/multimap.hpp new file mode 100644 index 0000000..1426769 --- /dev/null +++ b/tpl/cereal/unittests/multimap.hpp @@ -0,0 +1,129 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_MULTIMAP_H_ +#define CEREAL_TEST_MULTIMAP_H_ +#include "common.hpp" + +template inline +void test_multimap() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::multimap o_podmultimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_podmultimap.insert({key, random_value(gen)}); + o_podmultimap.insert({key, random_value(gen)}); + } + + std::multimap o_isermultimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_isermultimap.insert({key, { random_value(gen), random_value(gen) }}); + o_isermultimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::multimap o_isplmultimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_isplmultimap.insert({key, { random_value(gen), random_value(gen) }}); + o_isplmultimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::multimap o_esermultimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_esermultimap.insert({key, { random_value(gen), random_value(gen) }}); + o_esermultimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::multimap o_esplmultimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_esplmultimap.insert({key, { random_value(gen), random_value(gen) }}); + o_esplmultimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podmultimap); + oar(o_isermultimap); + oar(o_isplmultimap); + oar(o_esermultimap); + oar(o_esplmultimap); + } + + std::multimap i_podmultimap; + std::multimap i_isermultimap; + std::multimap i_isplmultimap; + std::multimap i_esermultimap; + std::multimap i_esplmultimap; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podmultimap); + iar(i_isermultimap); + iar(i_isplmultimap); + iar(i_esermultimap); + iar(i_esplmultimap); + } + +#define MULTIMAP_CHECK(InMap, OutMap) \ + for( auto & pair : OutMap ) \ + { \ + auto const count = InMap.count( pair.first ); \ + CHECK_EQ( count, OutMap.count( pair.first ) ); \ + auto find = InMap.find( pair.first ); \ + bool found = false; \ + for( size_t i = 0; i < count; ++i, ++find ) \ + found |= find->second == pair.second; \ + CHECK_UNARY( found ); \ + } + + MULTIMAP_CHECK( i_podmultimap, o_podmultimap ); + MULTIMAP_CHECK( i_isermultimap, o_isermultimap ); + MULTIMAP_CHECK( i_isplmultimap, o_isplmultimap ); + MULTIMAP_CHECK( i_esermultimap, o_esermultimap ); + MULTIMAP_CHECK( i_esplmultimap, o_esplmultimap ); + +#undef MULTIMAP_CHECK + } +} + +#endif // CEREAL_TEST_MULTIMAP_H_ diff --git a/tpl/cereal/unittests/multiset.cpp b/tpl/cereal/unittests/multiset.cpp new file mode 100644 index 0000000..de53d2a --- /dev/null +++ b/tpl/cereal/unittests/multiset.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "multiset.hpp" + +TEST_SUITE("multiset"); + +TEST_CASE("binary_multiset") +{ + test_multiset(); +} + +TEST_CASE("portable_binary_multiset") +{ + test_multiset(); +} + +TEST_CASE("xml_multiset") +{ + test_multiset(); +} + +TEST_CASE("json_multiset") +{ + test_multiset(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/multiset.hpp b/tpl/cereal/unittests/multiset.hpp new file mode 100644 index 0000000..89ad7a1 --- /dev/null +++ b/tpl/cereal/unittests/multiset.hpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_MULTISET_H_ +#define CEREAL_TEST_MULTISET_H_ +#include "common.hpp" + +template inline +void test_multiset() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::multiset o_podmultiset; + for(int j=0; j<100; ++j) + { + int value = random_value(gen); + o_podmultiset.insert(value); + o_podmultiset.insert(value); + } + + std::multiset o_isermultiset; + for(int j=0; j<100; ++j) + { + StructInternalSerialize value = { random_value(gen), random_value(gen) }; + o_isermultiset.insert(value); + o_isermultiset.insert(value); + } + + std::multiset o_isplmultiset; + for(int j=0; j<100; ++j) + { + StructInternalSplit value = { random_value(gen), random_value(gen) }; + o_isplmultiset.insert(value); + o_isplmultiset.insert(value); + } + + std::multiset o_esermultiset; + for(int j=0; j<100; ++j) + { + StructExternalSerialize value = { random_value(gen), random_value(gen) }; + o_esermultiset.insert(value); + o_esermultiset.insert(value); + } + + std::multiset o_esplmultiset; + for(int j=0; j<100; ++j) + { + StructExternalSplit value = { random_value(gen), random_value(gen) }; + o_esplmultiset.insert(value); + o_esplmultiset.insert(value); + } + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podmultiset); + oar(o_isermultiset); + oar(o_isplmultiset); + oar(o_esermultiset); + oar(o_esplmultiset); + } + + std::multiset i_podmultiset; + std::multiset i_isermultiset; + std::multiset i_isplmultiset; + std::multiset i_esermultiset; + std::multiset i_esplmultiset; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podmultiset); + iar(i_isermultiset); + iar(i_isplmultiset); + iar(i_esermultiset); + iar(i_esplmultiset); + } + + for(auto const & p : i_podmultiset) + { + CHECK_EQ(o_podmultiset.count(p), i_podmultiset.count(p)); + } + + for(auto const & p : i_isermultiset) + { + CHECK_EQ(o_isermultiset.count(p), i_isermultiset.count(p)); + } + + for(auto const & p : i_isplmultiset) + { + CHECK_EQ(o_isplmultiset.count(p), i_isplmultiset.count(p)); + } + + for(auto const & p : i_esermultiset) + { + CHECK_EQ(o_esermultiset.count(p), i_esermultiset.count(p)); + } + + for(auto const & p : i_esplmultiset) + { + CHECK_EQ(o_esplmultiset.count(p), i_esplmultiset.count(p)); + } + } +} + +#endif // CEREAL_TEST_MULTISET_H_ diff --git a/tpl/cereal/unittests/pair.cpp b/tpl/cereal/unittests/pair.cpp new file mode 100644 index 0000000..063237b --- /dev/null +++ b/tpl/cereal/unittests/pair.cpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "pair.hpp" + +TEST_SUITE("pair"); + +TEST_CASE("binary_pair") +{ + test_pair(); +} + +TEST_CASE("portable_binary_pair") +{ + test_pair(); +} + +TEST_CASE("xml_pair") +{ + test_pair(); +} +TEST_CASE("json_pair") +{ + test_pair(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/pair.hpp b/tpl/cereal/unittests/pair.hpp new file mode 100644 index 0000000..fe60931 --- /dev/null +++ b/tpl/cereal/unittests/pair.hpp @@ -0,0 +1,92 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_PAIR_H_ +#define CEREAL_TEST_PAIR_H_ +#include "common.hpp" + +template inline +void test_pair() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rng = [&](){ return random_value(gen); }; + + for(int ii=0; ii<100; ++ii) + { + std::pair o_podpair = {rng(), rng()}; + std::pair o_iserpair = {{rng(), rng()}, {rng(), rng()}}; + std::pair o_isplpair = {{rng(), rng()}, {rng(), rng()}}; + std::pair o_eserpair = {{rng(), rng()}, {rng(), rng()}}; + std::pair o_esplpair = {{rng(), rng()}, {rng(), rng()}}; + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podpair); + oar(o_iserpair); + oar(o_isplpair); + oar(o_eserpair); + oar(o_esplpair); + } + + std::pair i_podpair; + std::pair i_iserpair; + std::pair i_isplpair; + std::pair i_eserpair; + std::pair i_esplpair; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podpair); + iar(i_iserpair); + iar(i_isplpair); + iar(i_eserpair); + iar(i_esplpair); + } + + CHECK_EQ( i_podpair.first, o_podpair.first ); + CHECK_EQ( i_podpair.second, o_podpair.second ); + + CHECK_EQ( i_iserpair.first, o_iserpair.first ); + CHECK_EQ( i_iserpair.second, o_iserpair.second ); + + CHECK_EQ( i_isplpair.first, o_isplpair.first ); + CHECK_EQ( i_isplpair.second, o_isplpair.second ); + + CHECK_EQ( i_eserpair.first, o_eserpair.first ); + CHECK_EQ( i_eserpair.second, o_eserpair.second ); + + CHECK_EQ( i_esplpair.first, o_esplpair.first ); + CHECK_EQ( i_esplpair.second, o_esplpair.second ); + } +} + +#endif // CEREAL_TEST_PAIR_H_ diff --git a/tpl/cereal/unittests/pod.cpp b/tpl/cereal/unittests/pod.cpp new file mode 100644 index 0000000..b2450e7 --- /dev/null +++ b/tpl/cereal/unittests/pod.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "pod.hpp" + +TEST_SUITE("pod"); + +TEST_CASE("binary_pod") +{ + test_pod(); +} + +TEST_CASE("portable_binary_pod") +{ + test_pod(); +} + +TEST_CASE("xml_pod") +{ + test_pod(); +} + +TEST_CASE("json_pod") +{ + test_pod(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/pod.hpp b/tpl/cereal/unittests/pod.hpp new file mode 100644 index 0000000..876d819 --- /dev/null +++ b/tpl/cereal/unittests/pod.hpp @@ -0,0 +1,147 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_POD_H_ +#define CEREAL_TEST_POD_H_ +#include "common.hpp" + +template inline +void test_pod() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(size_t i=0; i<100; ++i) + { + bool const o_bool = random_value(gen) % 2 ? true : false; + char const o_char = random_value(gen); + unsigned char const o_uchar = random_value(gen); + uint8_t const o_uint8 = random_value(gen); + int8_t const o_int8 = random_value(gen); + uint16_t const o_uint16 = random_value(gen); + int16_t const o_int16 = random_value(gen); + uint32_t const o_uint32 = random_value(gen); + int32_t const o_int32 = random_value(gen); + uint64_t const o_uint64 = random_value(gen); + int64_t const o_int64 = random_value(gen); + float const o_float = random_value(gen); + double const o_double = random_value(gen); + + long double const o_long_double = random_value(gen); + long const o_long = random_value(gen); + unsigned long const o_ulong = random_value(gen); + long long const o_long_long = random_value(gen); + unsigned long long const o_ulong_long = random_value(gen); + + std::ostringstream os; + { + OArchive oar(os); + oar(o_bool); + oar(o_char); + oar(o_uchar); + oar(o_uint8); + oar(o_int8); + oar(o_uint16); + oar(o_int16); + oar(o_uint32); + oar(o_int32); + oar(o_uint64); + oar(o_int64); + oar(o_float); + oar(o_double); + oar(o_long_double); + oar(o_long); + oar(o_ulong); + oar(o_long_long); + oar(o_ulong_long); + } + + bool i_bool = false; + char i_char = 0; + unsigned char i_uchar = 0; + uint8_t i_uint8 = 0; + int8_t i_int8 = 0; + uint16_t i_uint16 = 0; + int16_t i_int16 = 0; + uint32_t i_uint32 = 0; + int32_t i_int32 = 0; + uint64_t i_uint64 = 0; + int64_t i_int64 = 0; + float i_float = 0; + double i_double = 0; + + long double i_long_double = 0; + long i_long = 0; + unsigned long i_ulong = 0; + long long i_long_long = 0; + unsigned long long i_ulong_long = 0; + + std::istringstream is(os.str()); + { + IArchive iar(is); + iar(i_bool); + iar(i_char); + iar(i_uchar); + iar(i_uint8); + iar(i_int8); + iar(i_uint16); + iar(i_int16); + iar(i_uint32); + iar(i_int32); + iar(i_uint64); + iar(i_int64); + iar(i_float); + iar(i_double); + iar(i_long_double); + iar(i_long); + iar(i_ulong); + iar(i_long_long); + iar(i_ulong_long); + } + + CHECK_EQ(i_bool , o_bool); + CHECK_EQ(i_char , o_char); + CHECK_EQ(i_uchar , o_uchar); + CHECK_EQ(i_uint8 , o_uint8); + CHECK_EQ(i_int8 , o_int8); + CHECK_EQ(i_uint16 , o_uint16); + CHECK_EQ(i_int16 , o_int16); + CHECK_EQ(i_uint32 , o_uint32); + CHECK_EQ(i_int32 , o_int32); + CHECK_EQ(i_uint64 , o_uint64); + CHECK_EQ(i_int64 , o_int64); + CHECK_EQ(i_float , doctest::Approx(o_float).epsilon(1e-5F)); + CHECK_EQ(i_double , doctest::Approx(o_double).epsilon(1e-5)); + + CHECK_EQ(i_long_double, doctest::Approx(o_long_double).epsilon(1e-5L)); + CHECK_EQ(i_long, o_long); + CHECK_EQ(i_ulong, o_ulong); + CHECK_EQ(i_long_long, o_long_long); + CHECK_EQ(i_ulong_long, o_ulong_long); + } +} + +#endif // CEREAL_TEST_POD_H_ diff --git a/tpl/cereal/unittests/polymorphic.cpp b/tpl/cereal/unittests/polymorphic.cpp new file mode 100644 index 0000000..87e6de4 --- /dev/null +++ b/tpl/cereal/unittests/polymorphic.cpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "polymorphic.hpp" + +TEST_SUITE("polymorphic"); + +TEST_CASE("binary_polymorphic") +{ + test_polymorphic(); +} + +TEST_CASE("portable_binary_polymorphic") +{ + test_polymorphic(); +} + +TEST_CASE("xml_polymorphic") +{ + test_polymorphic(); +} + +TEST_CASE("json_polymorphic") +{ + test_polymorphic(); +} + +#if CEREAL_THREAD_SAFE +TEST_CASE("binary_polymorphic_threading") +{ + test_polymorphic_threading(); +} + +TEST_CASE("portable_binary_polymorphic_threading") +{ + test_polymorphic_threading(); +} + +TEST_CASE("xml_polymorphic_threading") +{ + test_polymorphic_threading(); +} + +TEST_CASE("json_polymorphic_threading") +{ + test_polymorphic_threading(); +} +#endif // CEREAL_THREAD_SAFE + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/polymorphic.hpp b/tpl/cereal/unittests/polymorphic.hpp new file mode 100644 index 0000000..9309ac4 --- /dev/null +++ b/tpl/cereal/unittests/polymorphic.hpp @@ -0,0 +1,359 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_POLYMORPHIC_H_ +#define CEREAL_TEST_POLYMORPHIC_H_ +#include "common.hpp" + +#if CEREAL_THREAD_SAFE +#include +#endif + +struct PolyBaseA +{ + virtual void foo() = 0; + virtual ~PolyBaseA() {} +}; + +struct PolyBaseAA : PolyBaseA +{ + PolyBaseAA() {} + PolyBaseAA( long ww ) : w(ww) {} + virtual ~PolyBaseAA() {} + long w; + + void foo() {} + + template + void serialize( Archive & ar ) + { + ar( w ); + } + + bool operator==( PolyBaseAA const & other ) const + { + return w == other.w; + } +}; + +CEREAL_REGISTER_POLYMORPHIC_RELATION(PolyBaseA, PolyBaseAA) + +struct PolyBaseB : virtual PolyBaseAA +{ + PolyBaseB() {} + PolyBaseB( int xx, long ww ) : PolyBaseAA(ww), x(xx) {} + virtual ~PolyBaseB() {} + int x; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); + ar( x ); + } + + bool operator==( PolyBaseB const & other ) const + { + return PolyBaseAA::operator==( other ) && + x == other.x; + } +}; + +struct PolyBaseC : virtual PolyBaseAA +{ + PolyBaseC() {} + PolyBaseC( double yy, long ww ) : PolyBaseAA(ww), y(yy) {} + virtual ~PolyBaseC() {} + double y; + + template + void serialize( Archive & ar ) + { + ar( cereal::virtual_base_class( this ) ); + ar( y ); + } + + bool operator==( PolyBaseC const & other ) const + { + return PolyBaseAA::operator==( other ) && + std::abs(y - other.y) < 1e-5; + } +}; + +struct PolyDerivedD : PolyBaseB, PolyBaseC +{ + PolyDerivedD() {} + PolyDerivedD( std::string const & zz, double yy, int xx, long ww ) : + PolyBaseAA( ww ), PolyBaseB( xx, ww ), PolyBaseC( yy, ww ), z(zz) {} + virtual ~PolyDerivedD() {} + std::string z; + + template + void serialize( Archive & ar ) + { + ar( cereal::base_class( this ) ); + ar( cereal::base_class( this ) ); + ar( z ); + } + + bool operator==( PolyDerivedD const & other ) const + { + return PolyBaseB::operator==( other ) && + PolyBaseC::operator==( other ) && + z == other.z; + } +}; + +CEREAL_REGISTER_TYPE(PolyDerivedD) + +struct PolyBase +{ + PolyBase() {} + PolyBase( int xx, float yy ) : x(xx), y(yy) {} + virtual ~PolyBase() {} + int x; + float y; + + template + void serialize( Archive & ar ) + { + ar( x, y ); + } + + virtual void foo() = 0; + + bool operator==( PolyBase const & other ) const + { + return x == other.x && std::abs(y - other.y) < 1e-5; + } +}; + +struct PolyDerived : PolyBase +{ + PolyDerived() {} + PolyDerived( int xx, float yy, bool aa, double bb ) : + PolyBase( xx, yy ), a(aa), b(bb) {} + virtual ~PolyDerived() {} + + bool a; + double b; + + template + void serialize( Archive & ar ) + { + ar( cereal::base_class( this ), + a, b ); + } + + bool operator==( PolyDerived const & other ) const + { + return PolyBase::operator==( other ) && a == other.a && std::abs(b - other.b) < 1e-5; + } + + void foo() {} +}; + +CEREAL_REGISTER_TYPE(PolyDerived) + +struct PolyLA : std::enable_shared_from_this +{ + PolyLA() {} + virtual ~PolyLA() {} + virtual void foo() = 0; +}; + +struct PolyDerivedLA : public PolyLA +{ + PolyDerivedLA( int xx ) : x( xx ) { } + virtual ~PolyDerivedLA() {} + + int x; + std::vector> vec; + + template + void serialize( Archive & ar ) + { + ar( x ); + ar( vec ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int xx; + ar( xx ); + construct( xx ); + ar( construct->vec ); + } + + void foo() {} + + bool operator==( PolyDerivedLA const & other ) const + { + if( x != other.x ) + return false; + if( vec.size() != other.vec.size() ) + return false; + for( size_t i = 0; i < vec.size(); ++i ) + if( !(*std::dynamic_pointer_cast(vec[i]) == *std::dynamic_pointer_cast(other.vec[i])) ) + return false; + + return true; + } +}; + +CEREAL_REGISTER_TYPE(PolyDerivedLA) +CEREAL_REGISTER_POLYMORPHIC_RELATION(PolyLA, PolyDerivedLA) + +std::ostream& operator<<(std::ostream& os, PolyDerivedLA const & s) +{ + os << "[x: " << s.x << "] "; + for( auto const & v : s.vec ) + os << " child: " << (*std::dynamic_pointer_cast(v)); + return os; +} + +std::ostream& operator<<(std::ostream& os, PolyDerived const & s) +{ + os << "[x: " << s.x << " y: " << s.y << " a: " << s.a << " b: " << s.b << "]"; + return os; +} + +std::ostream& operator<<(std::ostream& os, PolyDerivedD const & s) +{ + os << "[w: " << s.w << " x: " << s.x << " y: " << s.y << " z: " << s.z << "]"; + return os; +} + +template inline +void test_polymorphic() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + #if CEREAL_THREAD_SAFE + static std::mutex testMutex; + #endif + + auto rngB = [&](){ return random_value( gen ) % 2 == 0; }; + auto rngI = [&](){ return random_value( gen ); }; + auto rngL = [&](){ return random_value( gen ); }; + auto rngF = [&](){ return random_value( gen ); }; + auto rngD = [&](){ return random_value( gen ); }; + + for(int ii=0; ii<100; ++ii) + { + std::shared_ptr o_shared = std::make_shared( rngI(), rngF(), rngB(), rngD() ); + std::weak_ptr o_weak = o_shared; + std::unique_ptr o_unique( new PolyDerived( rngI(), rngF(), rngB(), rngD() ) ); + + std::shared_ptr o_sharedA = std::make_shared( random_basic_string(gen), + rngD(), rngI(), rngL() ); + + std::weak_ptr o_weakA = o_sharedA; + std::unique_ptr o_uniqueA( new PolyDerivedD( random_basic_string(gen), + rngD(), rngI(), rngL() ) ); + + auto pda = std::make_shared( rngI() ); + pda->vec.emplace_back( std::make_shared( rngI() ) ); + std::shared_ptr o_sharedLA = pda; + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_shared, o_weak, o_unique ); + oar( o_sharedLA ); + + oar( o_sharedA, o_weakA, o_uniqueA ); + } + + decltype(o_shared) i_shared; + decltype(o_weak) i_weak; + decltype(o_unique) i_unique; + + decltype(o_sharedLA) i_sharedLA; + + decltype(o_sharedA) i_sharedA; + decltype(o_weakA) i_weakA; + decltype(o_uniqueA) i_uniqueA; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_shared, i_weak, i_unique ); + iar( i_sharedLA ); + iar( i_sharedA, i_weakA, i_uniqueA ); + } + + auto i_locked = i_weak.lock(); + auto o_locked = o_weak.lock(); + + auto i_sharedLA2 = i_sharedLA->shared_from_this(); + + auto i_lockedA = i_weakA.lock(); + auto o_lockedA = o_weakA.lock(); + + #if CEREAL_THREAD_SAFE + std::lock_guard lock( testMutex ); + #endif + + CHECK_EQ(i_shared.get(), i_locked.get()); + CHECK_EQ(*dynamic_cast(i_shared.get()), *dynamic_cast(o_shared.get())); + CHECK_EQ(*dynamic_cast(i_shared.get()), *dynamic_cast(i_locked.get())); + CHECK_EQ(*dynamic_cast(i_locked.get()), *dynamic_cast(o_locked.get())); + CHECK_EQ(*dynamic_cast(i_unique.get()), *dynamic_cast(o_unique.get())); + + CHECK_EQ(*dynamic_cast(i_sharedLA.get()), *dynamic_cast(o_sharedLA.get())); + CHECK_EQ(*dynamic_cast(i_sharedLA2.get()), *dynamic_cast(o_sharedLA.get())); + + CHECK_EQ(i_sharedA.get(), i_lockedA.get()); + CHECK_EQ(*dynamic_cast(i_sharedA.get()), *dynamic_cast(o_sharedA.get())); + CHECK_EQ(*dynamic_cast(i_sharedA.get()), *dynamic_cast(i_lockedA.get())); + CHECK_EQ(*dynamic_cast(i_lockedA.get()), *dynamic_cast(o_lockedA.get())); + CHECK_EQ(*dynamic_cast(i_uniqueA.get()), *dynamic_cast(o_uniqueA.get())); + } +} + +#if CEREAL_THREAD_SAFE +template inline +void test_polymorphic_threading() +{ + std::vector> pool; + for( size_t i = 0; i < 100; ++i ) + pool.emplace_back( std::async( std::launch::async, + [](){ test_polymorphic(); return true; } ) ); + + for( auto & future : pool ) + future.wait(); + + for( auto & future : pool ) + CHECK_UNARY( future.get() ); +} +#endif // CEREAL_THREAD_SAFE + +#endif // CEREAL_TEST_POLYMORPHIC_H_ diff --git a/tpl/cereal/unittests/portability_test.cpp b/tpl/cereal/unittests/portability_test.cpp new file mode 100644 index 0000000..f3a7edb --- /dev/null +++ b/tpl/cereal/unittests/portability_test.cpp @@ -0,0 +1,202 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include +#include +#include + +#include +#include + +struct Data : std::enable_shared_from_this +{ + int32_t x; + int64_t y; + + Data( int32_t xx, int64_t yy ) : x(xx), y(yy) {} + + template + void serialize( Archive & ar ) + { + ar( x, y ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + int32_t xx; + int64_t yy; + ar( xx, yy ); + construct( xx, yy ); + } + + bool operator==( Data const & other ) const + { + if( x != other.x ) + { + std::cerr << "x=" << x << ", other.x=" << other.x << std::endl; + return false; + } + if( y != other.y ) + { + std::cerr << "y=" << y << ", other.y=" << other.y << std::endl; + return false; + } + + return true; + } +}; + +struct Data2 +{ + int32_t x; + int64_t y; + + Data2() = default; + Data2( int32_t xx, int64_t yy ) : x(xx), y(yy) {} + + template + void serialize( Archive & ar ) + { + ar( x, y ); + } + + bool operator==( Data2 const & other ) const + { + if( x != other.x ) + { + std::cerr << "x=" << x << ", other.x=" << other.x << std::endl; + return false; + } + if( y != other.y ) + { + std::cerr << "y=" << y << ", other.y=" << other.y << std::endl; + return false; + } + + return true; + } +}; + +const uint32_t AnotherCount = 32; + +struct Another +{ + Another() = default; + + Another( bool ) + { + for( uint32_t i = 0; i < AnotherCount; ++i ) + data[i] = Data2( i, i+1 ); + } + + std::map data; + + template + void serialize( Archive & ar ) + { + ar( data ); + } + + bool operator==( Another const & other ) const + { + auto iter = data.begin(); + auto oiter = other.data.begin(); + for( ; iter != data.end(); ++iter, ++oiter ) + { + if( !(iter->second == oiter->second) ) + { + std::cerr << "Data mismatch at i=" << iter->first << std::endl; + return false; + } + } + + return true; + } +}; + +int main( int, char ** argv ) +{ + std::vector vec_o; + for( int i = 0; i < 5; ++i ) + vec_o.emplace_back( true ); + + auto data_o = std::make_shared( 33, 64 ); + int32_t int_o = 7; + + std::cerr << "Portability test: " << argv[2] << "bit" << std::endl; + + if( std::string(argv[1]) == "load" ) + { + std::ifstream is("portable.cereal", std::ios::binary); + cereal::PortableBinaryInputArchive ar( is ); + + std::vector vec_i; + std::shared_ptr data_i; + int32_t int_i; + + ar( int_i ); + + if( int_i != int_o ) + { + std::cerr << "in " << int_i << ", out: " << int_o << std::endl; + return -1; + } + + ar( vec_i ); + ar( data_i ); + + if( vec_i != vec_o ) + { + std::cerr << "Input vector did not equal output vector" << std::endl; + return -1; + } + + if( !(*data_i == *data_o) ) + { + std::cerr << "Data did not match" << std::endl; + return -1; + } + } + else if( std::string(argv[1]) == "save" ) + { + std::ofstream os("portable.cereal", std::ios::binary); + cereal::PortableBinaryOutputArchive ar( os ); + + ar( int_o ); + ar( vec_o ); + ar( data_o ); + } + else // clean + { + std::remove( "portable.cereal" ); + } + + return 0; +} diff --git a/tpl/cereal/unittests/portable_binary_archive.cpp b/tpl/cereal/unittests/portable_binary_archive.cpp new file mode 100644 index 0000000..3b64b0b --- /dev/null +++ b/tpl/cereal/unittests/portable_binary_archive.cpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "portable_binary_archive.hpp" + +TEST_SUITE("portable_binary_archive"); + +#ifdef _MSC_VER +TEST_CASE("util") +{ + CHECK_EQ( cereal::util::demangledName(), "struct mynamespace::MyCustomClass" ); +} +#else +TEST_CASE("util") +{ + CHECK_EQ( cereal::util::demangledName(), "mynamespace::MyCustomClass" ); +} +#endif + +TEST_CASE("portable_binary_archive_endian_conversions") +{ + // (last parameter is whether we load as little endian) + test_endian_serialization( + cereal::PortableBinaryInputArchive::Options::BigEndian(), cereal::PortableBinaryOutputArchive::Options::BigEndian(), false ); + test_endian_serialization( + cereal::PortableBinaryInputArchive::Options::LittleEndian(), cereal::PortableBinaryOutputArchive::Options::BigEndian(), true ); + test_endian_serialization( + cereal::PortableBinaryInputArchive::Options::BigEndian(), cereal::PortableBinaryOutputArchive::Options::LittleEndian(), false ); + test_endian_serialization( + cereal::PortableBinaryInputArchive::Options::LittleEndian(), cereal::PortableBinaryOutputArchive::Options::LittleEndian(), true ); +} + +// Tests the default behavior to swap bytes to current machine's endianness +TEST_CASE("portable_binary_archive_default_behavior") +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(size_t i=0; i<100; ++i) + { + bool o_bool = random_value(gen) % 2 ? true : false; + uint8_t o_uint8 = random_value(gen); + int8_t o_int8 = random_value(gen); + uint16_t o_uint16 = random_value(gen); + int16_t o_int16 = random_value(gen); + uint32_t o_uint32 = random_value(gen); + int32_t o_int32 = random_value(gen); + uint64_t o_uint64 = random_value(gen); + int64_t o_int64 = random_value(gen); + float o_float = random_value(gen); + double o_double = random_value(gen); + + // swap the bytes on all of the data + CEREAL_TEST_SWAP_OUTPUT + + std::ostringstream os; + { + cereal::BinaryOutputArchive oar(os); + // manually insert incorrect endian encoding + oar(!cereal::portable_binary_detail::is_little_endian()); + + oar(o_bool); + oar(o_uint8); + oar(o_int8); + oar(o_uint16); + oar(o_int16); + oar(o_uint32); + oar(o_int32); + oar(o_uint64); + oar(o_int64); + oar(o_float); + oar(o_double); + } + + // swap back to original values + CEREAL_TEST_SWAP_OUTPUT + + bool i_bool = false; + uint8_t i_uint8 = 0; + int8_t i_int8 = 0; + uint16_t i_uint16 = 0; + int16_t i_int16 = 0; + uint32_t i_uint32 = 0; + int32_t i_int32 = 0; + uint64_t i_uint64 = 0; + int64_t i_int64 = 0; + float i_float = 0; + double i_double = 0; + + std::istringstream is(os.str()); + { + cereal::PortableBinaryInputArchive iar(is); + iar(i_bool); + iar(i_uint8); + iar(i_int8); + iar(i_uint16); + iar(i_int16); + iar(i_uint32); + iar(i_int32); + iar(i_uint64); + iar(i_int64); + iar(i_float); + iar(i_double); + } + + CEREAL_TEST_CHECK_EQUAL + } +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/portable_binary_archive.hpp b/tpl/cereal/unittests/portable_binary_archive.hpp new file mode 100644 index 0000000..b74c4f9 --- /dev/null +++ b/tpl/cereal/unittests/portable_binary_archive.hpp @@ -0,0 +1,156 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_PORTABLE_BINARY_ARCHIVE_H_ +#define CEREAL_TEST_PORTABLE_BINARY_ARCHIVE_H_ +#include "common.hpp" + +#include + +namespace mynamespace { struct MyCustomClass {}; } + +template +inline void swapBytes( T & t ) +{ + cereal::portable_binary_detail::swap_bytes( reinterpret_cast(&t) ); +} + +// swaps all output data +#define CEREAL_TEST_SWAP_OUTPUT \ + swapBytes(o_bool); \ + swapBytes(o_uint8); \ + swapBytes(o_int8); \ + swapBytes(o_uint16); \ + swapBytes(o_int16); \ + swapBytes(o_uint32); \ + swapBytes(o_int32); \ + swapBytes(o_uint64); \ + swapBytes(o_int64); \ + swapBytes(o_float); \ + swapBytes(o_double); + +#define CEREAL_TEST_CHECK_EQUAL \ + CHECK_EQ(i_bool , o_bool); \ + CHECK_EQ(i_uint8 , o_uint8); \ + CHECK_EQ(i_int8 , o_int8); \ + CHECK_EQ(i_uint16 , o_uint16); \ + CHECK_EQ(i_int16 , o_int16); \ + CHECK_EQ(i_uint32 , o_uint32); \ + CHECK_EQ(i_int32 , o_int32); \ + CHECK_EQ(i_uint64 , o_uint64); \ + CHECK_EQ(i_int64 , o_int64); \ + if( !std::isnan(i_float) && !std::isnan(o_float) ) CHECK_EQ(i_float , doctest::Approx(o_float).epsilon(1e-5F)); \ + if( !std::isnan(i_float) && !std::isnan(o_float) ) CHECK_EQ(i_double, doctest::Approx(o_double).epsilon(1e-5)); + +// Last parameter exists to keep everything hidden in options +template inline +void test_endian_serialization( typename IArchive::Options const & iOptions, typename OArchive::Options const & oOptions, const std::uint8_t inputLittleEndian ) +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(size_t i=0; i<100; ++i) + { + bool o_bool = random_value(gen) % 2 ? true : false; + uint8_t o_uint8 = random_value(gen); + int8_t o_int8 = random_value(gen); + uint16_t o_uint16 = random_value(gen); + int16_t o_int16 = random_value(gen); + uint32_t o_uint32 = random_value(gen); + int32_t o_int32 = random_value(gen); + uint64_t o_uint64 = random_value(gen); + int64_t o_int64 = random_value(gen); + float o_float = random_value(gen); + double o_double = random_value(gen); + + std::vector o_vector(100); + for(auto & elem : o_vector) + elem = random_value(gen); + + std::ostringstream os; + { + OArchive oar(os, oOptions); + oar(o_bool); + oar(o_uint8); + oar(o_int8); + oar(o_uint16); + oar(o_int16); + oar(o_uint32); + oar(o_int32); + oar(o_uint64); + oar(o_int64); + oar(o_float); + oar(o_double); + // We can't test vector directly here since we are artificially interfering with the endianness, + // which can result in the size being incorrect + oar(cereal::binary_data( o_vector.data(), static_cast( o_vector.size() * sizeof(int32_t) ) )); + } + + bool i_bool = false; + uint8_t i_uint8 = 0; + int8_t i_int8 = 0; + uint16_t i_uint16 = 0; + int16_t i_int16 = 0; + uint32_t i_uint32 = 0; + int32_t i_int32 = 0; + uint64_t i_uint64 = 0; + int64_t i_int64 = 0; + float i_float = 0; + double i_double = 0; + std::vector i_vector(100); + + std::istringstream is(os.str()); + { + IArchive iar(is, iOptions); + iar(i_bool); + iar(i_uint8); + iar(i_int8); + iar(i_uint16); + iar(i_int16); + iar(i_uint32); + iar(i_int32); + iar(i_uint64); + iar(i_int64); + iar(i_float); + iar(i_double); + iar(cereal::binary_data( i_vector.data(), static_cast( i_vector.size() * sizeof(int32_t) ) )); + } + + // Convert to big endian if we expect to read big and didn't start big + if( cereal::portable_binary_detail::is_little_endian() ^ inputLittleEndian ) // Convert to little endian if + { + CEREAL_TEST_SWAP_OUTPUT + for( auto & val : o_vector ) + swapBytes(val); + } + + CEREAL_TEST_CHECK_EQUAL + + check_collection(i_vector, o_vector); + } +} + +#endif // CEREAL_TEST_PORTABLE_BINARY_ARCHIVE_H_ diff --git a/tpl/cereal/unittests/priority_queue.cpp b/tpl/cereal/unittests/priority_queue.cpp new file mode 100644 index 0000000..e8b997f --- /dev/null +++ b/tpl/cereal/unittests/priority_queue.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "priority_queue.hpp" + +TEST_SUITE("priority_queue"); + +TEST_CASE("binary_priority_queue") +{ + test_priority_queue(); +} + +TEST_CASE("portable_binary_priority_queue") +{ + test_priority_queue(); +} + +TEST_CASE("xml_priority_queue") +{ + test_priority_queue(); +} + +TEST_CASE("json_priority_queue") +{ + test_priority_queue(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/priority_queue.hpp b/tpl/cereal/unittests/priority_queue.hpp new file mode 100644 index 0000000..27d31a5 --- /dev/null +++ b/tpl/cereal/unittests/priority_queue.hpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_PRIORITY_QUEUE_H_ +#define CEREAL_TEST_PRIORITY_QUEUE_H_ +#include "common.hpp" + +template inline +void test_priority_queue() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::priority_queue o_podpriority_queue; + for(int j=0; j<100; ++j) + o_podpriority_queue.push(random_value(gen)); + + std::priority_queue o_iserpriority_queue; + for(int j=0; j<100; ++j) + o_iserpriority_queue.push({ random_value(gen), random_value(gen) }); + + std::priority_queue o_isplpriority_queue; + for(int j=0; j<100; ++j) + o_isplpriority_queue.push({ random_value(gen), random_value(gen) }); + + std::priority_queue o_eserpriority_queue; + for(int j=0; j<100; ++j) + o_eserpriority_queue.push({ random_value(gen), random_value(gen) }); + + std::priority_queue o_esplpriority_queue; + for(int j=0; j<100; ++j) + o_esplpriority_queue.push({ random_value(gen), random_value(gen) }); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podpriority_queue); + oar(o_iserpriority_queue); + oar(o_isplpriority_queue); + oar(o_eserpriority_queue); + oar(o_esplpriority_queue); + } + + std::priority_queue i_podpriority_queue; + std::priority_queue i_iserpriority_queue; + std::priority_queue i_isplpriority_queue; + std::priority_queue i_eserpriority_queue; + std::priority_queue i_esplpriority_queue; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podpriority_queue); + iar(i_iserpriority_queue); + iar(i_isplpriority_queue); + iar(i_eserpriority_queue); + iar(i_esplpriority_queue); + } + + auto & i_podpriority_queue_c = cereal::queue_detail::container(i_podpriority_queue); + auto & i_iserpriority_queue_c = cereal::queue_detail::container(i_iserpriority_queue); + auto & i_isplpriority_queue_c = cereal::queue_detail::container(i_isplpriority_queue); + auto & i_eserpriority_queue_c = cereal::queue_detail::container(i_eserpriority_queue); + auto & i_esplpriority_queue_c = cereal::queue_detail::container(i_esplpriority_queue); + + auto & o_podpriority_queue_c = cereal::queue_detail::container(o_podpriority_queue); + auto & o_iserpriority_queue_c = cereal::queue_detail::container(o_iserpriority_queue); + auto & o_isplpriority_queue_c = cereal::queue_detail::container(o_isplpriority_queue); + auto & o_eserpriority_queue_c = cereal::queue_detail::container(o_eserpriority_queue); + auto & o_esplpriority_queue_c = cereal::queue_detail::container(o_esplpriority_queue); + + check_collection(i_podpriority_queue_c, o_podpriority_queue_c); + check_collection(i_iserpriority_queue_c, o_iserpriority_queue_c); + check_collection(i_isplpriority_queue_c, o_isplpriority_queue_c); + check_collection(i_eserpriority_queue_c, o_eserpriority_queue_c); + check_collection(i_esplpriority_queue_c, o_esplpriority_queue_c); + } +} + +#endif // CEREAL_TEST_PRIORITY_QUEUE_H_ diff --git a/tpl/cereal/unittests/queue.cpp b/tpl/cereal/unittests/queue.cpp new file mode 100644 index 0000000..5f87705 --- /dev/null +++ b/tpl/cereal/unittests/queue.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "queue.hpp" + +TEST_SUITE("queue"); + +TEST_CASE("binary_queue") +{ + test_queue(); +} + +TEST_CASE("portable_binary_queue") +{ + test_queue(); +} + +TEST_CASE("xml_queue") +{ + test_queue(); +} + +TEST_CASE("json_queue") +{ + test_queue(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/queue.hpp b/tpl/cereal/unittests/queue.hpp new file mode 100644 index 0000000..3b0484d --- /dev/null +++ b/tpl/cereal/unittests/queue.hpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_QUEUE_H_ +#define CEREAL_TEST_QUEUE_H_ +#include "common.hpp" + +template inline +void test_queue() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::queue o_podqueue; + for(int j=0; j<100; ++j) + o_podqueue.push(random_value(gen)); + + std::queue o_iserqueue; + for(int j=0; j<100; ++j) + o_iserqueue.push({ random_value(gen), random_value(gen) }); + + std::queue o_isplqueue; + for(int j=0; j<100; ++j) + o_isplqueue.push({ random_value(gen), random_value(gen) }); + + std::queue o_eserqueue; + for(int j=0; j<100; ++j) + o_eserqueue.push({ random_value(gen), random_value(gen) }); + + std::queue o_esplqueue; + for(int j=0; j<100; ++j) + o_esplqueue.push({ random_value(gen), random_value(gen) }); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podqueue); + oar(o_iserqueue); + oar(o_isplqueue); + oar(o_eserqueue); + oar(o_esplqueue); + } + + std::queue i_podqueue; + std::queue i_iserqueue; + std::queue i_isplqueue; + std::queue i_eserqueue; + std::queue i_esplqueue; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podqueue); + iar(i_iserqueue); + iar(i_isplqueue); + iar(i_eserqueue); + iar(i_esplqueue); + } + + auto & i_podqueue_c = cereal::queue_detail::container(i_podqueue); + auto & i_iserqueue_c = cereal::queue_detail::container(i_iserqueue); + auto & i_isplqueue_c = cereal::queue_detail::container(i_isplqueue); + auto & i_eserqueue_c = cereal::queue_detail::container(i_eserqueue); + auto & i_esplqueue_c = cereal::queue_detail::container(i_esplqueue); + + auto & o_podqueue_c = cereal::queue_detail::container(o_podqueue); + auto & o_iserqueue_c = cereal::queue_detail::container(o_iserqueue); + auto & o_isplqueue_c = cereal::queue_detail::container(o_isplqueue); + auto & o_eserqueue_c = cereal::queue_detail::container(o_eserqueue); + auto & o_esplqueue_c = cereal::queue_detail::container(o_esplqueue); + + check_collection(i_podqueue_c, o_podqueue_c); + check_collection(i_iserqueue_c, o_iserqueue_c); + check_collection(i_isplqueue_c, o_isplqueue_c); + check_collection(i_eserqueue_c, o_eserqueue_c); + check_collection(i_esplqueue_c, o_esplqueue_c); + } +} + +#endif // CEREAL_TEST_QUEUE_H_ diff --git a/tpl/cereal/unittests/run_portability_test.cmake b/tpl/cereal/unittests/run_portability_test.cmake new file mode 100644 index 0000000..2841e6c --- /dev/null +++ b/tpl/cereal/unittests/run_portability_test.cmake @@ -0,0 +1,16 @@ +macro(EXEC_CMD_CHECK) + message("running ${ARGN}") + execute_process(COMMAND ${ARGN} RESULT_VARIABLE CMD_RESULT) + if(CMD_RESULT) + message(FATAL_ERROR "Error running ${ARGN}") + endif() +endmacro() + +set(PORTABILITY_TEST_32 "${PORTABILITY_TEST_DIR}/portability_test32") +set(PORTABILITY_TEST_64 "${PORTABILITY_TEST_DIR}/portability_test64") + +exec_cmd_check(${PORTABILITY_TEST_64} save 64) +exec_cmd_check(${PORTABILITY_TEST_32} load 32) +exec_cmd_check(${PORTABILITY_TEST_32} save 32) +exec_cmd_check(${PORTABILITY_TEST_64} load 64) +exec_cmd_check(${PORTABILITY_TEST_64} remove 64) diff --git a/tpl/cereal/unittests/run_valgrind.sh b/tpl/cereal/unittests/run_valgrind.sh new file mode 100755 index 0000000..022b1b0 --- /dev/null +++ b/tpl/cereal/unittests/run_valgrind.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +TESTS=./test_* + +for f in $TESTS + do + valgrind --tool=memcheck --leak-check=full $f + done diff --git a/tpl/cereal/unittests/set.cpp b/tpl/cereal/unittests/set.cpp new file mode 100644 index 0000000..cc2e00c --- /dev/null +++ b/tpl/cereal/unittests/set.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "set.hpp" + +TEST_SUITE("set"); + +TEST_CASE("binary_set") +{ + test_set(); +} + +TEST_CASE("portable_binary_set") +{ + test_set(); +} + +TEST_CASE("xml_set") +{ + test_set(); +} + +TEST_CASE("json_set") +{ + test_set(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/set.hpp b/tpl/cereal/unittests/set.hpp new file mode 100644 index 0000000..6e39942 --- /dev/null +++ b/tpl/cereal/unittests/set.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_SET_H_ +#define CEREAL_TEST_SET_H_ +#include "common.hpp" + +template inline +void test_set() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::set o_podset; + for(int j=0; j<100; ++j) + o_podset.insert(random_value(gen)); + + std::set o_iserset; + for(int j=0; j<100; ++j) + o_iserset.insert({ random_value(gen), random_value(gen) }); + + std::set o_isplset; + for(int j=0; j<100; ++j) + o_isplset.insert({ random_value(gen), random_value(gen) }); + + std::set o_eserset; + for(int j=0; j<100; ++j) + o_eserset.insert({ random_value(gen), random_value(gen) }); + + std::set o_esplset; + for(int j=0; j<100; ++j) + o_esplset.insert({ random_value(gen), random_value(gen) }); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podset); + oar(o_iserset); + oar(o_isplset); + oar(o_eserset); + oar(o_esplset); + } + + std::set i_podset; + std::set i_iserset; + std::set i_isplset; + std::set i_eserset; + std::set i_esplset; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podset); + iar(i_iserset); + iar(i_isplset); + iar(i_eserset); + iar(i_esplset); + } + + check_collection(i_podset, o_podset); + check_collection(i_iserset, o_iserset); + check_collection(i_isplset, o_isplset); + check_collection(i_eserset, o_eserset); + check_collection(i_esplset, o_esplset); + } +} + +#endif // CEREAL_TEST_SET_H_ diff --git a/tpl/cereal/unittests/stack.cpp b/tpl/cereal/unittests/stack.cpp new file mode 100644 index 0000000..3e4212c --- /dev/null +++ b/tpl/cereal/unittests/stack.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "stack.hpp" + +TEST_SUITE("stack"); + +TEST_CASE("binary_stack") +{ + test_stack(); +} + +TEST_CASE("portable_binary_stack") +{ + test_stack(); +} + +TEST_CASE("xml_stack") +{ + test_stack(); +} + +TEST_CASE("json_stack") +{ + test_stack(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/stack.hpp b/tpl/cereal/unittests/stack.hpp new file mode 100644 index 0000000..4ea1ae3 --- /dev/null +++ b/tpl/cereal/unittests/stack.hpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_STACK_H_ +#define CEREAL_TEST_STACK_H_ +#include "common.hpp" + +template inline +void test_stack() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::stack o_podstack; + for(int j=0; j<100; ++j) + o_podstack.push(random_value(gen)); + + std::stack o_iserstack; + for(int j=0; j<100; ++j) + o_iserstack.push({ random_value(gen), random_value(gen) }); + + std::stack o_isplstack; + for(int j=0; j<100; ++j) + o_isplstack.push({ random_value(gen), random_value(gen) }); + + std::stack o_eserstack; + for(int j=0; j<100; ++j) + o_eserstack.push({ random_value(gen), random_value(gen) }); + + std::stack o_esplstack; + for(int j=0; j<100; ++j) + o_esplstack.push({ random_value(gen), random_value(gen) }); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podstack); + oar(o_iserstack); + oar(o_isplstack); + oar(o_eserstack); + oar(o_esplstack); + } + + std::stack i_podstack; + std::stack i_iserstack; + std::stack i_isplstack; + std::stack i_eserstack; + std::stack i_esplstack; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podstack); + iar(i_iserstack); + iar(i_isplstack); + iar(i_eserstack); + iar(i_esplstack); + } + + auto & i_podstack_c = cereal::stack_detail::container(i_podstack); + auto & i_iserstack_c = cereal::stack_detail::container(i_iserstack); + auto & i_isplstack_c = cereal::stack_detail::container(i_isplstack); + auto & i_eserstack_c = cereal::stack_detail::container(i_eserstack); + auto & i_esplstack_c = cereal::stack_detail::container(i_esplstack); + + auto & o_podstack_c = cereal::stack_detail::container(o_podstack); + auto & o_iserstack_c = cereal::stack_detail::container(o_iserstack); + auto & o_isplstack_c = cereal::stack_detail::container(o_isplstack); + auto & o_eserstack_c = cereal::stack_detail::container(o_eserstack); + auto & o_esplstack_c = cereal::stack_detail::container(o_esplstack); + + check_collection(i_podstack_c, o_podstack_c ); + check_collection(i_iserstack_c, o_iserstack_c); + check_collection(i_isplstack_c, o_isplstack_c); + check_collection(i_eserstack_c, o_eserstack_c); + check_collection(i_esplstack_c, o_esplstack_c); + } +} + +#endif // CEREAL_TEST_STACK_H_ diff --git a/tpl/cereal/unittests/structs.cpp b/tpl/cereal/unittests/structs.cpp new file mode 100644 index 0000000..117e7cc --- /dev/null +++ b/tpl/cereal/unittests/structs.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "structs.hpp" + +TEST_SUITE("structs"); + +TEST_CASE("binary_structs") +{ + test_structs(); +} + +TEST_CASE("portable_binary_structs") +{ + test_structs(); +} + +TEST_CASE("xml_structs") +{ + test_structs(); +} + +TEST_CASE("json_structs") +{ + test_structs(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/structs.hpp b/tpl/cereal/unittests/structs.hpp new file mode 100644 index 0000000..db7ce13 --- /dev/null +++ b/tpl/cereal/unittests/structs.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_STRUCTS_H_ +#define CEREAL_TEST_STRUCTS_H_ +#include "common.hpp" + +template inline +void test_structs() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + StructInternalSerialize o_iser = { random_value(gen), random_value(gen) }; + StructInternalSplit o_ispl = { random_value(gen), random_value(gen) }; + StructExternalSerialize o_eser = { random_value(gen), random_value(gen) }; + StructExternalSplit o_espl = { random_value(gen), random_value(gen) }; + + std::ostringstream os; + { + OArchive oar(os); + oar( o_iser, o_ispl, o_eser, o_espl); + } + + StructInternalSerialize i_iser; + StructInternalSplit i_ispl; + StructExternalSerialize i_eser; + StructExternalSplit i_espl; + + std::istringstream is(os.str()); + { + IArchive iar(is); + iar( i_iser, i_ispl, i_eser, i_espl); + } + + CHECK_EQ(i_iser, o_iser); + CHECK_EQ(i_ispl, o_ispl); + CHECK_EQ(i_eser, o_eser); + CHECK_EQ(i_espl, o_espl); + } +} + +#endif // CEREAL_TEST_STRUCTS_H_ diff --git a/tpl/cereal/unittests/structs_minimal.cpp b/tpl/cereal/unittests/structs_minimal.cpp new file mode 100644 index 0000000..d78f819 --- /dev/null +++ b/tpl/cereal/unittests/structs_minimal.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "structs_minimal.hpp" + +TEST_SUITE("structs_minimal"); + +TEST_CASE("binary_structs_minimal") +{ + test_structs_minimal(); +} + +TEST_CASE("portable_binary_structs_minimal") +{ + test_structs_minimal(); +} + +TEST_CASE("xml_structs_minimal") +{ + test_structs_minimal(); +} + +TEST_CASE("json_structs_minimal") +{ + test_structs_minimal(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/structs_minimal.hpp b/tpl/cereal/unittests/structs_minimal.hpp new file mode 100644 index 0000000..57ccd49 --- /dev/null +++ b/tpl/cereal/unittests/structs_minimal.hpp @@ -0,0 +1,254 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_STRUCTS_MINIMAL_H_ +#define CEREAL_TEST_STRUCTS_MINIMAL_H_ +#include "common.hpp" + +class MemberMinimal +{ + public: + MemberMinimal() = default; + MemberMinimal( std::string const & str ) : x( str ) {} + + protected: + friend class cereal::access; + + template + std::string save_minimal( Archive const & ) const + { + return x; + } + + template + void load_minimal( Archive const &, std::string const & str ) + { + x = str; + } + + public: + std::string x; +}; + +class MemberMinimalVersioned +{ + public: + MemberMinimalVersioned() = default; + MemberMinimalVersioned( double d ) : x( d ) {} + + protected: + friend class cereal::access; + + template + double save_minimal( Archive const &, const std::uint32_t ) const + { + return x; + } + + template + void load_minimal( Archive const &, double const & d, const std::uint32_t ) + { + x = d; + } + + public: + double x; +}; + +struct NonMemberMinimal +{ + NonMemberMinimal() = default; + NonMemberMinimal( std::uint32_t xx ) : x(xx) {} + std::uint32_t x; +}; + +template inline +std::uint32_t save_minimal( Archive const &, NonMemberMinimal const & nmm ) +{ + return nmm.x; +} + +template inline +void load_minimal( Archive const &, NonMemberMinimal & nmm, std::uint32_t const & data ) +{ + nmm.x = data; +} + +struct NonMemberMinimalVersioned +{ + NonMemberMinimalVersioned() = default; + NonMemberMinimalVersioned( bool xx ) : x(xx) {} + bool x; +}; + +template inline +bool save_minimal( Archive const &, NonMemberMinimalVersioned const & nmm, std::uint32_t const ) +{ + return nmm.x; +} + +template inline +void load_minimal( Archive const &, NonMemberMinimalVersioned & nmm, bool const & data, std::uint32_t const ) +{ + nmm.x = data; +} + +struct TestStruct +{ + TestStruct() = default; + TestStruct( std::string const & s, double d, std::uint32_t u, bool b ) : + mm(s), mmv(d), nmm(u), nmmv(b) {} + + template + void serialize( Archive & ar ) + { + ar( mm, mmv ); + ar( nmm, nmmv ); + } + + MemberMinimal mm; + MemberMinimalVersioned mmv; + NonMemberMinimal nmm; + NonMemberMinimalVersioned nmmv; +}; + +struct Issue79Struct +{ + Issue79Struct() = default; + Issue79Struct( std::int32_t xx ) : x(xx) {} + std::int32_t x; +}; + +template ::value || + std::is_same::value> = cereal::traits::sfinae> +inline std::string save_minimal( Archive const &, Issue79Struct const & val ) +{ + return std::to_string( val.x ); +} + +template ::value || + std::is_same::value> = cereal::traits::sfinae> +inline void load_minimal( Archive const &, Issue79Struct & val, std::string const & str ) +{ + val.x = std::stoi( str ); +} + +template ::value || + std::is_same::value> = cereal::traits::sfinae> +inline std::int32_t save_minimal( Archive const &, Issue79Struct const & val ) +{ + return val.x; +} + +template ::value || + std::is_same::value> = cereal::traits::sfinae> +inline void load_minimal( Archive const &, Issue79Struct & val, std::int32_t const & xx ) +{ + val.x = xx; +} + +struct Issue79StructInternal +{ + Issue79StructInternal() = default; + Issue79StructInternal( std::int32_t xx ) : x(xx) {} + std::int32_t x; + + template ::value || + std::is_same::value> = cereal::traits::sfinae> + inline std::string save_minimal( Archive const & ) const + { + return std::to_string( x ); + } + + template ::value || + std::is_same::value> = cereal::traits::sfinae> + inline void load_minimal( Archive const &, std::string const & str ) + { + x = std::stoi( str ); + } + + template ::value || + std::is_same::value> = cereal::traits::sfinae> + inline std::int32_t save_minimal( Archive const & ) const + { + return x; + } + + template ::value || + std::is_same::value> = cereal::traits::sfinae> + inline void load_minimal( Archive const &, std::int32_t const & xx ) + { + x = xx; + } +}; + +template inline +void test_structs_minimal() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + TestStruct o_struct = { random_basic_string(gen), random_value(gen), + random_value(gen), random_value(gen) % 2 ? true : false }; + + Issue79Struct o_struct2 = { random_value(gen) }; + Issue79StructInternal o_struct3 = { random_value(gen) }; + + std::ostringstream os; + { + OArchive oar(os); + oar( o_struct ); + oar( o_struct2 ); + oar( o_struct3 ); + } + + decltype(o_struct) i_struct; + decltype(o_struct2) i_struct2; + decltype(o_struct3) i_struct3; + + std::istringstream is(os.str()); + { + IArchive iar(is); + iar( i_struct ); + iar( i_struct2 ); + iar( i_struct3 ); + } + + CHECK_EQ(o_struct.mm.x, i_struct.mm.x); + CHECK_EQ(o_struct.mmv.x, doctest::Approx(i_struct.mmv.x).epsilon(1e-5)); + + CHECK_EQ(o_struct.nmm.x, i_struct.nmm.x); + CHECK_EQ(o_struct.nmmv.x, i_struct.nmmv.x); + + CHECK_EQ(o_struct2.x, i_struct2.x); + + CHECK_EQ(o_struct3.x, i_struct3.x); + } +} + +#endif // CEREAL_TEST_STRUCTS_MINIMAL_H_ diff --git a/tpl/cereal/unittests/structs_specialized.cpp b/tpl/cereal/unittests/structs_specialized.cpp new file mode 100644 index 0000000..1aa3e67 --- /dev/null +++ b/tpl/cereal/unittests/structs_specialized.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "structs_specialized.hpp" + +TEST_SUITE("structs_specialized"); + +TEST_CASE("binary_structs_specialized") +{ + test_structs_specialized(); +} + +TEST_CASE("portable_binary_structs_specialized") +{ + test_structs_specialized(); +} + +TEST_CASE("xml_structs_specialized") +{ + test_structs_specialized(); +} + +TEST_CASE("json_structs_specialized") +{ + test_structs_specialized(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/structs_specialized.hpp b/tpl/cereal/unittests/structs_specialized.hpp new file mode 100644 index 0000000..8daec99 --- /dev/null +++ b/tpl/cereal/unittests/structs_specialized.hpp @@ -0,0 +1,461 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_STRUCTS_MINIMAL_H_ +#define CEREAL_TEST_STRUCTS_MINIMAL_H_ +#include "common.hpp" + +struct BogusBase +{ + template + void serialize( Archive & ) {} + + template + void save( Archive & ) const {} + + template + void load( Archive & ) {} + + template + int save_minimal( Archive const & ) const { return 0; } + + template + void load_minimal( Archive const &, int const & ) {} +}; + +struct BogusBaseVersioned +{ + template + void serialize( Archive &, const std::uint32_t ) {} + + template + void save( Archive &, const std::uint32_t ) const {} + + template + void load( Archive &, const std::uint32_t ) {} + + template + int save_minimal( Archive const &, const std::uint32_t ) const { return 0; } + + template + void load_minimal( Archive const &, int const &, const std::uint32_t ) {} +}; + +struct BogusBasePolymorphic +{ + template + void serialize( Archive & ) {} + + virtual void doesNothing() {} +}; + +class SpecializedMSerialize : public BogusBase +{ + public: + SpecializedMSerialize() = default; + SpecializedMSerialize( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + void serialize( Archive & ar ) + { + ar( x ); + } +}; + +class SpecializedMSerializeVersioned : public BogusBaseVersioned +{ + public: + SpecializedMSerializeVersioned() = default; + SpecializedMSerializeVersioned( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + void serialize( Archive & ar, const std::uint32_t ) + { + ar( x ); + } +}; + +class SpecializedMSplit : public BogusBase +{ + public: + SpecializedMSplit() = default; + SpecializedMSplit( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + void save( Archive & ar ) const + { + ar( x ); + } + + template + void load( Archive & ar ) + { + ar( x ); + } +}; + +class SpecializedMSplitVersioned : public BogusBaseVersioned +{ + public: + SpecializedMSplitVersioned() = default; + SpecializedMSplitVersioned( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + void save( Archive & ar, const std::uint32_t ) const + { + ar( x ); + } + + template + void load( Archive & ar, const std::uint32_t ) + { + ar( x ); + } +}; + +class SpecializedMSplitPolymorphic : public BogusBasePolymorphic +{ + public: + SpecializedMSplitPolymorphic() = default; + SpecializedMSplitPolymorphic( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + void save( Archive & ar ) const + { + ar( x ); + } + + template + void load( Archive & ar ) + { + ar( x ); + } +}; + +class SpecializedMSplitMinimal : public BogusBase +{ + public: + SpecializedMSplitMinimal() = default; + SpecializedMSplitMinimal( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + int save_minimal( Archive const & ) const + { + return x; + } + + template + void load_minimal( Archive const &, int const & value ) + { + x = value; + } +}; + +class SpecializedMSplitVersionedMinimal : public BogusBaseVersioned +{ + public: + SpecializedMSplitVersionedMinimal() = default; + SpecializedMSplitVersionedMinimal( int xx ) : x(xx) {} + + int x; + + private: + friend class cereal::access; + template + int save_minimal( Archive const &, const std::uint32_t ) const + { + return x; + } + + template + void load_minimal( Archive const &, int const & value, const std::uint32_t ) + { + x = value; + } +}; + +class SpecializedNMSerialize : public BogusBase +{ + public: + SpecializedNMSerialize() = default; + SpecializedNMSerialize( int xx ) : x(xx) {} + + int x; +}; + +template +void serialize( Archive & ar, SpecializedNMSerialize & s ) +{ + ar( s.x ); +} + +class SpecializedNMSerializeVersioned : public BogusBaseVersioned +{ + public: + SpecializedNMSerializeVersioned() = default; + SpecializedNMSerializeVersioned( int xx ) : x(xx) {} + + int x; +}; + +template +void serialize( Archive & ar, SpecializedNMSerializeVersioned & s ) +{ + ar( s.x ); +} + +class SpecializedNMSplit : public BogusBase +{ + public: + SpecializedNMSplit() = default; + SpecializedNMSplit( int xx ) : x(xx) {} + + int x; +}; + +template +void load( Archive & ar, SpecializedNMSplit & s ) +{ + ar( s.x ); +} + +template +void save( Archive & ar, SpecializedNMSplit const & s ) +{ + ar( s.x ); +} + +class SpecializedNMSplitVersioned : public BogusBaseVersioned +{ + public: + SpecializedNMSplitVersioned() = default; + SpecializedNMSplitVersioned( int xx ) : x(xx) {} + + int x; +}; + +template +void load( Archive & ar, SpecializedNMSplitVersioned & s, const std::uint32_t ) +{ + ar( s.x ); +} + +template +void save( Archive & ar, SpecializedNMSplitVersioned const & s, const std::uint32_t ) +{ + ar( s.x ); +} + +class SpecializedNMSplitMinimal : public BogusBase +{ + public: + SpecializedNMSplitMinimal() = default; + SpecializedNMSplitMinimal( int xx ) : x(xx) {} + + int x; +}; + +template +void load_minimal( Archive const &, SpecializedNMSplitMinimal & s, int const & value ) +{ + s.x = value; +} + +template +int save_minimal( Archive const &, SpecializedNMSplitMinimal const & s ) +{ + return s.x; +} + +class SpecializedNMSplitVersionedMinimal : public BogusBaseVersioned +{ + public: + SpecializedNMSplitVersionedMinimal() = default; + SpecializedNMSplitVersionedMinimal( int xx ) : x(xx) {} + + int x; +}; + +template +void load_minimal( Archive const &, SpecializedNMSplitVersionedMinimal & s, int const & value, const std::uint32_t ) +{ + s.x = value; +} + +template +int save_minimal( Archive const &, SpecializedNMSplitVersionedMinimal const & s, const std::uint32_t ) +{ + return s.x; +} + +namespace cereal +{ + template struct specialize {}; + template struct specialize {}; + + template struct specialize {}; + template struct specialize {}; + template struct specialize {}; + + template struct specialize {}; + template struct specialize {}; + + template struct specialize {}; + template struct specialize {}; + + template struct specialize {}; + template struct specialize {}; + + template struct specialize {}; + template struct specialize {}; +} + +CEREAL_REGISTER_TYPE(SpecializedMSplitPolymorphic) +CEREAL_REGISTER_POLYMORPHIC_RELATION(BogusBasePolymorphic, SpecializedMSplitPolymorphic) + +template inline +void test_structs_specialized() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + SpecializedMSerialize o_iser = { random_value(gen) }; + SpecializedMSerializeVersioned o_iserv = { random_value(gen) }; + + SpecializedMSplit o_ispl = { random_value(gen) }; + SpecializedMSplitVersioned o_isplv = { random_value(gen) }; + + // added re: issue #180 + std::shared_ptr o_shared_ispl = std::make_shared( random_value(gen) ); + + SpecializedMSplitMinimal o_isplm = { random_value(gen) }; + SpecializedMSplitVersionedMinimal o_isplvm = { random_value(gen) }; + + SpecializedNMSerialize o_eser = { random_value(gen) }; + SpecializedNMSerializeVersioned o_eserv = { random_value(gen) }; + + SpecializedNMSplit o_espl = { random_value(gen) }; + SpecializedNMSplitVersioned o_esplv = { random_value(gen) }; + + SpecializedNMSplitMinimal o_esplm = { random_value(gen) }; + SpecializedNMSplitVersionedMinimal o_esplvm = { random_value(gen) }; + + + std::ostringstream os; + { + OArchive oar(os); + + oar( o_iser, o_iserv, + o_ispl, o_isplv, o_shared_ispl, + o_isplm, o_isplvm, + o_eser, o_eserv, + o_espl, o_esplv, + o_esplm, o_esplvm ); + } + + decltype(o_iser) i_iser; + decltype(o_iserv) i_iserv; + + decltype(o_ispl) i_ispl; + decltype(o_isplv) i_isplv; + + decltype(o_shared_ispl) i_shared_ispl; + + decltype(o_isplm) i_isplm; + decltype(o_isplvm) i_isplvm; + + decltype(o_eser) i_eser; + decltype(o_eserv) i_eserv; + + decltype(o_espl) i_espl; + decltype(o_esplv) i_esplv; + + decltype(o_esplm) i_esplm; + decltype(o_esplvm) i_esplvm; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( i_iser, i_iserv, + i_ispl, i_isplv, i_shared_ispl, + i_isplm, i_isplvm, + i_eser, i_eserv, + i_espl, i_esplv, + i_esplm, i_esplvm ); + } + + CHECK_EQ(i_iser.x, o_iser.x); + CHECK_EQ(i_iserv.x, o_iserv.x); + + CHECK_EQ(i_ispl.x, o_ispl.x); + CHECK_EQ(i_isplv.x, o_isplv.x); + + CHECK_EQ(dynamic_cast(i_shared_ispl.get())->x, dynamic_cast(o_shared_ispl.get())->x); + + CHECK_EQ(i_isplm.x, o_isplm.x); + CHECK_EQ(i_isplvm.x, o_isplvm.x); + + CHECK_EQ(i_eser.x, o_eser.x); + CHECK_EQ(i_eserv.x, o_eserv.x); + + CHECK_EQ(i_espl.x, o_espl.x); + CHECK_EQ(i_esplv.x, o_esplv.x); + + CHECK_EQ(i_esplm.x, o_esplm.x); + CHECK_EQ(i_esplvm.x, o_esplvm.x); + } +} + +#endif // CEREAL_TEST_STRUCTS_SPECIALIZED_H_ diff --git a/tpl/cereal/unittests/tuple.cpp b/tpl/cereal/unittests/tuple.cpp new file mode 100644 index 0000000..d3ea635 --- /dev/null +++ b/tpl/cereal/unittests/tuple.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "tuple.hpp" + +TEST_SUITE("tuple"); + +TEST_CASE("binary_tuple") +{ + test_tuple(); +} + +TEST_CASE("portable_binary_tuple") +{ + test_tuple(); +} + +TEST_CASE("xml_tuple") +{ + test_tuple(); +} + +TEST_CASE("json_tuple") +{ + test_tuple(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/tuple.hpp b/tpl/cereal/unittests/tuple.hpp new file mode 100644 index 0000000..edb25b2 --- /dev/null +++ b/tpl/cereal/unittests/tuple.hpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_TUPLE_H_ +#define CEREAL_TEST_TUPLE_H_ +#include "common.hpp" + +template inline +void test_tuple() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rng = [&](){ return random_value(gen); }; + + for(int ii=0; ii<100; ++ii) + { + auto o_podtuple = std::make_tuple( rng(), rng(), rng(), rng() ); + auto o_podtuple11 = std::make_tuple( rng(), rng(), rng(), rng(), rng(), rng(), + rng(), rng(), rng(), rng(), rng() ); + auto o_isertuple = std::make_tuple( StructInternalSerialize( rng(), rng() ), + StructInternalSerialize( rng(), rng() ), + StructInternalSerialize( rng(), rng() ), + StructInternalSerialize( rng(), rng() ) ); + auto o_ispltuple = std::make_tuple( StructInternalSplit( rng(), rng() ), + StructInternalSplit( rng(), rng() ), + StructInternalSplit( rng(), rng() ), + StructInternalSplit( rng(), rng() ) ); + auto o_esertuple = std::make_tuple( StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ) ); + auto o_espltuple = std::make_tuple( StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ), + StructExternalSerialize( rng(), rng() ) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podtuple); + oar(o_podtuple11); + oar(o_isertuple); + oar(o_ispltuple); + oar(o_esertuple); + oar(o_espltuple); + } + + decltype( o_podtuple ) i_podtuple; + decltype( o_podtuple11 ) i_podtuple11; + decltype( o_isertuple ) i_isertuple; + decltype( o_ispltuple ) i_ispltuple; + decltype( o_esertuple ) i_esertuple; + decltype( o_espltuple ) i_espltuple; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podtuple); + iar(i_podtuple11); + iar(i_isertuple); + iar(i_ispltuple); + iar(i_esertuple); + iar(i_espltuple); + } + + CHECK_EQ( i_podtuple, o_podtuple); + CHECK_EQ( i_podtuple11, o_podtuple11); + CHECK_EQ( i_isertuple, o_isertuple); + CHECK_EQ( i_ispltuple, o_ispltuple); + CHECK_EQ( i_esertuple, o_esertuple); + CHECK_EQ( i_espltuple, o_espltuple); + } +} + +#endif // CEREAL_TEST_TUPLE_H_ diff --git a/tpl/cereal/unittests/unordered_loads.cpp b/tpl/cereal/unittests/unordered_loads.cpp new file mode 100644 index 0000000..9377276 --- /dev/null +++ b/tpl/cereal/unittests/unordered_loads.cpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "unordered_loads.hpp" + +TEST_SUITE("unordered_loads"); + +TEST_CASE("xml_unordered_loads") +{ + test_unordered_loads(); +} + +TEST_CASE("json_unordered_loads") +{ + test_unordered_loads(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/unordered_loads.hpp b/tpl/cereal/unittests/unordered_loads.hpp new file mode 100644 index 0000000..278fb65 --- /dev/null +++ b/tpl/cereal/unittests/unordered_loads.hpp @@ -0,0 +1,149 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_UNORDERED_LOADS_H_ +#define CEREAL_TEST_UNORDERED_LOADS_H_ +#include "common.hpp" + +struct unordered_naming +{ + int x; + int xx; + int y; + int z; + + template + void save( Archive & ar ) const + { + ar( CEREAL_NVP(x), + CEREAL_NVP(z), + CEREAL_NVP(y), + CEREAL_NVP(xx) ); + } + + template + void load( Archive & ar ) + { + ar( x, + CEREAL_NVP(y), + CEREAL_NVP(z), + CEREAL_NVP(xx) ); + } + + bool operator==( unordered_naming const & other ) const + { + return x == other.x && xx == other.xx && y == other.y && z == other.z; + } +}; + +std::ostream& operator<<(std::ostream& os, unordered_naming const & s) +{ + os << "[x: " << s.x << " xx: " << s.xx << " y: " << s.y << " z: " << s.z << "]"; + return os; +} + +template inline +void test_unordered_loads() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rngB = [&](){ return random_value( gen ) % 2 == 0; }; + auto rngI = [&](){ return random_value( gen ); }; + auto rngF = [&](){ return random_value( gen ); }; + auto rngD = [&](){ return random_value( gen ); }; + auto rngS = [&](){ return random_basic_string( gen ); }; + + for(int ii=0; ii<100; ++ii) + { + auto const name1 = rngS(); + auto const name2 = rngS(); + auto const name3 = rngS(); + auto const name4 = rngS(); + auto const name5 = rngS(); + auto const name6 = rngS(); + auto const name7 = rngS(); + + int o_int1 = rngI(); + double o_double2 = rngD(); + std::vector o_vecbool3 = { rngB(), rngB(), rngB(), rngB(), rngB() }; + int o_int4 = rngI(); + int o_int5 = rngI(); + int o_int6 = rngI(); + std::pair o_un7; + o_un7.first = rngF(); + o_un7.second.x = rngI(); + o_un7.second.xx = rngI(); + o_un7.second.y = rngI(); + o_un7.second.z = rngI(); + + std::ostringstream os; + { + OArchive oar(os); + + oar( cereal::make_nvp( name1, o_int1 ), + cereal::make_nvp( name2, o_double2 ), + cereal::make_nvp( name3, o_vecbool3 ), + cereal::make_nvp( name4, o_int4 ), + cereal::make_nvp( name5, o_int5 ), + cereal::make_nvp( name6, o_int6 ), + cereal::make_nvp( name7, o_un7 ) ); + } + + decltype(o_int1) i_int1; + decltype(o_double2) i_double2; + decltype(o_vecbool3) i_vecbool3; + decltype(o_int4) i_int4; + decltype(o_int5) i_int5; + decltype(o_int6) i_int6; + decltype(o_un7) i_un7; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar( cereal::make_nvp( name7, i_un7 ), + cereal::make_nvp( name2, i_double2 ), + cereal::make_nvp( name4, i_int4 ), + cereal::make_nvp( name3, i_vecbool3 ), + cereal::make_nvp( name1, i_int1 ), + cereal::make_nvp( name5, i_int5 ), + i_int6 ); + } + + CHECK_EQ(o_int1, i_int1); + CHECK_EQ(o_double2, doctest::Approx(o_double2).epsilon(1e-5)); + CHECK_EQ(o_vecbool3.size(), i_vecbool3.size()); + check_collection(i_vecbool3, o_vecbool3); + CHECK_EQ(o_int4, i_int4); + CHECK_EQ(o_int5, i_int5); + CHECK_EQ(o_int6, i_int6); + CHECK_EQ(o_un7.first, i_un7.first); + CHECK_EQ(o_un7.second, i_un7.second); + } +} + +#endif // CEREAL_TEST_UNORDERED_LOADS_H_ diff --git a/tpl/cereal/unittests/unordered_map.cpp b/tpl/cereal/unittests/unordered_map.cpp new file mode 100644 index 0000000..a0c0cbb --- /dev/null +++ b/tpl/cereal/unittests/unordered_map.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "unordered_map.hpp" + +TEST_SUITE("unordered_map"); + +TEST_CASE("binary_unordered_map") +{ + test_unordered_map(); +} + +TEST_CASE("portable_binary_unordered_map") +{ + test_unordered_map(); +} + +TEST_CASE("xml_unordered_map") +{ + test_unordered_map(); +} + +TEST_CASE("json_unordered_map") +{ + test_unordered_map(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/unordered_map.hpp b/tpl/cereal/unittests/unordered_map.hpp new file mode 100644 index 0000000..7896197 --- /dev/null +++ b/tpl/cereal/unittests/unordered_map.hpp @@ -0,0 +1,124 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_UNORDERED_MAP_H_ +#define CEREAL_TEST_UNORDERED_MAP_H_ +#include "common.hpp" + +template inline +void test_unordered_map() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::unordered_map o_podunordered_map; + for(int j=0; j<100; ++j) + o_podunordered_map.insert({random_value(gen), random_value(gen)}); + + std::unordered_map o_iserunordered_map; + for(int j=0; j<100; ++j) + o_iserunordered_map.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::unordered_map o_isplunordered_map; + for(int j=0; j<100; ++j) + o_isplunordered_map.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::unordered_map o_eserunordered_map; + for(int j=0; j<100; ++j) + o_eserunordered_map.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::unordered_map o_esplunordered_map; + for(int j=0; j<100; ++j) + o_esplunordered_map.insert({random_value(gen), { random_value(gen), random_value(gen) }}); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podunordered_map); + oar(o_iserunordered_map); + oar(o_isplunordered_map); + oar(o_eserunordered_map); + oar(o_esplunordered_map); + } + + std::unordered_map i_podunordered_map; + std::unordered_map i_iserunordered_map; + std::unordered_map i_isplunordered_map; + std::unordered_map i_eserunordered_map; + std::unordered_map i_esplunordered_map; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podunordered_map); + iar(i_iserunordered_map); + iar(i_isplunordered_map); + iar(i_eserunordered_map); + iar(i_esplunordered_map); + } + + for(auto const & p : i_podunordered_map) + { + auto v = o_podunordered_map.find(p.first); + CHECK_NE(v, o_podunordered_map.end()); + CHECK_EQ(p.second, v->second); + } + + for(auto const & p : i_iserunordered_map) + { + auto v = o_iserunordered_map.find(p.first); + CHECK_NE(v, o_iserunordered_map.end()); + CHECK_EQ(p.second, v->second); + } + + for(auto const & p : i_isplunordered_map) + { + auto v = o_isplunordered_map.find(p.first); + CHECK_NE(v, o_isplunordered_map.end()); + CHECK_EQ(p.second, v->second); + } + + for(auto const & p : i_eserunordered_map) + { + auto v = o_eserunordered_map.find(p.first); + CHECK_NE(v, o_eserunordered_map.end()); + CHECK_EQ(p.second, v->second); + } + + for(auto const & p : i_esplunordered_map) + { + auto v = o_esplunordered_map.find(p.first); + CHECK_NE(v, o_esplunordered_map.end()); + CHECK_EQ(p.second, v->second); + } + } +} + +#endif // CEREAL_TEST_UNORDERED_MAP_H_ diff --git a/tpl/cereal/unittests/unordered_multimap.cpp b/tpl/cereal/unittests/unordered_multimap.cpp new file mode 100644 index 0000000..4d9a7c3 --- /dev/null +++ b/tpl/cereal/unittests/unordered_multimap.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "unordered_multimap.hpp" + +TEST_SUITE("unordered_multimap"); + +TEST_CASE("binary_unordered_multimap") +{ + test_unordered_multimap(); +} + +TEST_CASE("portable_binary_unordered_multimap") +{ + test_unordered_multimap(); +} + +TEST_CASE("xml_unordered_multimap") +{ + test_unordered_multimap(); +} + +TEST_CASE("json_unordered_multimap") +{ + test_unordered_multimap(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/unordered_multimap.hpp b/tpl/cereal/unittests/unordered_multimap.hpp new file mode 100644 index 0000000..6f000e6 --- /dev/null +++ b/tpl/cereal/unittests/unordered_multimap.hpp @@ -0,0 +1,155 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_UNORDERED_MULTIMAP_H_ +#define CEREAL_TEST_UNORDERED_MULTIMAP_H_ +#include "common.hpp" + +template inline +void test_unordered_multimap() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::unordered_multimap o_podunordered_multimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_podunordered_multimap.insert({key, random_value(gen)}); + o_podunordered_multimap.insert({key, random_value(gen)}); + } + + std::unordered_multimap o_iserunordered_multimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_iserunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + o_iserunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::unordered_multimap o_isplunordered_multimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_isplunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + o_isplunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::unordered_multimap o_eserunordered_multimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_eserunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + o_eserunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::unordered_multimap o_esplunordered_multimap; + for(int j=0; j<100; ++j) + { + auto key = random_value(gen); + o_esplunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + o_esplunordered_multimap.insert({key, { random_value(gen), random_value(gen) }}); + } + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podunordered_multimap); + oar(o_iserunordered_multimap); + oar(o_isplunordered_multimap); + oar(o_eserunordered_multimap); + oar(o_esplunordered_multimap); + } + + std::unordered_multimap i_podunordered_multimap; + std::unordered_multimap i_iserunordered_multimap; + std::unordered_multimap i_isplunordered_multimap; + std::unordered_multimap i_eserunordered_multimap; + std::unordered_multimap i_esplunordered_multimap; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podunordered_multimap); + iar(i_iserunordered_multimap); + iar(i_isplunordered_multimap); + iar(i_eserunordered_multimap); + iar(i_esplunordered_multimap); + } + + CHECK_EQ(i_podunordered_multimap.size(), o_podunordered_multimap.size()); + CHECK_EQ(i_iserunordered_multimap.size(), o_iserunordered_multimap.size()); + CHECK_EQ(i_isplunordered_multimap.size(), o_isplunordered_multimap.size()); + CHECK_EQ(i_eserunordered_multimap.size(), o_eserunordered_multimap.size()); + CHECK_EQ(i_esplunordered_multimap.size(), o_esplunordered_multimap.size()); + + for(auto const & p : i_podunordered_multimap) + { + size_t const bucket = o_podunordered_multimap.bucket(p.first); + auto bucket_begin = o_podunordered_multimap.begin(bucket); + auto bucket_end = o_podunordered_multimap.end(bucket); + CHECK_NE(std::find(bucket_begin, bucket_end, p), bucket_end); + } + + for(auto const & p : i_iserunordered_multimap) + { + size_t const bucket = o_iserunordered_multimap.bucket(p.first); + auto bucket_begin = o_iserunordered_multimap.begin(bucket); + auto bucket_end = o_iserunordered_multimap.end(bucket); + CHECK_NE(std::find(bucket_begin, bucket_end, p), bucket_end); + } + + for(auto const & p : i_isplunordered_multimap) + { + size_t const bucket = o_isplunordered_multimap.bucket(p.first); + auto bucket_begin = o_isplunordered_multimap.begin(bucket); + auto bucket_end = o_isplunordered_multimap.end(bucket); + CHECK_NE(std::find(bucket_begin, bucket_end, p), bucket_end); + } + + for(auto const & p : i_eserunordered_multimap) + { + size_t const bucket = o_eserunordered_multimap.bucket(p.first); + auto bucket_begin = o_eserunordered_multimap.begin(bucket); + auto bucket_end = o_eserunordered_multimap.end(bucket); + CHECK_NE(std::find(bucket_begin, bucket_end, p), bucket_end); + } + + for(auto const & p : i_esplunordered_multimap) + { + size_t const bucket = o_esplunordered_multimap.bucket(p.first); + auto bucket_begin = o_esplunordered_multimap.begin(bucket); + auto bucket_end = o_esplunordered_multimap.end(bucket); + CHECK_NE(std::find(bucket_begin, bucket_end, p), bucket_end); + } + } +} + +#endif // CEREAL_TEST_UNORDERED_MULTIMAP_H_ diff --git a/tpl/cereal/unittests/unordered_multiset.cpp b/tpl/cereal/unittests/unordered_multiset.cpp new file mode 100644 index 0000000..a98ffa1 --- /dev/null +++ b/tpl/cereal/unittests/unordered_multiset.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "unordered_multiset.hpp" + +TEST_SUITE("unordered_multiset"); + +TEST_CASE("binary_unordered_multiset") +{ + test_unordered_multiset(); +} + +TEST_CASE("portable_binary_unordered_multiset") +{ + test_unordered_multiset(); +} + +TEST_CASE("xml_unordered_multiset") +{ + test_unordered_multiset(); +} + +TEST_CASE("json_unordered_multiset") +{ + test_unordered_multiset(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/unordered_multiset.hpp b/tpl/cereal/unittests/unordered_multiset.hpp new file mode 100644 index 0000000..ae9d072 --- /dev/null +++ b/tpl/cereal/unittests/unordered_multiset.hpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_UNORDERED_MULTISET_H_ +#define CEREAL_TEST_UNORDERED_MULTISET_H_ +#include "common.hpp" + +template inline +void test_unordered_multiset() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::unordered_multiset o_podunordered_multiset; + for(int j=0; j<100; ++j) + { + int value = random_value(gen); + o_podunordered_multiset.insert(value); + o_podunordered_multiset.insert(value); + } + + std::unordered_multiset> o_iserunordered_multiset; + for(int j=0; j<100; ++j) + { + StructInternalSerialize value = { random_value(gen), random_value(gen) }; + o_iserunordered_multiset.insert(value); + o_iserunordered_multiset.insert(value); + } + + std::unordered_multiset> o_isplunordered_multiset; + for(int j=0; j<100; ++j) + { + StructInternalSplit value = { random_value(gen), random_value(gen) }; + o_isplunordered_multiset.insert(value); + o_isplunordered_multiset.insert(value); + } + + std::unordered_multiset> o_eserunordered_multiset; + for(int j=0; j<100; ++j) + { + StructExternalSerialize value = { random_value(gen), random_value(gen) }; + o_eserunordered_multiset.insert(value); + o_eserunordered_multiset.insert(value); + } + + std::unordered_multiset> o_esplunordered_multiset; + for(int j=0; j<100; ++j) + { + StructExternalSplit value = { random_value(gen), random_value(gen) }; + o_esplunordered_multiset.insert(value); + o_esplunordered_multiset.insert(value); + } + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podunordered_multiset); + oar(o_iserunordered_multiset); + oar(o_isplunordered_multiset); + oar(o_eserunordered_multiset); + oar(o_esplunordered_multiset); + } + + std::unordered_multiset i_podunordered_multiset; + std::unordered_multiset> i_iserunordered_multiset; + std::unordered_multiset> i_isplunordered_multiset; + std::unordered_multiset> i_eserunordered_multiset; + std::unordered_multiset> i_esplunordered_multiset; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podunordered_multiset); + iar(i_iserunordered_multiset); + iar(i_isplunordered_multiset); + iar(i_eserunordered_multiset); + iar(i_esplunordered_multiset); + } + + for(auto const & p : i_podunordered_multiset) + { + CHECK_EQ(o_podunordered_multiset.count(p), i_podunordered_multiset.count(p)); + } + + for(auto const & p : i_iserunordered_multiset) + { + CHECK_EQ(o_iserunordered_multiset.count(p), i_iserunordered_multiset.count(p)); + } + + for(auto const & p : i_isplunordered_multiset) + { + CHECK_EQ(o_isplunordered_multiset.count(p), i_isplunordered_multiset.count(p)); + } + + for(auto const & p : i_eserunordered_multiset) + { + CHECK_EQ(o_eserunordered_multiset.count(p), i_eserunordered_multiset.count(p)); + } + + for(auto const & p : i_esplunordered_multiset) + { + CHECK_EQ(o_esplunordered_multiset.count(p), i_esplunordered_multiset.count(p)); + } + } +} + +#endif // CEREAL_TEST_UNORDERED_MULTISET_H_ diff --git a/tpl/cereal/unittests/unordered_set.cpp b/tpl/cereal/unittests/unordered_set.cpp new file mode 100644 index 0000000..0c8be14 --- /dev/null +++ b/tpl/cereal/unittests/unordered_set.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "unordered_set.hpp" + +TEST_SUITE("unordered_set"); + +TEST_CASE("binary_unordered_set") +{ + test_unordered_set(); +} + +TEST_CASE("portable_binary_unordered_set") +{ + test_unordered_set(); +} + +TEST_CASE("xml_unordered_set") +{ + test_unordered_set(); +} + +TEST_CASE("json_unordered_set") +{ + test_unordered_set(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/unordered_set.hpp b/tpl/cereal/unittests/unordered_set.hpp new file mode 100644 index 0000000..3347313 --- /dev/null +++ b/tpl/cereal/unittests/unordered_set.hpp @@ -0,0 +1,114 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_UNORDERED_SET_H_ +#define CEREAL_TEST_UNORDERED_SET_H_ +#include "common.hpp" + +template inline +void test_unordered_set() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::unordered_set o_podunordered_set; + for(int j=0; j<100; ++j) + o_podunordered_set.insert(random_value(gen)); + + std::unordered_set> o_iserunordered_set; + for(int j=0; j<100; ++j) + o_iserunordered_set.insert({ random_value(gen), random_value(gen) }); + + std::unordered_set> o_isplunordered_set; + for(int j=0; j<100; ++j) + o_isplunordered_set.insert({ random_value(gen), random_value(gen) }); + + std::unordered_set> o_eserunordered_set; + for(int j=0; j<100; ++j) + o_eserunordered_set.insert({ random_value(gen), random_value(gen) }); + + std::unordered_set> o_esplunordered_set; + for(int j=0; j<100; ++j) + o_esplunordered_set.insert({ random_value(gen), random_value(gen) }); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podunordered_set); + oar(o_iserunordered_set); + oar(o_isplunordered_set); + oar(o_eserunordered_set); + oar(o_esplunordered_set); + } + + std::unordered_set i_podunordered_set; + std::unordered_set> i_iserunordered_set; + std::unordered_set> i_isplunordered_set; + std::unordered_set> i_eserunordered_set; + std::unordered_set> i_esplunordered_set; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podunordered_set); + iar(i_iserunordered_set); + iar(i_isplunordered_set); + iar(i_eserunordered_set); + iar(i_esplunordered_set); + } + + for(auto const & p : i_podunordered_set) + { + CHECK_EQ(o_podunordered_set.count(p), 1lu); + } + + for(auto const & p : i_iserunordered_set) + { + CHECK_EQ(o_iserunordered_set.count(p), 1lu); + } + + for(auto const & p : i_isplunordered_set) + { + CHECK_EQ(o_isplunordered_set.count(p), 1lu); + } + + for(auto const & p : i_eserunordered_set) + { + CHECK_EQ(o_eserunordered_set.count(p), 1lu); + } + + for(auto const & p : i_esplunordered_set) + { + CHECK_EQ(o_esplunordered_set.count(p), 1lu); + } + } +} + +#endif // CEREAL_TEST_UNORDERED_SET_H_ diff --git a/tpl/cereal/unittests/user_data_adapters.cpp b/tpl/cereal/unittests/user_data_adapters.cpp new file mode 100644 index 0000000..9a88848 --- /dev/null +++ b/tpl/cereal/unittests/user_data_adapters.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "user_data_adapters.hpp" + +TEST_SUITE("user_data_adapters"); + +TEST_CASE("binary_user_data_adapters") +{ + test_user_data_adapters(); +} + +TEST_CASE("portable_binary_user_data_adapters") +{ + test_user_data_adapters(); +} + +TEST_CASE("xml_user_data_adapters") +{ + test_user_data_adapters(); +} + +TEST_CASE("json_user_data_adapters") +{ + test_user_data_adapters(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/user_data_adapters.hpp b/tpl/cereal/unittests/user_data_adapters.hpp new file mode 100644 index 0000000..23a2510 --- /dev/null +++ b/tpl/cereal/unittests/user_data_adapters.hpp @@ -0,0 +1,120 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_USER_DATA_ADAPTERS_H_ +#define CEREAL_TEST_USER_DATA_ADAPTERS_H_ + +#include "common.hpp" +#define CEREAL_FUTURE_EXPERIMENTAL +#include + +struct SomeStruct {}; + +struct UserData +{ + UserData( SomeStruct * pp, SomeStruct & r ) : + p(pp), ref(r) {} + + SomeStruct * p; + std::reference_wrapper ref; +}; + +struct UserStruct +{ + UserStruct( std::int32_t i, + SomeStruct * pointer, + SomeStruct & reference ) : + i32( i ), + p( pointer ), + ref( reference ) + { } + + UserStruct & operator=( UserStruct const & ) = delete; + + std::int32_t i32; + SomeStruct const * p; + SomeStruct & ref; + + template + void serialize( Archive & ar ) + { + ar( i32 ); + } + + template + static void load_and_construct( Archive & ar, cereal::construct & construct ) + { + std::int32_t ii; + ar( ii ); + auto & data = cereal::get_user_data( ar ); + construct( ii, data.p, data.ref.get() ); + } +}; + +template inline +void test_user_data_adapters() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + auto rng = [&](){ return random_value(gen); }; + + for(int ii=0; ii<100; ++ii) + { + SomeStruct ss; + std::unique_ptr o_ptr( new UserStruct( rng(), &ss, ss ) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_ptr); + } + + decltype( o_ptr ) i_ptr; + + std::istringstream is(os.str()); + { + UserData ud(&ss, ss); + cereal::UserDataAdapter iar(ud, is); + + iar(i_ptr); + } + + CHECK_EQ( i_ptr->p, o_ptr->p ); + CHECK_EQ( std::addressof(i_ptr->ref), std::addressof(o_ptr->ref) ); + CHECK_EQ( i_ptr->i32, o_ptr->i32 ); + + std::istringstream bad_is(os.str()); + { + IArchive iar(bad_is); + + CHECK_THROWS_AS( iar(i_ptr), ::cereal::Exception ); + } + } +} + +#endif // CEREAL_TEST_USER_DATA_ADAPTERS_H_ diff --git a/tpl/cereal/unittests/valarray.cpp b/tpl/cereal/unittests/valarray.cpp new file mode 100644 index 0000000..4866c02 --- /dev/null +++ b/tpl/cereal/unittests/valarray.cpp @@ -0,0 +1,52 @@ +/* +Copyright (c) 2014, Randolph Voorhies, Shane Grant +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of cereal nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "valarray.hpp" + +TEST_SUITE("valarray"); + +TEST_CASE("binary_valarray") +{ + test_valarray(); +} + +TEST_CASE("portable_binary_valarray") +{ + test_valarray(); +} + +TEST_CASE("xml_valarray") +{ + test_valarray(); +} + +TEST_CASE("json_valarray") +{ + test_valarray(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/valarray.hpp b/tpl/cereal/unittests/valarray.hpp new file mode 100644 index 0000000..19df153 --- /dev/null +++ b/tpl/cereal/unittests/valarray.hpp @@ -0,0 +1,101 @@ +/* +Copyright (c) 2014, Randolph Voorhies, Shane Grant +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of cereal nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_VALARRAY_H_ +#define CEREAL_TEST_VALARRAY_H_ +#include "common.hpp" + +template inline +void test_valarray() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for (int ii = 0; ii<100; ++ii) + { + std::valarray o_podvalarray(100); + for (auto & elem : o_podvalarray) + elem = random_value(gen); + + std::valarray o_iservalarray(100); + for (auto & elem : o_iservalarray) + elem = StructInternalSerialize(random_value(gen), random_value(gen)); + + std::valarray o_isplvalarray(100); + for (auto & elem : o_isplvalarray) + elem = StructInternalSplit(random_value(gen), random_value(gen)); + + std::valarray o_eservalarray(100); + for (auto & elem : o_eservalarray) + elem = StructExternalSerialize(random_value(gen), random_value(gen)); + + std::valarray o_esplvalarray(100); + for (auto & elem : o_esplvalarray) + elem = StructExternalSplit(random_value(gen), random_value(gen)); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podvalarray); + oar(o_iservalarray); + oar(o_isplvalarray); + oar(o_eservalarray); + oar(o_esplvalarray); + } + + std::valarray i_podvalarray; + std::valarray i_iservalarray; + std::valarray i_isplvalarray; + std::valarray i_eservalarray; + std::valarray i_esplvalarray; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podvalarray); + iar(i_iservalarray); + iar(i_isplvalarray); + iar(i_eservalarray); + iar(i_esplvalarray); + } + + CHECK_EQ(i_podvalarray.size(), o_podvalarray.size()); + CHECK_EQ(i_iservalarray.size(), o_iservalarray.size()); + CHECK_EQ(i_isplvalarray.size(), o_isplvalarray.size()); + CHECK_EQ(i_eservalarray.size(), o_eservalarray.size()); + CHECK_EQ(i_esplvalarray.size(), o_esplvalarray.size()); + + check_collection(i_podvalarray , o_podvalarray ); + check_collection(i_iservalarray, o_iservalarray); + check_collection(i_isplvalarray, o_isplvalarray); + check_collection(i_eservalarray, o_eservalarray); + check_collection(i_esplvalarray, o_esplvalarray); + } +} + +#endif // CEREAL_TEST_VALARRAY_H_ diff --git a/tpl/cereal/unittests/vector.cpp b/tpl/cereal/unittests/vector.cpp new file mode 100644 index 0000000..be172ba --- /dev/null +++ b/tpl/cereal/unittests/vector.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "vector.hpp" + +TEST_SUITE("vector"); + +TEST_CASE("binary_vector") +{ + test_vector(); +} + +TEST_CASE("portable_binary_vector") +{ + test_vector(); +} + +TEST_CASE("xml_vector") +{ + test_vector(); +} + +TEST_CASE("json_vector") +{ + test_vector(); +} + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/vector.hpp b/tpl/cereal/unittests/vector.hpp new file mode 100644 index 0000000..a029790 --- /dev/null +++ b/tpl/cereal/unittests/vector.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_VECTOR_H_ +#define CEREAL_TEST_VECTOR_H_ +#include "common.hpp" + +template inline +void test_vector() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + for(int ii=0; ii<100; ++ii) + { + std::vector o_podvector(100); + for(auto & elem : o_podvector) + elem = random_value(gen); + + std::vector o_boolvector; o_boolvector.resize(100); + for( size_t i = 0; i < 100; ++i ) + o_boolvector[i] = (random_value(gen) % 2) == 0; + + std::vector o_iservector(100); + for(auto & elem : o_iservector) + elem = StructInternalSerialize( random_value(gen), random_value(gen) ); + + std::vector o_isplvector(100); + for(auto & elem : o_isplvector) + elem = StructInternalSplit( random_value(gen), random_value(gen) ); + + std::vector o_eservector(100); + for(auto & elem : o_eservector) + elem = StructExternalSerialize( random_value(gen), random_value(gen) ); + + std::vector o_esplvector(100); + for(auto & elem : o_esplvector) + elem = StructExternalSplit( random_value(gen), random_value(gen) ); + + std::ostringstream os; + { + OArchive oar(os); + + oar(o_podvector); + oar(o_boolvector); + oar(o_iservector); + oar(o_isplvector); + oar(o_eservector); + oar(o_esplvector); + } + + std::vector i_podvector; + std::vector i_boolvector; + std::vector i_iservector; + std::vector i_isplvector; + std::vector i_eservector; + std::vector i_esplvector; + + std::istringstream is(os.str()); + { + IArchive iar(is); + + iar(i_podvector); + iar(i_boolvector); + iar(i_iservector); + iar(i_isplvector); + iar(i_eservector); + iar(i_esplvector); + } + + CHECK_EQ(i_podvector.size(), o_podvector.size()); + CHECK_EQ(i_boolvector.size(), o_boolvector.size()); + CHECK_EQ(i_iservector.size(), o_iservector.size()); + CHECK_EQ(i_isplvector.size(), o_isplvector.size()); + CHECK_EQ(i_eservector.size(), o_eservector.size()); + CHECK_EQ(i_esplvector.size(), o_esplvector.size()); + + check_collection(i_podvector, o_podvector ); + check_collection(i_boolvector, o_boolvector); + check_collection(i_iservector, o_iservector); + check_collection(i_isplvector, o_isplvector); + check_collection(i_eservector, o_eservector); + check_collection(i_esplvector, o_esplvector); + } +} + +#endif // CEREAL_TEST_VECTOR_H_ diff --git a/tpl/cereal/unittests/versioning.cpp b/tpl/cereal/unittests/versioning.cpp new file mode 100644 index 0000000..8727426 --- /dev/null +++ b/tpl/cereal/unittests/versioning.cpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "versioning.hpp" + +TEST_SUITE("versioning"); + +TEST_CASE("binary_versioning") +{ + test_versioning(); +} + +TEST_CASE("portable_binary_versioning") +{ + test_versioning(); +} + +TEST_CASE("xml_versioning") +{ + test_versioning(); +} + +TEST_CASE("json_versioning") +{ + test_versioning(); +} + +#if CEREAL_THREAD_SAFE +TEST_CASE("binary_versioning_threading") +{ + test_versioning_threading(); +} + +TEST_CASE("portable_binary_versioning_threading") +{ + test_versioning_threading(); +} + +TEST_CASE("xml_versioning_threading") +{ + test_versioning_threading(); +} + +TEST_CASE("json_versioning_threading") +{ + test_versioning_threading(); +} +#endif // CEREAL_THREAD_SAFE + +TEST_SUITE_END(); diff --git a/tpl/cereal/unittests/versioning.hpp b/tpl/cereal/unittests/versioning.hpp new file mode 100644 index 0000000..33e21d2 --- /dev/null +++ b/tpl/cereal/unittests/versioning.hpp @@ -0,0 +1,217 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef CEREAL_TEST_VERSIONING_H_ +#define CEREAL_TEST_VERSIONING_H_ +#include "common.hpp" + +#if CEREAL_THREAD_SAFE +#include +#endif + +namespace Nested +{ + struct NestedClass + { + int x; + + template + void serialize( Archive & ar ) + { ar( x ); } + }; +} + +CEREAL_CLASS_VERSION( Nested::NestedClass, 1 ) + +class VersionStructMS +{ + public: + bool x; + std::uint32_t v; + + private: + friend class cereal::access; + template + void serialize( Archive & ar, std::uint32_t const version ) + { + ar( x ); + v = version; + } +}; + +struct VersionStructMSP +{ + uint8_t x; + std::uint32_t v; + template + void save( Archive & ar, std::uint32_t const /*version*/ ) const + { + ar( x ); + } + + template + void load( Archive & ar, std::uint32_t const version ) + { + ar( x ); + v = version; + } +}; + +struct VersionStructNMS +{ + std::int32_t x; + std::uint32_t v; +}; + +template +void serialize( Archive & ar, VersionStructNMS & vnms, const std::uint32_t version ) +{ + ar( vnms.x ); + vnms.v = version; +} + +struct VersionStructNMSP +{ + double x; + std::uint32_t v; +}; + +template +void save( Archive & ar, VersionStructNMSP const & vnms, const std::uint32_t /*version*/ ) +{ + ar( vnms.x ); +} + +template +void load( Archive & ar, VersionStructNMSP & vnms, const std::uint32_t version ) +{ + ar( vnms.x ); + vnms.v = version; +} + +CEREAL_CLASS_VERSION( VersionStructMSP, 33 ) +CEREAL_CLASS_VERSION( VersionStructNMS, 66 ) +CEREAL_CLASS_VERSION( VersionStructNMSP, 99 ) + +template inline +void test_versioning() +{ + std::random_device rd; + std::mt19937 gen(rd()); + + #if CEREAL_THREAD_SAFE + #include + static std::mutex testMutex; + #endif + + for(size_t i=0; i<100; ++i) + { + VersionStructMS o_MS = {random_value(gen) % 2 ? true : false, 1}; + VersionStructMSP o_MSP = {random_value(gen), 1}; + VersionStructNMS o_NMS = {random_value(gen), 1}; + VersionStructNMSP o_NMSP = {random_value(gen), 1}; + VersionStructMS o_MS2 = {random_value(gen) % 2 ? true : false, 1}; + VersionStructMSP o_MSP2 = {random_value(gen), 1}; + VersionStructNMS o_NMS2 = {random_value(gen), 1}; + VersionStructNMSP o_NMSP2 = {random_value(gen), 1}; + + std::ostringstream os; + { + OArchive oar(os); + oar( o_MS ); + oar( o_MSP ); + oar( o_NMS ); + oar( o_NMSP ); + oar( o_MS2 ); + oar( o_MSP2 ); + oar( o_NMS2 ); + oar( o_NMSP2 ); + } + + decltype(o_MS) i_MS; + decltype(o_MSP) i_MSP; + decltype(o_NMS) i_NMS; + decltype(o_NMSP) i_NMSP; + decltype(o_MS2) i_MS2; + decltype(o_MSP2) i_MSP2; + decltype(o_NMS2) i_NMS2; + decltype(o_NMSP2) i_NMSP2; + + std::istringstream is(os.str()); + { + IArchive iar(is); + iar( i_MS ); + iar( i_MSP ); + iar( i_NMS ); + iar( i_NMSP ); + iar( i_MS2 ); + iar( i_MSP2 ); + iar( i_NMS2 ); + iar( i_NMSP2 ); + } + + #if CEREAL_THREAD_SAFE + std::lock_guard lock( testMutex ); + #endif + + CHECK_EQ(o_MS.x, i_MS.x); + CHECK_EQ(i_MS.v, 0u); + CHECK_EQ(o_MSP.x, i_MSP.x); + CHECK_EQ(i_MSP.v, 33u); + CHECK_EQ(o_NMS.x, i_NMS.x); + CHECK_EQ(i_NMS.v, 66u); + CHECK_EQ(o_NMSP.x, doctest::Approx(i_NMSP.x).epsilon(1e-5)); + CHECK_EQ(i_NMSP.v, 99u); + + CHECK_EQ(o_MS2.x, i_MS2.x); + CHECK_EQ(i_MS2.v, 0u); + CHECK_EQ(o_MSP2.x, i_MSP2.x); + CHECK_EQ(i_MSP2.v, 33u); + CHECK_EQ(o_NMS2.x, i_NMS2.x); + CHECK_EQ(i_NMS2.v, 66u); + CHECK_EQ(o_NMSP2.x, doctest::Approx(i_NMSP2.x).epsilon(1e-5)); + CHECK_EQ(i_NMSP2.v, 99u); + } +} + +#if CEREAL_THREAD_SAFE +template inline +void test_versioning_threading() +{ + std::vector> pool; + for( size_t i = 0; i < 100; ++i ) + pool.emplace_back( std::async( std::launch::async, + [](){ test_versioning(); return true; } ) ); + + for( auto & future : pool ) + future.wait(); + + for( auto & future : pool ) + CHECK_UNARY( future.get() ); +} +#endif // CEREAL_THREAD_SAFE + +#endif // CEREAL_TEST_VERSIONING_H_ diff --git a/tpl/cereal/vs2013/.gitignore b/tpl/cereal/vs2013/.gitignore new file mode 100644 index 0000000..c6eec7a --- /dev/null +++ b/tpl/cereal/vs2013/.gitignore @@ -0,0 +1,3 @@ +*/Debug +*/Release +*/x64 diff --git a/tpl/cereal/vs2013/sandbox/sandbox.vcxproj b/tpl/cereal/vs2013/sandbox/sandbox.vcxproj new file mode 100644 index 0000000..89da192 --- /dev/null +++ b/tpl/cereal/vs2013/sandbox/sandbox.vcxproj @@ -0,0 +1,256 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + {DAC23DBB-630B-46B9-B115-F1449697898A} + sandbox + + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + false + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + false + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + false + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + false + + + true + true + true + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox/sandbox.vcxproj.filters b/tpl/cereal/vs2013/sandbox/sandbox.vcxproj.filters new file mode 100644 index 0000000..90e0f7d --- /dev/null +++ b/tpl/cereal/vs2013/sandbox/sandbox.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj b/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj new file mode 100644 index 0000000..fc18258 --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj @@ -0,0 +1,248 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + {52E96EC3-125A-4525-BC72-F3C524F24640} + sandbox_json + + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj.filters b/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj.filters new file mode 100644 index 0000000..3edef0a --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_json/sandbox_json.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj b/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj new file mode 100644 index 0000000..035a8ca --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj @@ -0,0 +1,252 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + {C81EF3F9-7849-4506-898C-85D0D564CD6A} + sandbox_rtti + + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_54;$(IncludePath) + C:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + $(SolutionDir)\..\include;C:\Boost\include\boost-1_55;$(IncludePath) + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj.filters b/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj.filters new file mode 100644 index 0000000..813de5f --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_rtti/sandbox_rtti.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj b/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj new file mode 100644 index 0000000..4615581 --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj @@ -0,0 +1,266 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734} + sandbox_vs + + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\Debug;E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\Debug;E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\x64\Debug;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\x64\Debug;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\Release;E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\Release;E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\x64\Release;$(LibraryPath) + + + $(SolutionDir)\sandbox_vs_dll;$(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(SolutionDir)\..\sandbox\sandbox_shared_lib;$(IncludePath) + $(SolutionDir)\x64\Release;$(LibraryPath) + + + + Level4 + Disabled + true + false + + + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + Disabled + true + false + + + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + Disabled + true + + + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + Disabled + true + + + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + sandbox_vs_dll.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj.filters b/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj.filters new file mode 100644 index 0000000..70ecf39 --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_vs/sandbox_vs.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj b/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj new file mode 100755 index 0000000..a335650 --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj @@ -0,0 +1,261 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + {95D6662F-4166-40D2-87D7-CABFB60C199A} + sandbox_vs_dll + + + + DynamicLibrary + true + v120 + MultiByte + + + DynamicLibrary + true + v120 + MultiByte + + + DynamicLibrary + true + v120 + MultiByte + + + DynamicLibrary + true + v120 + MultiByte + + + DynamicLibrary + false + v120 + true + MultiByte + + + DynamicLibrary + false + v120 + true + MultiByte + + + DynamicLibrary + false + v120 + true + MultiByte + + + DynamicLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + Disabled + true + + + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + + + true + true + true + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj.filters b/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj.filters new file mode 100755 index 0000000..d61909d --- /dev/null +++ b/tpl/cereal/vs2013/sandbox_vs_dll/sandbox_vs_dll.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/unittests/main.cpp b/tpl/cereal/vs2013/unittests/main.cpp new file mode 100755 index 0000000..fa91267 --- /dev/null +++ b/tpl/cereal/vs2013/unittests/main.cpp @@ -0,0 +1,30 @@ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define BOOST_TEST_MODULE Cereal +#include +#include \ No newline at end of file diff --git a/tpl/cereal/vs2013/unittests/unittests.vcxproj b/tpl/cereal/vs2013/unittests/unittests.vcxproj new file mode 100644 index 0000000..d3c82ca --- /dev/null +++ b/tpl/cereal/vs2013/unittests/unittests.vcxproj @@ -0,0 +1,327 @@ + + + + + DEBUG_VS2015 + Win32 + + + DEBUG_VS2015 + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_VS2015 + Win32 + + + Release_VS2015 + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7} + unittests + + + + Application + true + v120 + MultiByte + + + Application + true + v140 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib\x64;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib\x64;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib;$(LibraryPath) + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib\x64;$(LibraryPath) + false + + + $(SolutionDir)\..\include;$(SolutionDir)\..\unittests;E:\Boost\include\boost-1_55;$(IncludePath) + E:\Boost\lib\x64;$(LibraryPath) + false + + + + Level4 + Disabled + true + true + /bigobj %(AdditionalOptions) + true + + + true + %(AdditionalDependencies) + NotSet + Console + + + + + Level4 + Disabled + true + true + /bigobj %(AdditionalOptions) + true + + + true + %(AdditionalDependencies) + NotSet + Console + + + + + Level4 + Disabled + true + /bigobj %(AdditionalOptions) + true + + + true + Console + + + + + Level4 + Disabled + true + /bigobj %(AdditionalOptions) + true + + + true + Console + + + + + Level4 + MaxSpeed + true + true + true + /bigobj %(AdditionalOptions) + true + + + true + true + true + %(AdditionalDependencies) + Console + + + + + Level4 + MaxSpeed + true + true + true + /bigobj %(AdditionalOptions) + true + + + true + true + true + %(AdditionalDependencies) + Console + + + + + Level4 + MaxSpeed + true + true + true + /bigobj %(AdditionalOptions) + false + true + + + true + true + true + Console + + + + + Level4 + MaxSpeed + true + true + true + /bigobj %(AdditionalOptions) + false + true + + + true + true + true + Console + + + + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/unittests/unittests.vcxproj.filters b/tpl/cereal/vs2013/unittests/unittests.vcxproj.filters new file mode 100644 index 0000000..902b535 --- /dev/null +++ b/tpl/cereal/vs2013/unittests/unittests.vcxproj.filters @@ -0,0 +1,127 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/tpl/cereal/vs2013/vs2013.sln b/tpl/cereal/vs2013/vs2013.sln new file mode 100644 index 0000000..d0ed26a --- /dev/null +++ b/tpl/cereal/vs2013/vs2013.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_vs", "sandbox_vs\sandbox_vs.vcxproj", "{A67E36D2-32BE-4D4D-BA2E-8FD59378E734}" + ProjectSection(ProjectDependencies) = postProject + {95D6662F-4166-40D2-87D7-CABFB60C199A} = {95D6662F-4166-40D2-87D7-CABFB60C199A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_json", "sandbox_json\sandbox_json.vcxproj", "{52E96EC3-125A-4525-BC72-F3C524F24640}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "sandbox\sandbox.vcxproj", "{DAC23DBB-630B-46B9-B115-F1449697898A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_rtti", "sandbox_rtti\sandbox_rtti.vcxproj", "{C81EF3F9-7849-4506-898C-85D0D564CD6A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests\unittests.vcxproj", "{D447E13A-97A4-4907-9F61-A9BCCDB91EF7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "performance", "performance\performance.vcxproj", "{2F374733-FCA8-4CBB-91A0-2B0B34393D86}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_vs_dll", "sandbox_vs_dll\sandbox_vs_dll.vcxproj", "{95D6662F-4166-40D2-87D7-CABFB60C199A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_VS2013|Win32 = Debug_VS2013|Win32 + Debug_VS2013|x64 = Debug_VS2013|x64 + Debug_VS2015|Win32 = Debug_VS2015|Win32 + Debug_VS2015|x64 = Debug_VS2015|x64 + Release_VS2013|Win32 = Release_VS2013|Win32 + Release_VS2013|x64 = Release_VS2013|x64 + Release_VS2015|Win32 = Release_VS2015|Win32 + Release_VS2015|x64 = Release_VS2015|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2013|x64.Build.0 = Debug|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2013|Win32.Build.0 = Release|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2013|x64.ActiveCfg = Release|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2013|x64.Build.0 = Release|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {A67E36D2-32BE-4D4D-BA2E-8FD59378E734}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2013|x64.Build.0 = Debug|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2013|Win32.Build.0 = Release|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2013|x64.ActiveCfg = Release|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2013|x64.Build.0 = Release|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {52E96EC3-125A-4525-BC72-F3C524F24640}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2013|x64.Build.0 = Debug|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2013|Win32.Build.0 = Release|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2013|x64.ActiveCfg = Release|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2013|x64.Build.0 = Release|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {DAC23DBB-630B-46B9-B115-F1449697898A}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2013|x64.Build.0 = Debug|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2013|Win32.Build.0 = Release|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2013|x64.ActiveCfg = Release|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2013|x64.Build.0 = Release|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {C81EF3F9-7849-4506-898C-85D0D564CD6A}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2013|x64.Build.0 = Debug|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2013|Win32.Build.0 = Release|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2013|x64.ActiveCfg = Release|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2013|x64.Build.0 = Release|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {D447E13A-97A4-4907-9F61-A9BCCDB91EF7}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2013|Win32.Build.0 = Release|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2013|x64.ActiveCfg = Release|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {2F374733-FCA8-4CBB-91A0-2B0B34393D86}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2013|Win32.Build.0 = Debug|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2013|x64.ActiveCfg = Debug|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2015|Win32.ActiveCfg = DEBUG_VS2015|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2015|Win32.Build.0 = DEBUG_VS2015|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2015|x64.ActiveCfg = DEBUG_VS2015|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Debug_VS2015|x64.Build.0 = DEBUG_VS2015|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2013|Win32.ActiveCfg = Release|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2013|Win32.Build.0 = Release|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2013|x64.ActiveCfg = Release|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2015|Win32.ActiveCfg = Release_VS2015|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2015|Win32.Build.0 = Release_VS2015|Win32 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2015|x64.ActiveCfg = Release_VS2015|x64 + {95D6662F-4166-40D2-87D7-CABFB60C199A}.Release_VS2015|x64.Build.0 = Release_VS2015|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tpl/gperftools/CMakeLists.txt b/tpl/gperftools/CMakeLists.txt index b5a92cb..a16129b 100644 --- a/tpl/gperftools/CMakeLists.txt +++ b/tpl/gperftools/CMakeLists.txt @@ -47,6 +47,43 @@ CHECK_FUNCTION_EXISTS ("sbrk" HAVE_SBRK) CHECK_FUNCTION_EXISTS ("mmap" HAVE_MMAP) CHECK_FUNCTION_EXISTS ("posix_memalign" HAVE_MEMALIGN) +CHECK_CXX_SOURCE_COMPILES( + "#include + int main(int argc, char **argv) + { + &_Unwind_Backtrace; + return 0; + }" + unwind_backtrace_exists) + +IF (unwind_backtrace_exists) + SET(HAVE_UNWIND_BACKTRACE 1 CACHE STRING "unwind_backtrace") + message( STATUS "UNWIND BACKTRACE works" ) +ELSE() + message( STATUS "UNWIND BACKTRACE **doesn't** work" ) +ENDIF() + +CHECK_CXX_SOURCE_COMPILES( + "#include + static void foo(void) __attribute__ ((unused)); + void foo(void) + { + return; + } + int main(int argc, char **argv) + { + return 0; + }" + supports__attribute__) + +IF (supports__attribute__) + SET(HAVE___ATTRIBUTE__ 1 CACHE STRING "have_attribute") + message( STATUS "Supports ATTRIBUTE" ) +ELSE() + message( STATUS "*DOESN'T support ATTRIBUTE" ) +ENDIF() + + CHECK_CXX_SOURCE_COMPILES( "#include void* (* volatile __malloc_hook)(size_t, const void*) = 0; @@ -57,9 +94,9 @@ CHECK_CXX_SOURCE_COMPILES( malloc_hook_volatile) IF (malloc_hook_volatile) - SET(Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE "volatile" CACHE string "does malloc hook need volatile") + SET(Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE "volatile" CACHE STRING "does malloc hook need volatile") ELSE () - SET(Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE "" CACHE string "does malloc hook need volatile") + SET(Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE "" CACHE STRING "does malloc hook need volatile") ENDIF () mark_as_advanced(Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE) @@ -73,25 +110,25 @@ CHECK_CXX_SOURCE_COMPILES( malloc_mallinfo) IF (malloc_mallinfo) - SET(Faodel_PERFTOOLS_HAVE_STRUCT_MALLINFO 1 CACHE string "support for mallinfo") + SET(Faodel_PERFTOOLS_HAVE_STRUCT_MALLINFO 1 CACHE STRING "support for mallinfo") ENDIF () mark_as_advanced(Faodel_PERFTOOLS_HAVE_STRUCT_MALLINFO) -set(Faodel_PERFTOOLS_DLL_DECL "" CACHE string "On windows, should be __declspec(dllexport)") -set(Faodel_PERFTOOLS_STL_NAMESPACE "std" CACHE string "namespace where STL code like vector<> is defined") +set(Faodel_PERFTOOLS_DLL_DECL "" CACHE STRING "On windows, should be __declspec(dllexport)") +set(Faodel_PERFTOOLS_STL_NAMESPACE "std" CACHE STRING "namespace where STL code like vector<> is defined") option(Faodel_PERFTOOLS_TCMALLOC_32K_PAGES "32K of internal page size" OFF) option(Faodel_PERFTOOLS_TCMALLOC_64K_PAGES "64K of internal page size" OFF) option(Faodel_PERFTOOLS_TCMALLOC_ALIGN_8BYTES "8 bytes of allocation alignment" OFF) # option(HAVE_MMAP "System has MMAP" ON) # option(NO_HEAP_CHECK "Don't check the heap" ON) -#set(VERSION "lunasa" CACHE string "version string for the config") +#set(VERSION "lunasa" CACHE STRING "version string for the config") mark_as_advanced(Faodel_PERFTOOLS_DLL_DECL) mark_as_advanced(Faodel_PERFTOOLS_STL_NAMESPACE) mark_as_advanced(Faodel_PERFTOOLS_TCMALLOC_32K_PAGES) mark_as_advanced(Faodel_PERFTOOLS_TCMALLOC_64K_PAGES) mark_as_advanced(Faodel_PERFTOOLS_TCMALLOC_ALIGN_8BYTES) -set(Faodel_PERFTOOLS_PACKAGE_VERSION "2.5" CACHE string "version of this package") +set(Faodel_PERFTOOLS_PACKAGE_VERSION "2.5" CACHE STRING "version of this package") mark_as_advanced(Faodel_PERFTOOLS_PACKAGE_VERSION) execute_process(COMMAND expr "${Faodel_PERFTOOLS_PACKAGE_VERSION}" : "\\([[0-9]]*\\)" OUTPUT_VARIABLE MAJOR) execute_process(COMMAND expr "${Faodel_PERFTOOLS_PACKAGE_VERSION}" : "[[0-9]]*\\.\\([[0-9]]*\\)" OUTPUT_VARIABLE MINOR) @@ -104,16 +141,16 @@ string(STRIP "${MAJOR}" MAJOR) string(STRIP "${MINOR}" MINOR) string(STRIP "${PATCH}" PATCH) -set(Faodel_PERFTOOLS_TC_VERSION_MAJOR "${MAJOR}" CACHE string "major version of this package") -set(Faodel_PERFTOOLS_TC_VERSION_MINOR "${MINOR}" CACHE string "minor version of this package") -set(Faodel_PERFTOOLS_TC_VERSION_PATCH "${PATCH}" CACHE string "patch version of this package") +set(Faodel_PERFTOOLS_TC_VERSION_MAJOR "${MAJOR}" CACHE STRING "major version of this package") +set(Faodel_PERFTOOLS_TC_VERSION_MINOR "${MINOR}" CACHE STRING "minor version of this package") +set(Faodel_PERFTOOLS_TC_VERSION_PATCH "${PATCH}" CACHE STRING "patch version of this package") mark_as_advanced(Faodel_PERFTOOLS_TC_VERSION_MAJOR) mark_as_advanced(Faodel_PERFTOOLS_TC_VERSION_MINOR) mark_as_advanced(Faodel_PERFTOOLS_TC_VERSION_PATCH) -set(Faodel_PERFTOOLS_PRIdS "%d" CACHE string "printf format code for printing size_t and ssize_t") -set(Faodel_PERFTOOLS_PRIuS "%u" CACHE string "printf format code for printing size_t and ssize_t") -set(Faodel_PERFTOOLS_PRIxS "%x" CACHE string "printf format code for printing size_t and ssize_t") +set(Faodel_PERFTOOLS_PRIdS "%d" CACHE STRING "printf format code for printing size_t and ssize_t") +set(Faodel_PERFTOOLS_PRIuS "%u" CACHE STRING "printf format code for printing size_t and ssize_t") +set(Faodel_PERFTOOLS_PRIxS "%x" CACHE STRING "printf format code for printing size_t and ssize_t") mark_as_advanced(Faodel_PERFTOOLS_PRIdS) mark_as_advanced(Faodel_PERFTOOLS_PRIuS) mark_as_advanced(Faodel_PERFTOOLS_PRIxS) @@ -186,6 +223,7 @@ set(TCMALLOC_MINIMAL_SOURCES src/tcmalloc.cc src/system-alloc.cc src/common.cc + src/fake_stacktrace_scope.cc src/internal_logging.cc src/memfs_malloc.cc src/central_freelist.cc @@ -199,7 +237,16 @@ set(TCMALLOC_MINIMAL_SOURCES src/maybe_threads.cc src/thread_cache.cc src/malloc_hook.cc - src/malloc_extension.cc) + src/malloc_extension.cc + src/memory_region_map.cc + src/heap-checker.cc + src/heap-checker-bcad.cc + src/heap-profile-table.cc + src/base/linuxthreads.cc + src/base/thread_lister.c + src/raw_printer.cc + src/base/low_level_alloc.cc) + add_library( tcmalloc src/config.h.in ${GPERF_HEADERS} ${TCMALLOC_MINIMAL_SOURCES} ) target_link_libraries( tcmalloc spinlock Threads::Threads ) diff --git a/tpl/gperftools/ChangeLog b/tpl/gperftools/ChangeLog index 4b334be..92bcb9b 100644 --- a/tpl/gperftools/ChangeLog +++ b/tpl/gperftools/ChangeLog @@ -1,646 +1,2 @@ -Fri Feb 03 15:40:45 2012 Google Inc. - - * gperftools: version 2.0 - * Renamed the project from google-perftools to gperftools (csilvers) - * Renamed the .deb/.rpm packagse from google-perftools to gperftools too - * Renamed include directory from google/ to gperftools/ (csilvers) - * Changed the 'official' perftools email in setup.py/etc - * Renamed google-perftools.sln to gperftools.sln - * PORTING: Removed bash-isms & grep -q in heap-checker-death_unittest.sh - * Changed copyright text to reflect Google's relinquished ownership - -Tue Jan 31 10:43:50 2012 Google Inc. - - * google-perftools: version 1.10 release - * PORTING: Support for patching assembly on win x86_64! (scott.fr...) - * PORTING: Work around atexit-execution-order bug on freebsd (csilvers) - * PORTING: Patch _calloc_crt for windows (roger orr) - * PORTING: Add C++11 compatibility method for stl allocator (jdennett) - * PORTING: use MADV_FREE, not MADV_DONTNEED, on freebsd (csilvers) - * PORTING: Don't use SYS_open when not supported on solaris (csilvers) - * PORTING: Do not assume uname() returns 0 on success (csilvers) - * LSS: Improved ARM support in linux-syscall-support (dougkwan) - * LSS: Get rid of unused syscalls in linux-syscall-support (csilvers) - * LSS: Fix broken mmap wrapping for ppc (markus) - * LSS: Emit .cfi_adjust_cfa_offset when appropriate (ppluzhnikov) - * LSS: Be more accurate in register use in __asm__ (markus) - * LSS: Fix __asm__ calls to compile under clang (chandlerc) - * LSS: Fix ARM inline assembly bug around r7 and swi (lcwu) - * No longer log when an allocator fails (csilvers) - * void* -> const void* for MallocExtension methods (llib) - * Improve HEAP_PROFILE_MMAP and fix bugs with it (dmikurube) - * Replace int-based abs with more correct fabs in a test (pmurin) - -Thu Dec 22 16:22:45 2011 Google Inc. - - * google-perftools: version 1.9 release - * Lightweight check for double-frees (blount) - * BUGFIX: Fix pprof to exit properly if run with no args (dagitses) - * Suggest ASan as a way to diagnose buggy code (ppluzhnikov) - * Get rid of unused CACHELINE_SIZE (csilvers) - * Replace atexit() calls with global dtors; helps freebsd (csilvers) - * Disable heap-checker under AddressSanitizer (kcc) - * Fix bug in powerpc stacktracing (ppluzhnikov) - * PERF: Use exponential backoff waiting for spinlocks (m3b) - * Fix 64-bit nm on 32-bit binaries in pprof (csilvers) - * Add ProfileHandlerDisallowForever (rsc) - * BUGFIX: Shell escape when forking in pprof (csilvers) - * No longer combine overloaded functions in pprof (csilvers) - * Fix address-normalizing bug in pprof (csilvers) - * More consistently call abort() instead of exit() on failure (csilvers) - * Allow NoGlobalLeaks to be safely called more than once (csilvers) - * PORTING/BUGFIX: Fix ARM cycleclock to use volatile asm (dougkwan) - * PORTING: 64-bit atomic ops for ARMv7 (dougkwan) - * PORTING: Implement stacktrace for ARM (dougkwan) - * PORTING: Fix malloc_hook_mmap_linux for ARM (dougkwan) - * PORTING: Update linux_syscall_support.h for ARM/etc (evannier, sanek) - * PORTING: Fix freebsd to work on x86_64 (chapp...@gmail.com) - * PORTING: Added additional SYS_mmap fixes for FreeBSD (chappedm) - * PORTING: Allow us to compile on OS X 10.6 and run on 10.5 (raltherr) - * PORTING: Check for mingw compilers that *do* define timespec - * PORTING: Add "support" for MIPS cycletimer - * PORTING: Fix fallback cycle-timer to work with Now (dougkwan) - * PERF: Move stack trace collecting out of the mutex (taylorc) - * PERF: Get the deallocation stack trace outside the mutex (sean) - * Make PageHeap dynamically allocated for leak checks (maxim) - * BUGFIX: Fix probing of nm -f behavior in pprof (dpeng) - * BUGFIX: Fix a race with the CentralFreeList lock before main (sanjay) - * Support /pprof/censusprofile url arguments (rajatjain) - * Change IgnoreObject to return its argument (nlewycky) - * Update malloc-hook files to support more CPUs - * BUGFIX: write our own strstr to avoid libc problems (csilvers) - * Use simple callgrind compression facility in pprof - * Print an error message when we can't run pprof to symbolize (csilvers) - * Die in configure when g++ is't installed (csilvers) - * DOC: Beef up the documentation a bit about using libunwind (csilvers) - -Fri Aug 26 13:29:25 2011 Google Inc. - - * google-perftools: version 1.8.3 release - * Added back the 'pthreads unsafe early' #define, needed for FreeBSD - -Thu Aug 11 15:01:47 2011 Google Inc. - - * google-perftools: version 1.8.2 release - * Fixed calculation of patchlevel, 'make check' should all pass again - -Tue Jul 26 20:57:51 2011 Google Inc. - - * google-perftools: version 1.8.1 release - * Added an #include to fix compile breakage on latest gcc's - * Removed an extra , in the configure.ac script - -Fri Jul 15 16:10:51 2011 Google Inc. - - * google-perftools: version 1.8 release - * PORTING: (Disabled) support for patching mmap on freebsd (chapp...) - * PORTING: Support volatile __malloc_hook for glibc 2.14 (csilvers) - * PORTING: Use _asm rdtsc and __rdtsc to get cycleclock in windows (koda) - * PORTING: Fix fd vs. HANDLE compiler error on cygwin (csilvers) - * PORTING: Do not test memalign or double-linking on OS X (csilvers) - * PORTING: Actually enable TLS on windows (jontra) - * PORTING: Some work to compile under Native Client (krasin) - * PORTING: deal with pthread_once w/o -pthread on freebsd (csilvers) - * Rearrange libc-overriding to make it easier to port (csilvers) - * Display source locations in pprof disassembly (sanjay) - * BUGFIX: Actually initialize allocator name (mec) - * BUGFIX: Keep track of 'overhead' bytes in malloc reporting (csilvers) - * Allow ignoring one object twice in the leak checker (glider) - * BUGFIX: top10 in pprof should print 10 lines, not 11 (rsc) - * Refactor vdso source files (tipp) - * Some documentation cleanups - * Document MAX_TOTAL_THREAD_CACHE_SIZE <= 1Gb (nsethi) - * Add MallocExtension::GetOwnership(ptr) (csilvers) - * BUGFIX: We were leaving out a needed $(top_srcdir) in the Makefile - * PORTING: Support getting argv0 on OS X - * Add 'weblist' command to pprof: like 'list' but html (sanjay) - * Improve source listing in pprof (sanjay) - * Cap cache sizes to reduce fragmentation (ruemmler) - * Improve performance by capping or increasing sizes (ruemmler) - * Add M{,un}mapReplacmenet hooks into MallocHook (ribrdb) - * Refactored system allocator logic (gangren) - * Include cleanups (csilvers) - * Add TCMALLOC_SMALL_BUT_SLOW support (ruemmler) - * Clarify that tcmalloc stats are MiB (robinson) - * Remove support for non-tcmalloc debugallocation (blount) - * Add a new test: malloc_hook_test (csilvers) - * Change the configure script to be more crosstool-friendly (mcgrathr) - * PORTING: leading-underscore changes to support win64 (csilvers) - * Improve debugallocation tc_malloc_size (csilvers) - * Extend atomicops.h and cyceclock to use ARM V6+ optimized code (sanek) - * Change malloc-hook to use a list-like structure (llib) - * Add flag to use MAP_PRIVATE in memfs_malloc (gangren) - * Windows support for pprof: nul and /usr/bin/file (csilvers) - * TESTING: add test on strdup to tcmalloc_test (csilvers) - * Augment heap-checker to deal with no-inode maps (csilvers) - * Count .dll/.dylib as shared libs in heap-checker (csilvers) - * Disable sys_futex for arm; it's not always reliable (sanek) - * PORTING: change lots of windows/port.h macros to functions - * BUGFIX: Generate correct version# in tcmalloc.h on windows (csilvers) - * PORTING: Some casting to make solaris happier about types (csilvers) - * TESTING: Disable debugallocation_test in 'minimal' mode (csilvers) - * Rewrite debugallocation to be more modular (csilvers) - * Don't try to run the heap-checker under valgrind (ppluzhnikov) - * BUGFIX: Make focused stat %'s relative, not absolute (sanjay) - * BUGFIX: Don't use '//' comments in a C file (csilvers) - * Quiet new-gcc compiler warnings via -Wno-unused-result, etc (csilvers) - -Fri Feb 04 15:54:31 2011 Google Inc. - - * google-perftools: version 1.7 release - * Reduce page map key size under x86_64 by 4.4MB (rus) - * Remove a flaky malloc-extension test (fdabek) - * Improve the performance of PageHeap::New (ond..., csilvers) - * Improve sampling_test with no-inline additions/etc (fdabek) - * 16-byte align debug allocs (jyasskin) - * Change FillProcSelfMaps to detect out-of-buffer-space (csilvers) - * Document the need for sampling to use GetHeapSample (csilvers) - * Try to read TSC frequency from tsc_freq_khs (adurbin) - * Do better at figuring out if tests are running under gdb (ppluzhnikov) - * Improve spinlock contention performance (ruemmler) - * Better internal-function list for pprof's /contention (ruemmler) - * Speed up GoogleOnce (m3b) - * Limit number of incoming/outgoing edges in pprof (sanjay) - * Add pprof --evince to go along with --gv (csilvers) - * Document the various ways to get heap-profiling information (csilvers) - * Separate out synchronization profiling routines (ruemmler) - * Improve malloc-stats output to be more understandable (csilvers) - * Add support for census profiler in pporf (nabeelmian) - * Document how pprof's /symbol must support GET requests (csilvers) - * Improve acx_pthread.m4 (ssuomi, liujisi) - * Speed up pprof's ExtractSymbols (csilvers) - * Ignore some known-leaky (java) libraries in the heap checker (davidyu) - * Make kHideMask use all 64 bits in tests (ppluzhnikov) - * Clean up pprof input-file handling (csilvers) - * BUGFIX: Don't crash if __environ is NULL (csilvers) - * BUGFIX: Fix totally broken debugallocation tests (csilvers) - * BUGFIX: Fix up fake_VDSO handling for unittest (ppluzhnikov) - * BUGFIX: Suppress all large allocs when report threshold is 0 (lexie) - * BUGFIX: mmap2 on i386 takes an off_t, not off64_t (csilvers) - * PORTING: Add missing PERFTOOLS_DLL_DECL (csilvers) - * PORTING: Add stddef.h to make newer gcc's happy (csilvers) - * PORTING: Document some tricks for working under OS X (csilvers) - * PORTING: Don't try to check valgrind for windows (csilvers) - * PORTING: Make array-size a var to compile under clang (chandlerc) - * PORTING: No longer hook _aligned_malloc and _aligned_free (csilvers) - * PORTING: Quiet some gcc warnings (csilvers) - * PORTING: Replace %PRIxPTR with %p to be more portable (csilvers) - * PORTING: Support systems that capitalize /proc weirdly (sanek) - * PORTING: Treat arm3 the same as arm5t in cycletimer (csilvers) - * PORTING: Update windows logging to not allocate memory (csilvers) - * PORTING: avoid double-patching newer windows DLLs (roger.orr) - * PORTING: get dynamic_annotations.c to work on windows (csilvers) - * Add pkg-config .pc files for the 5 libraries we produce (csilvers) - * Added proper libtool versioning, so this lib will be 0.1.0 (csilvers) - * Moved from autoconf 2.64 to 2.65 - -Thu Aug 5 12:48:03 PDT 2010 Google Inc. - - * google-perftools: version 1.6 release - * Add tc_malloc_usable_size for compatibility with glibc (csilvers) - * Override malloc_usable_size with tc_malloc_usable_size (csilvers) - * Default to no automatic heap sampling in tcmalloc (csilvers) - * Add -DTCMALLOC_LARGE_PAGES, a possibly faster tcmalloc (rus) - * Make some functions extern "C" to avoid false ODR warnings (jyasskin) - * pprof: Add SVG-based output (rsc) - * pprof: Extend pprof --tools to allow per-tool configs (csilvers) - * pprof: Improve support of 64-bit and big-endian profiles (csilvers) - * pprof: Add interactive callgrind suport (weidenri...) - * pprof: Improve address->function mapping a bit (dpeng) - * Better detection of when we're running under valgrind (csilvers) - * Better CPU-speed detection under valgrind (saito) - * Use, and recommend, -fno-builtin-malloc when compiling (csilvers) - * Avoid false-sharing of memory between caches (bmaurer) - * BUGFIX: Fix heap sampling to use correct alloc size (bmauer) - * BUGFIX: Avoid gcc 4.0.x bug by making hook-clearing atomic (csilvers) - * BUGFIX: Avoid gcc 4.5.x optimization bug (csilvers) - * BUGFIX: Work around deps-determining bug in libtool 1.5.26 (csilvers) - * BUGFIX: Fixed test to use HAVE_PTHREAD, not HAVE_PTHREADS (csilvers) - * BUGFIX: Fix tls callback behavior on windows when using wpo (wtc) - * BUGFIX: properly align allocation sizes on Windows (antonm) - * BUGFIX: Fix prototypes for tcmalloc/debugalloc wrt throw() (csilvers) - * DOC: Updated heap-checker doc to match reality better (fischman) - * DOC: Document ProfilerFlush, ProfilerStartWithOptions (csilvers) - * DOC: Update docs for heap-profiler functions (csilvers) - * DOC: Clean up documentation around tcmalloc.slack_bytes (fikes) - * DOC: Renamed README.windows to README_windows.txt (csilvers) - * DOC: Update the NEWS file to be non-empty (csilvers) - * PORTING: Fix windows addr2line and nm with proper rc code (csilvers) - * PORTING: Add CycleClock and atomicops support for arm 5 (sanek) - * PORTING: Improve PC finding on cygwin and redhat 7 (csilvers) - * PORTING: speed up function-patching under windows (csilvers) - -Tue Jan 19 14:46:12 2010 Google Inc. - - * google-perftools: version 1.5 release - * Add tc_set_new_mode (willchan) - * Make memalign functions + realloc respect tc_set_new_mode (willchan) - * Add ReleaseToSystem(num_bytes) (kash) - * Handle zero-length symbols a bit better in pprof (csilvers) - * Prefer __environ to /proc/self/environ in cpu profiler (csilvers) - * Add HEAP_CHECK_MAX_LEAKS flag to control #leaks to report (glider) - * Add two new numeric pageheap properties to MallocExtension (fikes) - * Print alloc size when mmap fails (hakon) - * Add ITIMER_REAL support to cpu profiler (csilvers, nabeelmian) - * Speed up symbolizer in heap-checker reporting (glider) - * Speed up futexes with FUTEX_PRIVATE_FLAG (m3b) - * Speed up tcmalloc but doing better span coalescing (sanjay) - * Better support for different wget's and addr2maps in pprof (csilvres) - * Implement a nothrow version of delete and delete[] (csilvers) - * BUGFIX: fix a race on module_libcs[i] in windows patching (csilvers) - * BUGFIX: Fix debugallocation to call cpp_alloc for new (willchan) - * BUGFIX: A simple bugfix for --raw mode (mrabkin) - * BUGFIX: Fix C shims to actually be valid C (csilvers) - * BUGFIX: Fix recursively-unmapped-region accounting (ppluzhnikov) - * BUGFIX: better distinguish real and fake vdso (ppluzhnikov) - * WINDOWS: replace debugmodule with more reliable psai (andrey) - * PORTING: Add .bundle as another shared library extension (csilvers) - * PORTING: Fixed a typo bug in the ocnfigure PRIxx m4 macro (csilvers) - * PORTING: Augment sysinfo to work on 64-bit OS X (csilvers) - * PORTING: Use sys/ucontext.h to fix compiing on OS X 10.6 (csilvers) - * PORTING: Fix sysinfo libname reporting for solaris x86 (jeffrey) - * PORTING: Use libunwind for i386 when using --omitfp (ppluzhnikov) - -Thu Sep 10 13:51:15 2009 Google Inc. - - * google-perftools: version 1.4 release - * Add debugallocation library, to catch memory leaks, stomping, etc - * Add --raw mode to allow for delayed processing of pprof files - * Use less memory when reading CPU profiles - * New environment variables to control kernel-allocs (sbrk, memfs, etc) - * Add MarkThreadBusy(): performance improvement - * Remove static thread-cache-size code; all is dynamic now - * Add new HiddenPointer class to heap checker - * BUGFIX: pvalloc(0) allocates now (found by new debugalloc library) - * BUGFIX: valloc test (not implementation) no longer overruns memory - * BUGFIX: GetHeapProfile no longer deadlocks - * BUGFIX: Support unmapping memory regions before main - * BUGFIX: Fix some malloc-stats formatting - * BUGFIX: Don't crash as often when freeing libc-allocated memory - * BUGFIX: Deal better with incorrect PPROF_PATH when symbolizing - * BUGFIX: weaken new/delete/etc in addition to malloc/free/etc - * BUGFIX: Fix return value of GetAllocatedSize - * PORTING: Fix mmap-#define problem on some 64-bit systems - * PORTING: Call ranlib again (some OS X versions need it) - * PORTING: Fix a leak when building with LLVM - * PORTING: Remove some unneeded bash-ishs from testing scripts - * WINDOWS: Support library unloading as well as loading - * WINDOWS/BUGFIX: Set page to 'xrw' instead of 'rw' when patching - -Tue Jun 9 18:19:06 2009 Google Inc. - - * google-perftools: version 1.3 release - * Provide our own name for memory functions: tc_malloc, etc (csilvers) - * Weaken memory-alloc functions so user can override them (csilvers) - * Remove meaningless delete(nothrow) and delete[](nothrow) (csilvers) - * BUILD: replace clever libtcmalloc/profiler.a with a new .a (csilvers) - * PORTING: improve windows port by using google spinlocks (csilvers) - * PORTING: Fix RedHat 9 memory allocation in heapchecker (csilvers) - * PORTING: Rename OS_WINDOWS macro to PLATFORM_WINDOWS (mbelshe) - * PORTING/BUGFIX: Make sure we don't clobber GetLastError (mbelshe) - * BUGFIX: get rid of useless data for callgrind (weidenrinde) - * BUGFIX: Modify windows patching to deadlock sometimes (csilvers) - * BUGFIX: an improved fix for hook handling during fork (csilvers) - * BUGFIX: revamp profiler_unittest.sh, which was very broken (csilvers) - -Fri Apr 17 16:40:48 2009 Google Inc. - - * google-perftools: version 1.2 release - * Allow large_alloc_threshold=0 to turn it off entirely (csilvers) - * Die more helpfully when out of memory for internal data (csilvers) - * Refactor profile-data gathering, add a new unittest (cgd, nabeelmian) - * BUGFIX: fix rounding errors with static thread-size caches (addi) - * BUGFIX: disable hooks better when forking in leak-checker (csilvers) - * BUGFIX: fix realloc of crt pointers on windows (csilvers) - * BUGFIX: do a better job of finding binaries in .sh tests (csilvers) - * WINDOWS: allow overriding malloc/etc instead of patching (mbelshe) - * PORTING: fix compilation error in a ppc-specific file (csilvers) - * PORTING: deal with quirks in cygwin's /proc/self/maps (csilvers) - * PORTING: use 'A' version of functions for ascii input (mbelshe) - * PORTING: generate .so's on cygwin and mingw (ajenjo) - * PORTING: disable profiler methods on cygwin (jperkins) - * Updated autoconf version to 2.61 and libtool version to 1.5.26 - -Wed Mar 11 11:25:34 2009 Google Inc. - - * google-perftools: version 1.1 release - * Dynamically resize thread caches -- nice perf. improvement (kash) - * Add VDSO support to give better stacktraces in linux (ppluzhnikov) - * Improve heap-profiling sampling algorithm (ford) - * Rewrite leak-checking code: should be faster and more robust (sanjay) - * Use ps2 instead of ps for dot: better page cropping for gv (csilvers) - * Disable malloc-failure warning messages by default (csilvers) - * Update config/Makefile to disable tests on a per-OS basis (csilvers) - * PORTING: Get perftools compiling under MSVC 7.1 again (csilvers) - * PORTING: Get perftools compiling under cygwin again (csilvers) - * PORTING: automatically set library flags for solaris x86 (csilvers) - * Add TCMALLOC_SKIP_SBRK to mirror TCMALLOC_SKIP_MMAP (csilvers) - * Add --enable flags to allow selective building (csilvers) - * Put addr2line-pdb and nm-pdb in proper output directory (csilvers) - * Remove deprecated DisableChecksIn (sanjay) - * DOCUMENTATION: Document most MallocExtension routines (csilvers) - -Tue Jan 6 13:58:56 2009 Google Inc. - - * google-perftools: version 1.0 release - * Exactly the same as 1.0rc2 - -Sun Dec 14 17:10:35 2008 Google Inc. - - * google-perftools: version 1.0rc2 release - * Fix compile error on 64-bit systems (casting ptr to int) (csilvers) - -Thu Dec 11 16:01:32 2008 Google Inc. - - * google-perftools: version 1.0rc1 release - * Replace API for selectively disabling heap-checker in code (sanjay) - * Add a pre-mmap hook (daven, adlr) - * Add MallocExtension interface to set memory-releasing rate (fikes) - * Augment pprof to allow any string ending in /pprof/profile (csilvers) - * PORTING: Rewrite -- and fix -- malloc patching for windows (dvitek) - * PORTING: Add nm-pdb and addr2line-pdb for use by pprof (dvitek) - * PORTING: Improve cygwin and mingw support (jperkins, csilvers) - * PORTING: Fix pprof for mac os x, other pprof improvements (csilvers) - * PORTING: Fix some PPC bugs in our locking code (anton.blanchard) - * A new unittest, smapling_test, to verify tcmalloc-profiles (csilvers) - * Turn off TLS for gcc < 4.1.2, due to a TLS + -fPIC bug (csilvers) - * Prefer __builtin_frame_address to assembly for stacktraces (nlewycky) - * Separate tcmalloc.cc out into multiple files -- finally! (kash) - * Make our locking code work with -fPIC on 32-bit x86 (aruns) - * Fix an initialization-ordering bug for tcmalloc/profiling (csilvers) - * Use "initial exec" model of TLS to speed up tcmalloc (csilvers) - * Enforce 16-byte alignment for tcmalloc, for SSE (sanjay) - -Tue Sep 23 08:56:31 2008 Google Inc. - - * google-perftools: version 0.99.2 release - * COMPILE FIX: add #include needed for FreeBSD and OS X (csilvers) - -Sat Sep 20 09:37:18 2008 Google Inc. - - * google-perftools: version 0.99.1 release - * BUG FIX: look for nm, etc in /usr/bin, not /usr/crosstool (csilvers) - -Thu Sep 18 16:00:27 2008 Google Inc. - - * google-perftools: version 0.99 release - * Add IsHeapProfileRunning (csilvers) - * Add C shims for some of the C++ header files (csilvers) - * Fix heap profile file clean-up logic (maxim) - * Rename linuxthreads.c to .cc for better compiler support (csilvers) - * Add source info to disassembly in pprof (sanjay) - * Use open instead of fopen to avoid memory alloc (csilvers) - * Disable malloc extensions when running under valgrind (kcc) - * BUG FIX: Fix out-of-bound error by reordering a check (larryz) - * Add Options struct to ProfileData (cgd) - * Correct PC-handling of --base in pprof (csilvers) - * Handle 1 function occurring twice in an image (sanjay) - * Improve stack-data cleaning (maxim) - * Use 'struct Foo' to make header C compatible (csilvers) - * Add 'total' line to pprof --text (csilvers) - * Pre-allocate buffer for heap-profiler to avoid OOM errors (csilvers) - * Allow a few more env-settings to control tcmalloc (csilvers) - * Document some of the issues involving thread-local storage (csilvers) - * BUG FIX: Define strtoll and friends for windows (csilvers) - -Mon Jun 9 16:47:03 2008 Google Inc. - - * google-perftools: version 0.98 release - * Add ProfilerStartWithOptions() (cgd) - * Change tcmalloc_minimal to not do any stack-tracing at all (csilvers) - * Prefer mmap to sbrk for 64-buit debug mode (sanjay) - * Fix accounting for some tcmalloc stats (sanjay) - * Use setrlimit() to keep unittests from killing the machine (odo) - * Fix a bug when sbrk-ing near address 4G (csilvers) - * Make MallocHook thread-safe (jyasskin) - * Fix windows build for MemoryBarrier (jyasskin) - * Fix CPU-profiler docs to mention correct libs (csilvers) - * Fix for GetHeapProfile() when heap-profiling is off (maxim) - * Avoid realloc resizing ping-pongs using hysteresis (csilvers) - * Add --callgrind output support to pprof (klimek) - * Fix profiler.h and heap-profiler.h to be C-compatible (csilvers) - * Break malloc_hook.h into two parts to reduce dependencies (csilvers) - * Better handle systems that don't implement mmap (csilvers) - * PORTING: disable system_alloc_unittest for msvc (csilvers) - * PORTING: Makefile tweaks to build better on cygwin (csilvers) - -Mon Apr 21 15:20:52 2008 Google Inc. - - * google-perftools: version 0.97 release - * Refactor GetHeapProfile to avoid using malloc (maxim) - * Fix heap-checker and heap-profiler hook interactions (maxim) - * Fix a data race in MemoryRegionMap::Lock (jyasskin) - * Improve thread-safety of leak checker (maxim) - * Fix mmap profile to no longer deadlock (maxim) - * Fix rpm to have devel package depend on non-devel (csilvers) - * PORTING: Fix clock-speed detection for Mac OS X (csilvers) - -Tue Mar 18 14:30:44 2008 Google Inc. - - * google-perftools: version 0.96 release - * major atomicops rewrite; fixed atomic ops code for linux/ppc (vchen) - * nix the stacktrace library; now build structure is simpler (csilvers) - * Speed up heap-checker, and reduce extraneous logging (maxim) - * Improve itimer code for NPTL case (cgd) - * Add source code annotations for use by valgrind, etc (kcc) - * PORTING: Fix high resolution timers for Mac OS X (adlr) - -Tue Feb 19 12:01:31 2008 Google Inc. - - * google-perftools: version 0.95.1 release (bugfix release) - * x86_64 compile-fix: nix pread64 and pwrite64 (csilvers) - * more heap-checker debug logging (maxim) - * minor improvement to x86_64 CycleClock (gpike) - -Tue Feb 12 12:28:32 2008 Google Inc. - - * google-perftools: version 0.95 release - * Better -- not perfect -- support for linux-ppc (csilvers) - * Fix race condition in libunwind stacktrace (aruns) - * Speed up x86 spinlock locking (m3b) - * Improve heap-checker performance (maxim) - * Heap checker traverses more ptrs inside heap-alloced objects (maxim) - * Remove deprecated ProfilerThreadState function (cgd) - * Update libunwind documentation for statically linked binaries (aruns) - -Mon Dec 3 23:51:54 2007 Google Inc. - - * google-perftools: version 0.94.1 release (bugfix release) - * Fix missing #includes for x86_64 compile using libunwind (csilvers) - -Thu Nov 29 07:59:43 2007 Google Inc. - - * google-perftools: version 0.94 release - * PORTING: MinGW/Msys support -- runs same code as MSVC does (csilvers) - * PORTING: Add NumCPUs support for Mac OS X (csilvers) - * Work around a sscanf bug in glibc(?) (waldemar) - * Fix Windows MSVC bug triggered by thread deletion (csilvers) - * Fix bug that triggers in MSVC /O2: missing volatile (gpike) - * March-of-time support: quiet warnings/errors for gcc 4.2, OS X 10.5 - * Modify pprof so it works without nm: useful for windows (csilvers) - * pprof: Support filtering for CPU profiles (cgd) - * Bugfix: have realloc report to hooks in all situations (maxim) - * Speed improvement: replace slow memcpy with std::copy (soren) - * Speed: better iterator efficiency in RecordRegionRemoval (soren) - * Speed: minor speed improvements via better bitfield alignment (gpike) - * Documentation: add documentation of binary profile output (cgd) - -Fri Aug 17 12:32:56 2007 Google Inc. - - * google-perftools: version 0.93 release - * PORTING: everything compiles on Solaris, OS X, FreeBSD (see INSTALL) - * PORTING: cpu-profiler works on most platforms (much better GetPC()) - * PORTING: heap-profiler works on most platforms - * PORTING: improved windows support, including release builds - * No longer build or run ptmalloc tests by default - * Add support for using memfs filesystem to allocate memory in linux - * WINDOWS: give debug library and release library different names - -Tue Jul 17 22:26:27 2007 Google Inc. - - * google-perftools: version 0.92 release - * PERFORMANCE: use a packed cache to speed up tcmalloc - * PORTING: preliminary windows support! (see README.windows) - * PORTING: better support for solaris, OS X, FreeBSD (see INSTALL) - * Envvar support for running the heap-checker under gdb - * Add weak declarations to maybe_threads to fix no-pthreads compile bugs - * Some 64bit fixes, especially with pprof - * Better heap-checker support for some low-level allocations - * Fix bug where heap-profiles would sometimes get truncated - * New documentation about how to handle common heap leak situations - * Use computed includes for hash_map/set: easier config - * Added all used .m4 templates to the distribution - -Wed Apr 18 16:43:55 2007 Google Inc. - - * google-perftools: version 0.91 release - * Brown-paper-bag bugfix: compilation error on some x86-64 machines - -Fri Apr 13 14:50:51 2007 Google Inc. - - * google-perftools: version 0.90 release - * (As the version-number jump hints, this is a major new release: - almost every piece of functionality was rewritten. I can't do - justice to all the changes, but will concentrate on highlights.) - *** USER-VISIBLE CHANGES: - * Ability to "release" unused memory added to tcmalloc - * Exposed more tweaking knobs via environment variables (see docs) - * pprof tries harder to map addresses to functions - * tcmalloc_minimal compiles and runs on FreeBSD 6.0 and Solaris 10 - *** INTERNAL CHANGES: - * Much better 64-bit support - * Better multiple-processor support (e.g. multicore contention tweaks) - * Support for recent kernel ABI changes (e.g. new arg to mremap) - * Addition of spinlocks to tcmalloc to reduce contention cost - * Speed up tcmalloc by using __thread on systems that support TLS - * Total redesign of heap-checker to improve liveness checking - * More portable stack-frame analysis -- no more hard-coded constants! - * Disentangled heap-profiler code and heap-checker code - * Several new unittests to test, e.g., thread-contention costs - * Lots of small (but important!) bug fixes: e.g., fixing GetPC on amd64 - *** KNOWN PROBLEMS: - * CPU-profiling may crash on x86_64 (64-bit) systems. See the README - * Profiling/heap-checking may deadlock on x86_64 systems. See README - -Wed Jun 14 15:11:14 2006 Google Inc. - - * google-perftools: version 0.8 release - * Experimental support for remote profiling added to pprof (many) - * Fixed race condition in ProfileData::FlushTable (etune) - * Better support for weird /proc maps (maxim, mec) - * Fix heap-checker interaction with gdb (markus) - * Better 64-bit support in pprof (aruns) - * Reduce scavenging cost in tcmalloc by capping NumMoveSize (sanjay) - * Cast syscall(SYS_mmap); works on more 64-bit systems now (menage) - * Document the text output of pprof! (csilvers) - * Better compiler support for no-THREADS and for old compilers (csilvers) - * Make libunwind the default stack unwinder for x86-64 (aruns) - * Somehow the COPYING file got erased. Regenerate it (csilvers) - -Thu Apr 13 20:59:09 2006 Google Inc. - - * google-perftools: version 0.7 release - * Major rewrite of thread introspection for new kernels (markus) - * Major rewrite of heap-checker to use new thread tools (maxim) - * Add proper support for following data in thread registers (maxim) - * Syscall support for older kernels, including _syscall6 (markus) - * Support PIC mode (markus, mbland, iant) - * Better support for running in non-threaded contexts (csilvers) - -Fri Jan 27 14:04:27 2006 Google Inc. - - * google-perftools: version 0.6 release - * More sophisticated stacktrace usage, possibly using libunwind (aruns) - * Update pprof to handle 64-bit profiles (dehnert) - * Fix GetStackTrace to correctly return top stackframe (sanjay) - * Add ANSI compliance for new and new[], including new_handler (jkearney) - * More accuracy by reading ELF files directly rather than objdump (mec) - * Add readline support for pprof (addi) - * Add #includes for PPC (csilvers) - * New PC-detection routine for ibook powerpc (asbestoshead) - * Vastly improved tcmalloc unittest (csilvers) - * Move documentation from /usr/doc to /usr/share/doc - -Mon Nov 14 17:28:59 2005 Google Inc. - - * google-perftools: version 0.5 release - * Add va_start/va_end calls around vsnprintf() (csilvers) - * Write our own __syscall_return(), since it's not defined - consistently on all 64-bit linux distros (markus) - -Wed Oct 26 15:19:16 2005 Google Inc. - - * google-perftools: version 0.4 release - * Decrease fragmentation in tcmalloc (lefevere) - * Support for ARM in some of the thread-specific code (markus) - * Turn off heap-checker for statically-linked binaries, which - cause error leak reports now (etune) - * Many pprof improvements, including a command-line interface (jeff) - * CPU profiling now automatically affects all threads in linux 2.6. - (Kernel bugs break CPU profiling and threads in linux 2.4 a bit.) - ProfilerEnable() and ProfilerDisable() are deprecated. (sanjay) - * tcmalloc now correctly intercepts memalign (m3b, maxim) - * Syntax fix: added missing va_end()s. Helps non-gcc compiling (etune) - * Fixed a few coredumper bugs: race condition after PTRACE_DETACH, - ignore non-aligned stackframe pointers (markus, menage) - * 64-bit cleanup, especially for spinlock code (etune) and mmap (sanjay) - * Better support for finding threads in linux (markus) - * tcmalloc now tracks those stack traces that allocate memory (sanjay) - * Work around a weird setspecific problem (sanjay) - * Fix tcmalloc overflow problems when an alloc is close to 2G/4G (sanjay) - -Fri Jun 24 18:02:26 2005 Google Inc. - - * google-perftools: version 0.3 release - * Add missing errno include for one of the unittests (csilvers) - * Reduce tcmalloc startup memory from 5M to 256K (sanjay) - * Add support for mallopt() and mallinfo (sanjay) - * Improve stacktrace's performance on some 64-bit systems (etune) - * Improve the stacktrace unittest (etune) - -Tue May 31 08:14:38 2005 Google Inc. - - * google-perftools: version 0.2 release - * Use mmap2() instead of mmap(), to map more memory (menage) - * Do correct pthread-local checking in heap-checker! (maxim) - * Avoid overflow on 64-bit machines in pprof (sanjay) - * Add a few more GetPC() functions, including for AMD (csilvers) - * Better method for overriding pthread functions (menage) - * (Hacky) fix to avoid overwriting profile files after fork() (csilvers) - * Crashing bugfix involving dumping heaps on small-stack threads (tudor) - * Allow library versions with letters at the end (csilvers) - * Config fixes for systems that don't define PATH_MAX (csilvers) - * Confix fixes so we no longer need config.h after install (csilvers) - * Fix to pprof to correctly read very big cpu profiles (csilvers) - * Fix to pprof to deal with new commandline flags in modern gv's - * Better error reporting when we can't access /proc/maps (etune) - * Get rid of the libc-preallocate code (which could crash on some - systems); no longer needed with local-threads fix (csilvers) - -Tue Feb 8 09:57:17 2005 Google Inc. - - * google-perftools: initial release: - The google-perftools package contains some utilities to improve - and analyze the performance of C++ programs. This includes an - optimized thread-caching malloc() and cpu and heap profiling - utilities. +The ChangeLog is auto-generated when releasing. +If you are seeing this, use 'git log' for a detailed list of changes. diff --git a/tpl/gperftools/NEWS b/tpl/gperftools/NEWS index d5263e2..9938f0a 100644 --- a/tpl/gperftools/NEWS +++ b/tpl/gperftools/NEWS @@ -1,3 +1,418 @@ +== 29 Apr 2018 == +gperftools 2.7 is out! + +Few people contributed minor, but important fixes since rc. + +Changes: + +* bug in span stats printing introduced by new scalable page heap + change was fixed. + +* Christoph Müllner has contributed couple warnings fixes and initial + support for aarch64_ilp32 architecture. + +* Ben Dang contributed documentation fix for heap checker. + +* Fabrice Fontaine contributed fixed for linking benchmarks with + --disable-static. + +* Holy Wu has added sized deallocation unit tests. + +* Holy Wu has enabled support of sized deallocation (c++14) on recent + MSVC. + +* Holy Wu has fixed MSVC build in WIN32_OVERRIDE_ALLOCATORS mode. This + closed issue #716. + +* Holy Wu has contributed cleanup of config.h used on windows. + +* Mao Huang has contributed couple simple tcmalloc changes from + chromium code base. Making our tcmalloc forks a tiny bit closer. + +* issue #946 that caused compilation failures on some Linux clang + installations has been fixed. Much thanks to github user htuch for + helping to diagnose issue and proposing a fix. + +* Tulio Magno Quites Machado Filho has contributed build-time fix for + PPC (for problem introduced in one of commits since RC). + +== 18 Mar 2018 == +gperftools 2.7rc is out! + +Changes: + +* Most notable change in this release is that very large allocations + (>1MiB) are now handled be O(log n) implementation. This is + contributed by Todd Lipcon based on earlier work by Aliaksei + Kandratsenka and James Golick. Special thanks to Alexey Serbin for + contributing OSX fix for that commit. + +* detection of sized deallocation support is improved. Which should + fix another set of issues building on OSX. Much thanks to Alexey + Serbin for reporting the issue, suggesting a fix and verifying it. + +* Todd Lipcon made a change to extend page heaps freelists to 1 MiB + (up from 1MiB - 8KiB). This may help a little for some workloads. + +* Ishan Arora contributed typo fix to docs + +== 9 Dec 2017 == +gperftools 2.6.3 is out! + +Just two fixes were made in this release: + +* Stephan Zuercher has contributed a build fix for some recent XCode + versions. See issue #942 for more details. + +* assertion failure on some windows builds introduced by 2.6.2 was + fixed. Thanks to github user nkeemik for reporting it and testing + fix. See issue #944 for more details. + +== 30 Nov 2017 == +gperftools 2.6.2 is out! + +Most notable change is recently added support for C++17 over-aligned +allocation operators contributed by Andrey Semashev. I've extended his +implemention to have roughly same performance as malloc/new. This +release also has native support for C11 aligned_alloc. + +Rest is mostly bug fixes: + +* Jianbo Yang has contributed a fix for potentially severe data race + introduced by malloc fast-path work in gperftools 2.6. This race + could cause occasional violation of total thread cache size + constraint. See issue #929 for more details. + +* Correct behavior in out-of-memory condition in fast-path cases was + restored. This was another bug introduced by fast-path optimization + in gperftools 2.6 which caused operator new to silently return NULL + instead of doing correct C++ OOM handling (calling new_handler and + throwing bad_alloc). + +* Khem Raj has contributed couple build fixes for newer glibcs (ucontext_t vs + struct ucontext and loff_t definition) + +* Piotr Sikora has contributed build fix for OSX (not building unwind + benchmark). This was issue #910 (thanks to Yuriy Solovyov for + reporting it). + +* Dorin Lazăr has contributed fix for compiler warning + +* issue #912 (occasional deadlocking calling getenv too early on + windows) was fixed. Thanks to github user shangcangriluo for + reporting it. + +* Couple earlier lsan-related commits still causing occasional issues + linking on OSX has been reverted. See issue #901. + +* Volodimir Krylov has contributed GetProgramInvocationName for FreeBSD + +* changsu lee has contributed couple minor correctness fixes (missing + va_end() and missing free() call in rarely executed Symbolize path) + +* Andrew C. Morrow has contributed some more page heap stats. See issue + #935. + +* some cases of built-time warnings from various gcc/clang versions + about throw() declarations have been fixes. + +== 9 July 2017 == + +gperftools 2.6.1 is out! This is mostly bug-fixes release. + +* issue #901: build issue on OSX introduced in last-time commit in 2.6 + was fixed (contributed by Francis Ricci) + +* tcmalloc_minimal now works on 32-bit ABI of mips64. This is issue + #845. Much thanks to Adhemerval Zanella and github user mtone. + +* Romain Geissler contributed build fix for -std=c++17. This is pull + request #897. + +* As part of fixing issue #904, tcmalloc atfork handler is now + installed early. This should fix slight chance of hitting deadlocks + at fork in some cases. + +== 4 July 2017 == + +gperftools 2.6 is out! + +* Kim Gräsman contributed documentation update for HEAPPROFILESIGNAL + environment variable + +* KernelMaker contributed fix for population of min_object_size field + returned by MallocExtension::GetFreeListSizes + +* commit 8c3dc52fcfe0 "issue-654: [pprof] handle split text segments" + was reverted. Some OSX users reported issues with this commit. Given + our pprof implementation is strongly deprecated it is best to drop + recently introduced features rather than breaking it badly. + +* Francis Ricci contributed improvement for interaction with leak + sanitizer. + +== 22 May 2017 == + +gperftools 2.6rc4 is out! + +Dynamic sized delete is disabled by default again. There is no hope of +it working with eager dynamic symbols resolution (-z now linker +flag). More details in +https://bugzilla.redhat.com/show_bug.cgi?id=1452813 + +== 21 May 2017 == + +gperftools 2.6rc3 is out! + +gperftools compilation on older systems (e.g. rhel 5) was fixed. This +was originally reported in github issue #888. + +== 14 May 2017 == + +gperftools 2.6rc2 is out! + +Just 2 small fixes on top of 2.6rc. Particularly, Rajalakshmi +Srinivasaraghavan contributed build fix for ppc32. + +== 14 May 2017 == + +gperftools 2.6rc is out! + +Highlights of this release are performance work on malloc fast-path +and support for more modern visual studio runtimes, and deprecation of +bundled pprof. Another significant performance-affecting changes are +reverting central free list transfer batch size back to 32 and +disabling of aggressive decommit mode by default. + +Note, while we still ship perl implementation of pprof, everyone is +strongly advised to use golang reimplementation of pprof from +https://github.com/google/pprof. + +Here are notable changes in more details (and see ChangeLog for full +details): + +* a bunch of performance tweaks to tcmalloc fast-path were + merged. This speeds up critical path of tcmalloc by few tens of + %. Well tuned and allocation-heavy programs should see substantial + performance boost (should apply to all modern elf platforms). This + is based on Google-internal tcmalloc changes for fast-path (with + obvious exception of lacking per-cpu mode, of course). Original + changes were made by Aliaksei Kandratsenka. And Andrew Hunter, + Dmitry Vyukov and Sanjay Ghemawat contributed with reviews and + discussions. + +* Architectures with 48 bits address space (x86-64 and aarch64) now + use faster 2 level page map. This was ported from Google-internal + change by Sanjay Ghemawat. + +* Default value of TCMALLOC_TRANSFER_NUM_OBJ was returned back to + 32. Larger values have been found to hurt certain programs (but help + some other benchmarks). Value can still be tweaked at run time via + environment variable. + +* tcmalloc aggressive decommit mode is now disabled by default + again. It was found to degrade performance of certain tensorflow + benchmarks. Users who prefer smaller heap over small performance win + can still set environment variable TCMALLOC_AGGRESSIVE_DECOMMIT=t. + +* runtime switchable sized delete support has be fixed and re-enabled + (on GNU/Linux). Programs that use C++ 14 or later that use sized + delete can again be sped up by setting environment variable + TCMALLOC_ENABLE_SIZED_DELETE=t. Support for enabling sized + deallication support at compile-time is still present, of course. + +* tcmalloc now explicitly avoids use of MADV_FREE on Linux, unless + TCMALLOC_USE_MADV_FREE is defined at compile time. This is because + performance impact of MADV_FREE is not well known. Original issue + #780 raised by Mathias Stearn. + +* issue #786 with occasional deadlocks in stack trace capturing via + libunwind was fixed. It was originally reported as Ceph issue: + http://tracker.ceph.com/issues/13522 + +* ChangeLog is now automatically generated from git log. Old ChangeLog + is now ChangeLog.old. + +* tcmalloc now provides implementation of nallocx. Function was + originally introduced by jemalloc and can be used to return real + allocation size given allocation request size. This is ported from + Google-internal tcmalloc change contributed by Dmitry Vyukov. + +* issue #843 which made tcmalloc crash when used with erlang runtime + was fixed. + +* issue #839 which caused tcmalloc's aggressive decommit mode to + degrade performance in some corner cases was fixed. + +* Bryan Chan contributed support for 31-bit s390. + +* Brian Silverman contributed compilation fix for 32-bit ARMs + +* Issue #817 that was causing tcmalloc to fail on windows 10 and + later, as well as on recent msvc was fixed. We now patch _free_base + as well. + +* a bunch of minor documentaion/typos fixes by: Mike Gaffney + , iivlev , savefromgoogle + , John McDole + , zmertens , Kirill Müller + , Eugene , Ola Olsson + , Mostyn Bramley-Moore + +* Tulio Magno Quites Machado Filho has contributed removal of + deprecated glibc malloc hooks. + +* Issue #827 that caused intercepting malloc on osx 10.12 to fail was + fixed, by copying fix made by Mike Hommey to jemalloc. Much thanks + to Koichi Shiraishi and David Ribeiro Alves for reporting it and + testing fix. + +* Aman Gupta and Kenton Varda contributed minor fixes to pprof (but + note again that pprof is deprecated) + +* Ryan Macnak contributed compilation fix for aarch64 + +* Francis Ricci has fixed unaligned memory access in debug allocator + +* TCMALLOC_PAGE_FENCE_NEVER_RECLAIM now actually works thanks to + contribution by Andrew Morrow. + +== 12 Mar 2016 == + +gperftools 2.5 is out! + +Just single bugfix was merged after rc2. Which was fix for issue #777. + +== 5 Mar 2016 == + +gperftools 2.5rc2 is out! + +New release contains just few commits on top of first release +candidate. One of them is build fix for Visual Studio. Another +significant change is that dynamic sized delete is now disabled by +default. It turned out that IFUNC relocations are not supporting our +advanced use case on all platforms and in all cases. + +== 21 Feb 2016 == + +gperftools 2.5rc is out! + +Here are major changes since 2.4: + +* we've moved to github! + +* Bryan Chan has contributed s390x support + +* stacktrace capturing via libgcc's _Unwind_Backtrace was implemented + (for architectures with missing or broken libunwind). + +* "emergency malloc" was implemented. Which unbreaks recursive calls + to malloc/free from stacktrace capturing functions (such us glib'c + backtrace() or libunwind on arm). It is enabled by + --enable-emergency-malloc configure flag or by default on arm when + --enable-stacktrace-via-backtrace is given. It is another fix for a + number common issues people had on platforms with missing or broken + libunwind. + +* C++14 sized-deallocation is now supported (on gcc 5 and recent + clangs). It is off by default and can be enabled at configure time + via --enable-sized-delete. On GNU/Linux it can also be enabled at + run-time by either TCMALLOC_ENABLE_SIZED_DELETE environment variable + or by defining tcmalloc_sized_delete_enabled function which should + return 1 to enable it. + +* we've lowered default value of transfer batch size to 512. Previous + value (bumped up in 2.1) was too high and caused performance + regression for some users. 512 should still give us performance + boost for workloads that need higher transfer batch size while not + penalizing other workloads too much. + +* Brian Silverman's patch finally stopped arming profiling timer + unless profiling is started. + +* Andrew Morrow has contributed support for obtaining cache size of the + current thread and softer idling (for use in MongoDB). + +* we've implemented few minor performance improvements, particularly + on malloc fast-path. + +A number of smaller fixes were made. Many of them were contributed: + +* issue that caused spurious profiler_unittest.sh failures was fixed. + +* Jonathan Lambrechts contributed improved callgrind format support to + pprof. + +* Matt Cross contributed better support for debug symbols in separate + files to pprof. + +* Matt Cross contributed support for printing collapsed stack frame + from pprof aimed at producing flame graphs. + +* Angus Gratton has contributed documentation fix mentioning that on + windows only tcmalloc_minimal is supported. + +* Anton Samokhvalov has made tcmalloc use mi_force_{un,}lock on OSX + instead of pthread_atfork. Which apparently fixes forking + issues tcmalloc had on OSX. + +* Milton Chiang has contributed support for building 32-bit gperftools + on arm8. + +* Patrick LoPresti has contributed support for specifying alternative + profiling signal via CPUPROFILE_TIMER_SIGNAL environment variable. + +* Paolo Bonzini has contributed support configuring filename for + sending malloc tracing output via TCMALLOC_TRACE_FILE environment + variable. + +* user spotrh has enabled use of futex on arm. + +* user mitchblank has contributed better declaration for arg-less + profiler functions. + +* Tom Conerly contributed proper freeing of memory allocated in + HeapProfileTable::FillOrderedProfile on error paths. + +* user fdeweerdt has contributed curl arguments handling fix in pprof + +* Frederik Mellbin fixed tcmalloc's idea of mangled new and delete + symbols on windows x64 + +* Dair Grant has contributed cacheline alignment for ThreadCache + objects + +* Fredrik Mellbin has contributed updated windows/config.h for Visual + Studio 2015 and other windows fixes. + +* we're not linking libpthread to libtcmalloc_minimal anymore. Instead + libtcmalloc_minimal links to pthread symbols weakly. As a result + single-threaded programs remain single-threaded when linking to or + preloading libtcmalloc_minimal.so. + +* Boris Sazonov has contributed mips compilation fix and printf misue + in pprof. + +* Adhemerval Zanella has contributed alignment fixes for statically + allocated variables. + +* Jens Rosenboom has contributed fixes for heap-profiler_unittest.sh + +* gshirishfree has contributed better description for GetStats method. + +* cyshi has contributed spinlock pause fix. + +* Chris Mayo has contributed --docdir argument support for configure. + +* Duncan Sands has contributed fix for function aliases. + +* Simon Que contributed better include for malloc_hook_c.h + +* user wmamrak contributed struct timespec fix for Visual Studio 2015. + +* user ssubotin contributed typo in PrintAvailability code. + + == 10 Jan 2015 == gperftools 2.4 is out! The code is exactly same as 2.4rc. diff --git a/tpl/gperftools/README b/tpl/gperftools/README index bffc617..8f61410 100644 --- a/tpl/gperftools/README +++ b/tpl/gperftools/README @@ -1,7 +1,26 @@ -IMPORTANT NOTE FOR 64-BIT USERS -------------------------------- -There are known issues with some perftools functionality on x86_64 -systems. See 64-BIT ISSUES, below. +gperftools +---------- +(originally Google Performance Tools) + +The fastest malloc we’ve seen; works particularly well with threads +and STL. Also: thread-friendly heap-checker, heap-profiler, and +cpu-profiler. + + +OVERVIEW +--------- + +gperftools is a collection of a high-performance multi-threaded +malloc() implementation, plus some pretty nifty performance analysis +tools. + +gperftools is distributed under the terms of the BSD License. Join our +mailing list at gperftools@googlegroups.com for updates: +https://groups.google.com/forum/#!forum/gperftools + +gperftools was original home for pprof program. But do note that +original pprof (which is still included with gperftools) is now +deprecated in favor of golang version at https://github.com/google/pprof TCMALLOC @@ -30,7 +49,7 @@ the above flags :-) ). HEAP PROFILER ------------- -See doc/heap-profiler.html for information about how to use tcmalloc's +See docs/heapprofile.html for information about how to use tcmalloc's heap profiler and analyze its output. As a quick-start, do the following after installing this package: @@ -55,7 +74,7 @@ see INSTALL for more details. It is not currently available on Windows. HEAP CHECKER ------------ -See doc/heap-checker.html for information about how to use tcmalloc's +See docs/heap_checker.html for information about how to use tcmalloc's heap checker. In order to catch all heap leaks, tcmalloc must be linked *last* into @@ -83,7 +102,7 @@ for more details. CPU PROFILER ------------ -See doc/cpu-profiler.html for information about how to use the CPU +See docs/cpuprofile.html for information about how to use the CPU profiler and analyze its output. As a quick-start, do the following after installing this package: @@ -152,13 +171,13 @@ PROFILESELECTED=1 -- if set, cpu-profiler will only profile regions of code surrounded with ProfilerEnable()/ProfilerDisable(). CPUPROFILE_FREQUENCY=x-- how many interrupts/second the cpu-profiler samples. -TCMALLOC_DEBUG= -- the higher level, the more messages malloc emits +PERFTOOLS_VERBOSE= -- the higher level, the more messages malloc emits MALLOCSTATS= -- prints memory-use stats at program-exit For a full list of variables, see the documentation pages: - doc/cpuprofile.html - doc/heapprofile.html - doc/heap_checker.html + docs/cpuprofile.html + docs/heapprofile.html + docs/heap_checker.html COMPILING ON NON-LINUX SYSTEMS diff --git a/tpl/gperftools/benchmark/malloc_bench.cc b/tpl/gperftools/benchmark/malloc_bench.cc index e5e0d38..371b8c6 100644 --- a/tpl/gperftools/benchmark/malloc_bench.cc +++ b/tpl/gperftools/benchmark/malloc_bench.cc @@ -1,93 +1,293 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #include #include +#include + +#include + +#include "run_benchmark.h" + +static void bench_fastpath_throughput(long iterations, + uintptr_t param) +{ + size_t sz = 32; + for (; iterations>0; iterations--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + free(p); + // this makes next iteration use different free list. So + // subsequent iterations may actually overlap in time. + sz = ((sz * 8191) & 511) + 16; + } +} + +static void bench_fastpath_dependent(long iterations, + uintptr_t param) +{ + size_t sz = 32; + for (; iterations>0; iterations--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + free(p); + // this makes next iteration depend on current iteration. But this + // iteration's free may still overlap with next iteration's malloc + sz = ((sz | reinterpret_cast(p)) & 511) + 16; + } +} + +static void bench_fastpath_simple(long iterations, + uintptr_t param) +{ + size_t sz = static_cast(param); + for (; iterations>0; iterations--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + free(p); + // next iteration will use same free list as this iteration. So it + // should be prevent next iterations malloc to go too far before + // free done. But using same size will make free "too fast" since + // we'll hit size class cache. + } +} + +#ifdef __GNUC__ +#define HAVE_SIZED_FREE_OPTION + +extern "C" void tc_free_sized(void *ptr, size_t size) __attribute__((weak)); +extern "C" void *tc_memalign(size_t align, size_t size) __attribute__((weak)); + +static bool is_sized_free_available(void) +{ + return tc_free_sized != NULL; +} + +static bool is_memalign_available(void) +{ + return tc_memalign != NULL; +} + +static void bench_fastpath_simple_sized(long iterations, + uintptr_t param) +{ + size_t sz = static_cast(param); + for (; iterations>0; iterations--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + tc_free_sized(p, sz); + // next iteration will use same free list as this iteration. So it + // should be prevent next iterations malloc to go too far before + // free done. But using same size will make free "too fast" since + // we'll hit size class cache. + } +} + +static void bench_fastpath_memalign(long iterations, + uintptr_t param) +{ + size_t sz = static_cast(param); + for (; iterations>0; iterations--) { + void *p = tc_memalign(32, sz); + if (!p) { + abort(); + } + free(p); + // next iteration will use same free list as this iteration. So it + // should be prevent next iterations malloc to go too far before + // free done. But using same size will make free "too fast" since + // we'll hit size class cache. + } +} + +#endif // __GNUC__ + +#define STACKSZ (1 << 16) + +static void bench_fastpath_stack(long iterations, + uintptr_t _param) +{ + + void *stack[STACKSZ]; + size_t sz = 64; + long param = static_cast(_param); + param &= STACKSZ - 1; + param = param ? param : 1; + for (; iterations>0; iterations -= param) { + for (long k = param-1; k >= 0; k--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + stack[k] = p; + // this makes next iteration depend on result of this iteration + sz = ((sz | reinterpret_cast(p)) & 511) + 16; + } + for (long k = 0; k < param; k++) { + free(stack[k]); + } + } +} + +static void bench_fastpath_stack_simple(long iterations, + uintptr_t _param) +{ + + void *stack[STACKSZ]; + size_t sz = 128; + long param = static_cast(_param); + param &= STACKSZ - 1; + param = param ? param : 1; + for (; iterations>0; iterations -= param) { + for (long k = param-1; k >= 0; k--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + stack[k] = p; + } + for (long k = 0; k < param; k++) { + free(stack[k]); + } + } +} + +static void bench_fastpath_rnd_dependent(long iterations, + uintptr_t _param) +{ + static const uintptr_t rnd_c = 1013904223; + static const uintptr_t rnd_a = 1664525; + + void *ptrs[STACKSZ]; + size_t sz = 128; + if ((_param & (_param - 1))) { + abort(); + } + if (_param > STACKSZ) { + abort(); + } + int param = static_cast(_param); + + for (; iterations>0; iterations -= param) { + for (int k = param-1; k >= 0; k--) { + void *p = malloc(sz); + if (!p) { + abort(); + } + ptrs[k] = p; + sz = ((sz | reinterpret_cast(p)) & 511) + 16; + } + + // this will iterate through all objects in order that is + // unpredictable to processor's prefetchers + uint32_t rnd = 0; + uint32_t free_idx = 0; + do { + free(ptrs[free_idx]); + rnd = rnd * rnd_a + rnd_c; + free_idx = rnd & (param - 1); + } while (free_idx != 0); + } +} + +static void *randomize_buffer[13<<20]; + + +void randomize_one_size_class(size_t size) { + int count = (100<<20) / size; + if (count * sizeof(randomize_buffer[0]) > sizeof(randomize_buffer)) { + abort(); + } + for (int i = 0; i < count; i++) { + randomize_buffer[i] = malloc(size); + } + std::random_shuffle(randomize_buffer, randomize_buffer + count); + for (int i = 0; i < count; i++) { + free(randomize_buffer[i]); + } +} + +void randomize_size_classes() { + randomize_one_size_class(8); + int i; + for (i = 16; i < 256; i += 16) { + randomize_one_size_class(i); + } + for (; i < 512; i += 32) { + randomize_one_size_class(i); + } + for (; i < 1024; i += 64) { + randomize_one_size_class(i); + } + for (; i < (4 << 10); i += 128) { + randomize_one_size_class(i); + } + for (; i < (32 << 10); i += 1024) { + randomize_one_size_class(i); + } +} int main(void) { - long long i = 1LL<<(28-4); - size_t sz = 32; - printf("i = %lld\n", i); - for (;i>0;i--) { - void *p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - p = malloc(sz); - if (!p) { - abort(); - } - free(p); - sz = ((sz | reinterpret_cast(p)) & 511) + 16; - } - return 0; + randomize_size_classes(); + + report_benchmark("bench_fastpath_throughput", bench_fastpath_throughput, 0); + report_benchmark("bench_fastpath_dependent", bench_fastpath_dependent, 0); + report_benchmark("bench_fastpath_simple", bench_fastpath_simple, 64); + report_benchmark("bench_fastpath_simple", bench_fastpath_simple, 2048); + report_benchmark("bench_fastpath_simple", bench_fastpath_simple, 16384); + +#ifdef HAVE_SIZED_FREE_OPTION + if (is_sized_free_available()) { + report_benchmark("bench_fastpath_simple_sized", bench_fastpath_simple_sized, 64); + report_benchmark("bench_fastpath_simple_sized", bench_fastpath_simple_sized, 2048); + } + + if (is_memalign_available()) { + report_benchmark("bench_fastpath_memalign", bench_fastpath_memalign, 64); + report_benchmark("bench_fastpath_memalign", bench_fastpath_memalign, 2048); + } + +#endif + + for (int i = 8; i <= 512; i <<= 1) { + report_benchmark("bench_fastpath_stack", bench_fastpath_stack, i); + } + report_benchmark("bench_fastpath_stack_simple", bench_fastpath_stack_simple, 32); + report_benchmark("bench_fastpath_stack_simple", bench_fastpath_stack_simple, 8192); + report_benchmark("bench_fastpath_rnd_dependent", bench_fastpath_rnd_dependent, 32); + report_benchmark("bench_fastpath_rnd_dependent", bench_fastpath_rnd_dependent, 8192); + return 0; } diff --git a/tpl/gperftools/doc/cpuprofile-fileformat.html b/tpl/gperftools/doc/cpuprofile-fileformat.html deleted file mode 100644 index 3f90e6b..0000000 --- a/tpl/gperftools/doc/cpuprofile-fileformat.html +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - Google CPU Profiler Binary Data File Format - - - - -

Google CPU Profiler Binary Data File Format

- -

- Last modified - -

- -

This file documents the binary data file format produced by the -Google CPU Profiler. For information about using the CPU Profiler, -see its user guide. - -

The profiler source code, which generates files using this format, is at -src/profiler.cc. - - -

CPU Profile Data File Structure

- -

CPU profile data files each consist of four parts, in order: - -

    -
  • Binary header -
  • Binary profile records -
  • Binary trailer -
  • Text list of mapped objects -
- -

The binary data is expressed in terms of "slots." These are words -large enough to hold the program's pointer type, i.e., for 32-bit -programs they are 4 bytes in size, and for 64-bit programs they are 8 -bytes. They are stored in the profile data file in the native byte -order (i.e., little-endian for x86 and x86_64). - - -

Binary Header

- -

The binary header format is show below. Values written by the -profiler, along with requirements currently enforced by the analysis -tools, are shown in parentheses. - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
slotdata
0header count (0; must be 0)
1header slots after this one (3; must be >= 3)
2format version (0; must be 0)
3sampling period, in microseconds
4padding (0)
- -

The headers currently generated for 32-bit and 64-bit little-endian -(x86 and x86_64) profiles are shown below, for comparison. - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
hdr counthdr wordsversionsampling periodpad
32-bit or 64-bit (slots)030100000
32-bit (4-byte words in file)0x000000x000030x000000x027100x00000
64-bit LE (4-byte words in file)0x00000 0x000000x00003 0x000000x00000 0x000000x02710 0x000000x00000 0x00000
- -

The contents are shown in terms of slots, and in terms of 4-byte -words in the profile data file. The slot contents for 32-bit and -64-bit headers are identical. For 32-bit profiles, the 4-byte word -view matches the slot view. For 64-bit profiles, each (8-byte) slot -is shown as two 4-byte words, ordered as they would appear in the -file. - -

The profiling tools examine the contents of the file and use the -expected locations and values of the header words field to detect -whether the file is 32-bit or 64-bit. - - -

Binary Profile Records

- -

The binary profile record format is shown below. - -

- - - - - - - - - - - - - - - - - - - -
slotdata
0sample count, must be >= 1
1number of call chain PCs (num_pcs), must be >= 1
2 .. (num_pcs + 1)call chain PCs, most-recently-called function first. -
- -

The total length of a given record is 2 + num_pcs. - -

Note that multiple profile records can be emitted by the profiler -having an identical call chain. In that case, analysis tools should -sum the counts of all records having identical call chains. - -

Note: Some profile analysis tools terminate if they see -any profile record with a call chain with its first entry -having the address 0. (This is similar to the binary trailer.) - -

Example

- -This example shows the slots contained in a sample profile record. - -

- - - - - - - - -
530xa00000xc00000xe0000
- -

In this example, 5 ticks were received at PC 0xa0000, whose -function had been called by the function containing 0xc0000, which had -been called from the function containing 0xe0000. - - -

Binary Trailer

- -

The binary trailer consists of three slots of data with fixed -values, shown below. - -

- - - - - - - - - - - - - - - - - - - - -
slotvalue
00
11
20
- -

Note that this is the same data that would contained in a profile -record with sample count = 0, num_pcs = 1, and a one-element call -chain containing the address 0. - - -

Text List of Mapped Objects

- -

The binary data in the file is followed immediately by a list of -mapped objects. This list consists of lines of text separated by -newline characters. - -

Each line is one of the following types: - -

    -
  • Build specifier, starting with "build=". For example: -
      build=/path/to/binary
    - Leading spaces on the line are ignored. - -
  • Mapping line from ProcMapsIterator::FormatLine. For example: -
      40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
    - The first address must start at the beginning of the line. -
- -

Unrecognized lines should be ignored by analysis tools. - -

When processing the paths see in mapping lines, occurrences of -$build followed by a non-word character (i.e., characters -other than underscore or alphanumeric characters), should be replaced -by the path given on the last build specifier line. - -


-
Chris Demetriou
- - -Last modified: Mon Aug 27 12:18:26 PDT 2007 (cgd) - -
- - diff --git a/tpl/gperftools/doc/cpuprofile.html b/tpl/gperftools/doc/cpuprofile.html deleted file mode 100644 index c81feb6..0000000 --- a/tpl/gperftools/doc/cpuprofile.html +++ /dev/null @@ -1,536 +0,0 @@ - - - - - - Gperftools CPU Profiler - - - - -

- Last modified - -

- -

This is the CPU profiler we use at Google. There are three parts -to using it: linking the library into an application, running the -code, and analyzing the output.

- -

On the off-chance that you should need to understand it, the CPU -profiler data file format is documented separately, -here. - - -

Linking in the Library

- -

To install the CPU profiler into your executable, add --lprofiler to the link-time step for your executable. -(It's also probably possible to add in the profiler at run-time using -LD_PRELOAD, e.g. -% env LD_PRELOAD="/usr/lib/libprofiler.so" <binary>, -but this isn't necessarily recommended.)

- -

This does not turn on CPU profiling; it just inserts the -code. For that reason, it's practical to just always link --lprofiler into a binary while developing; that's what we -do at Google. (However, since any user can turn on the profiler by -setting an environment variable, it's not necessarily recommended to -install profiler-linked binaries into a production, running -system.)

- - -

Running the Code

- -

There are several alternatives to actually turn on CPU profiling -for a given run of an executable:

- -
    -
  1. Define the environment variable CPUPROFILE to the filename - to dump the profile to. For instance, if you had a version of - /bin/ls that had been linked against libprofiler, - you could run:

    -
    % env CPUPROFILE=ls.prof /bin/ls
    -
  2. -
  3. In addition to defining the environment variable CPUPROFILE - you can also define CPUPROFILESIGNAL. This allows profiling to be - controlled via the signal number that you specify. The signal number - must be unused by the program under normal operation. Internally it - acts as a switch, triggered by the signal, which is off by default. - For instance, if you had a copy of /bin/chrome that had been - been linked against libprofiler, you could run:

    -
    % env CPUPROFILE=chrome.prof CPUPROFILESIGNAL=12 /bin/chrome &
    -

    You can then trigger profiling to start:

    -
    % killall -12 chrome
    -

    Then after a period of time you can tell it to stop which will - generate the profile:

    -
    % killall -12 chrome
    -
  4. -
  5. In your code, bracket the code you want profiled in calls to - ProfilerStart() and ProfilerStop(). - (These functions are declared in <gperftools/profiler.h>.) - ProfilerStart() will take - the profile-filename as an argument.

    -
  6. -
- -

In Linux 2.6 and above, profiling works correctly with threads, -automatically profiling all threads. In Linux 2.4, profiling only -profiles the main thread (due to a kernel bug involving itimers and -threads). Profiling works correctly with sub-processes: each child -process gets its own profile with its own name (generated by combining -CPUPROFILE with the child's process id).

- -

For security reasons, CPU profiling will not write to a file -- and -is thus not usable -- for setuid programs.

- -

See the include-file gperftools/profiler.h for -advanced-use functions, including ProfilerFlush() and -ProfilerStartWithOptions().

- - -

Modifying Runtime Behavior

- -

You can more finely control the behavior of the CPU profiler via -environment variables.

- - - - - - - - - - - - - - - -
CPUPROFILE_FREQUENCY=xdefault: 100 - How many interrupts/second the cpu-profiler samples. -
CPUPROFILE_REALTIME=1default: [not set] - If set to any value (including 0 or the empty string), use - ITIMER_REAL instead of ITIMER_PROF to gather profiles. In - general, ITIMER_REAL is not as accurate as ITIMER_PROF, and also - interacts badly with use of alarm(), so prefer ITIMER_PROF unless - you have a reason prefer ITIMER_REAL. -
- - -

Analyzing the Output

- -

pprof is the script used to analyze a profile. It has -many output modes, both textual and graphical. Some give just raw -numbers, much like the -pg output of gcc, -and others show the data in the form of a dependency graph.

- -

pprof requires perl5 to be installed to run. -It also requires dot to be installed for any of the -graphical output routines, and gv to be installed for ---gv mode (described below). -

- -

Here are some ways to call pprof. These are described in more -detail below.

- -
-% pprof /bin/ls ls.prof
-                       Enters "interactive" mode
-% pprof --text /bin/ls ls.prof
-                       Outputs one line per procedure
-% pprof --gv /bin/ls ls.prof
-                       Displays annotated call-graph via 'gv'
-% pprof --gv --focus=Mutex /bin/ls ls.prof
-                       Restricts to code paths including a .*Mutex.* entry
-% pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
-                       Code paths including Mutex but not string
-% pprof --list=getdir /bin/ls ls.prof
-                       (Per-line) annotated source listing for getdir()
-% pprof --disasm=getdir /bin/ls ls.prof
-                       (Per-PC) annotated disassembly for getdir()
-% pprof --text localhost:1234
-                       Outputs one line per procedure for localhost:1234
-% pprof --callgrind /bin/ls ls.prof
-                       Outputs the call information in callgrind format
-
- - -

Analyzing Text Output

- -

Text mode has lines of output that look like this:

-
-       14   2.1%  17.2%       58   8.7% std::_Rb_tree::find
-
- -

Here is how to interpret the columns:

-
    -
  1. Number of profiling samples in this function -
  2. Percentage of profiling samples in this function -
  3. Percentage of profiling samples in the functions printed so far -
  4. Number of profiling samples in this function and its callees -
  5. Percentage of profiling samples in this function and its callees -
  6. Function name -
- -

Analyzing Callgrind Output

- -

Use kcachegrind to -analyze your callgrind output:

-
-% pprof --callgrind /bin/ls ls.prof > ls.callgrind
-% kcachegrind ls.callgrind
-
- -

The cost is specified in 'hits', i.e. how many times a function -appears in the recorded call stack information. The 'calls' from -function a to b record how many times function b was found in the -stack traces directly below function a.

- -

Tip: if you use a debug build the output will include file and line -number information and kcachegrind will show an annotated source -code view.

- -

Node Information

- -

In the various graphical modes of pprof, the output is a call graph -annotated with timing information, like so:

- - -
- -
-
- -

Each node represents a procedure. The directed edges indicate -caller to callee relations. Each node is formatted as follows:

- -
-Class Name
-Method Name
-local (percentage)
-of cumulative (percentage)
-
- -

The last one or two lines contains the timing information. (The -profiling is done via a sampling method, where by default we take 100 -samples a second. Therefor one unit of time in the output corresponds -to about 10 milliseconds of execution time.) The "local" time is the -time spent executing the instructions directly contained in the -procedure (and in any other procedures that were inlined into the -procedure). The "cumulative" time is the sum of the "local" time and -the time spent in any callees. If the cumulative time is the same as -the local time, it is not printed.

- -

For instance, the timing information for test_main_thread() -indicates that 155 units (about 1.55 seconds) were spent executing the -code in test_main_thread() and 200 units were spent while -executing test_main_thread() and its callees such as -snprintf().

- -

The size of the node is proportional to the local count. The -percentage displayed in the node corresponds to the count divided by -the total run time of the program (that is, the cumulative count for -main()).

- -

Edge Information

- -

An edge from one node to another indicates a caller to callee -relationship. Each edge is labelled with the time spent by the callee -on behalf of the caller. E.g, the edge from -test_main_thread() to snprintf() indicates -that of the 200 samples in test_main_thread(), 37 are -because of calls to snprintf().

- -

Note that test_main_thread() has an edge to -vsnprintf(), even though test_main_thread() -doesn't call that function directly. This is because the code was -compiled with -O2; the profile reflects the optimized -control flow.

- -

Meta Information

- -

The top of the display should contain some meta information -like:

-
-      /tmp/profiler2_unittest
-      Total samples: 202
-      Focusing on: 202
-      Dropped nodes with <= 1 abs(samples)
-      Dropped edges with <= 0 samples
-
- -

This section contains the name of the program, and the total -samples collected during the profiling run. If the ---focus option is on (see the Focus -section below), the legend also contains the number of samples being -shown in the focused display. Furthermore, some unimportant nodes and -edges are dropped to reduce clutter. The characteristics of the -dropped nodes and edges are also displayed in the legend.

- -

Focus and Ignore

- -

You can ask pprof to generate a display focused on a particular -piece of the program. You specify a regular expression. Any portion -of the call-graph that is on a path which contains at least one node -matching the regular expression is preserved. The rest of the -call-graph is dropped on the floor. For example, you can focus on the -vsnprintf() libc call in profiler2_unittest -as follows:

- -
-% pprof --gv --focus=vsnprintf /tmp/profiler2_unittest test.prof
-
- -
- -
-
- -

Similarly, you can supply the --ignore option to -ignore samples that match a specified regular expression. E.g., if -you are interested in everything except calls to -snprintf(), you can say:

-
-% pprof --gv --ignore=snprintf /tmp/profiler2_unittest test.prof
-
- - -

Interactive mode

- -

By default -- if you don't specify any flags to the contrary -- -pprof runs in interactive mode. At the (pprof) prompt, -you can run many of the commands described above. You can type -help for a list of what commands are available in -interactive mode.

- -

pprof Options

- -For a complete list of pprof options, you can run pprof ---help. - -

Output Type

- -

-

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--text - Produces a textual listing. (Note: If you have an X display, and - dot and gv installed, you will probably - be happier with the --gv output.) -
--gv - Generates annotated call-graph, converts to postscript, and - displays via gv (requres dot and gv be - installed). -
--dot - Generates the annotated call-graph in dot format and - emits to stdout (requres dot be installed). -
--ps - Generates the annotated call-graph in Postscript format and - emits to stdout (requres dot be installed). -
--pdf - Generates the annotated call-graph in PDF format and emits to - stdout (requires dot and ps2pdf be - installed). -
--gif - Generates the annotated call-graph in GIF format and - emits to stdout (requres dot be installed). -
--list=<regexp> -

Outputs source-code listing of routines whose - name matches <regexp>. Each line - in the listing is annotated with flat and cumulative - sample counts.

- -

In the presence of inlined calls, the samples - associated with inlined code tend to get assigned - to a line that follows the location of the - inlined call. A more precise accounting can be - obtained by disassembling the routine using the - --disasm flag.

-
--disasm=<regexp> - Generates disassembly of routines that match - <regexp>, annotated with flat and - cumulative sample counts and emits to stdout. -
-
- -

Reporting Granularity

- -

By default, pprof produces one entry per procedure. However you can -use one of the following options to change the granularity of the -output. The --files option seems to be particularly -useless, and may be removed eventually.

- -
- - - - - - - - - - - - - - -
--addresses - Produce one node per program address. -
--lines - Produce one node per source line. -
--functions - Produce one node per function (this is the default). -
--files - Produce one node per source file. -
-
- -

Controlling the Call Graph Display

- -

Some nodes and edges are dropped to reduce clutter in the output -display. The following options control this effect:

- -
- - - - - - - - - - - - - - - - - - - - - -
--nodecount=<n> - This option controls the number of displayed nodes. The nodes - are first sorted by decreasing cumulative count, and then only - the top N nodes are kept. The default value is 80. -
--nodefraction=<f> - This option provides another mechanism for discarding nodes - from the display. If the cumulative count for a node is - less than this option's value multiplied by the total count - for the profile, the node is dropped. The default value - is 0.005; i.e. nodes that account for less than - half a percent of the total time are dropped. A node - is dropped if either this condition is satisfied, or the - --nodecount condition is satisfied. -
--edgefraction=<f> - This option controls the number of displayed edges. First of all, - an edge is dropped if either its source or destination node is - dropped. Otherwise, the edge is dropped if the sample - count along the edge is less than this option's value multiplied - by the total count for the profile. The default value is - 0.001; i.e., edges that account for less than - 0.1% of the total time are dropped. -
--focus=<re> - This option controls what region of the graph is displayed - based on the regular expression supplied with the option. - For any path in the callgraph, we check all nodes in the path - against the supplied regular expression. If none of the nodes - match, the path is dropped from the output. -
--ignore=<re> - This option controls what region of the graph is displayed - based on the regular expression supplied with the option. - For any path in the callgraph, we check all nodes in the path - against the supplied regular expression. If any of the nodes - match, the path is dropped from the output. -
-
- -

The dropped edges and nodes account for some count mismatches in -the display. For example, the cumulative count for -snprintf() in the first diagram above was 41. However -the local count (1) and the count along the outgoing edges (12+1+20+6) -add up to only 40.

- - -

Caveats

- -
    -
  • If the program exits because of a signal, the generated profile - will be incomplete, and may perhaps be - completely empty. -
  • The displayed graph may have disconnected regions because - of the edge-dropping heuristics described above. -
  • If the program linked in a library that was not compiled - with enough symbolic information, all samples associated - with the library may be charged to the last symbol found - in the program before the library. This will artificially - inflate the count for that symbol. -
  • If you run the program on one machine, and profile it on - another, and the shared libraries are different on the two - machines, the profiling output may be confusing: samples that - fall within shared libaries may be assigned to arbitrary - procedures. -
  • If your program forks, the children will also be profiled - (since they inherit the same CPUPROFILE setting). Each process - is profiled separately; to distinguish the child profiles from - the parent profile and from each other, all children will have - their process-id appended to the CPUPROFILE name. -
  • Due to a hack we make to work around a possible gcc bug, your - profiles may end up named strangely if the first character of - your CPUPROFILE variable has ascii value greater than 127. - This should be exceedingly rare, but if you need to use such a - name, just set prepend ./ to your filename: - CPUPROFILE=./Ägypten. -
- - -
-
Sanjay Ghemawat
- - -Last modified: Fri May 9 14:41:29 PDT 2008 - -
- - diff --git a/tpl/gperftools/doc/designstyle.css b/tpl/gperftools/doc/designstyle.css deleted file mode 100644 index 29299af..0000000 --- a/tpl/gperftools/doc/designstyle.css +++ /dev/null @@ -1,109 +0,0 @@ -body { - background-color: #ffffff; - color: black; - margin-right: 1in; - margin-left: 1in; -} - - -h1, h2, h3, h4, h5, h6 { - color: #3366ff; - font-family: sans-serif; -} -@media print { - /* Darker version for printing */ - h1, h2, h3, h4, h5, h6 { - color: #000080; - font-family: helvetica, sans-serif; - } -} - -h1 { - text-align: center; - font-size: 18pt; -} -h2 { - margin-left: -0.5in; -} -h3 { - margin-left: -0.25in; -} -h4 { - margin-left: -0.125in; -} -hr { - margin-left: -1in; -} - -/* Definition lists: definition term bold */ -dt { - font-weight: bold; -} - -address { - text-align: right; -} -/* Use the tag for bits of code and for variables and objects. */ -code,pre,samp,var { - color: #006000; -} -/* Use the tag for file and directory paths and names. */ -file { - color: #905050; - font-family: monospace; -} -/* Use the tag for stuff the user should type. */ -kbd { - color: #600000; -} -div.note p { - float: right; - width: 3in; - margin-right: 0%; - padding: 1px; - border: 2px solid #6060a0; - background-color: #fffff0; -} - -UL.nobullets { - list-style-type: none; - list-style-image: none; - margin-left: -1em; -} - -/* pretty printing styles. See prettify.js */ -.str { color: #080; } -.kwd { color: #008; } -.com { color: #800; } -.typ { color: #606; } -.lit { color: #066; } -.pun { color: #660; } -.pln { color: #000; } -.tag { color: #008; } -.atn { color: #606; } -.atv { color: #080; } -pre.prettyprint { padding: 2px; border: 1px solid #888; } - -.embsrc { background: #eee; } - -@media print { - .str { color: #060; } - .kwd { color: #006; font-weight: bold; } - .com { color: #600; font-style: italic; } - .typ { color: #404; font-weight: bold; } - .lit { color: #044; } - .pun { color: #440; } - .pln { color: #000; } - .tag { color: #006; font-weight: bold; } - .atn { color: #404; } - .atv { color: #060; } -} - -/* Table Column Headers */ -.hdr { - color: #006; - font-weight: bold; - background-color: #dddddd; } -.hdr2 { - color: #006; - background-color: #eeeeee; } \ No newline at end of file diff --git a/tpl/gperftools/doc/heap-example1.png b/tpl/gperftools/doc/heap-example1.png deleted file mode 100644 index 9a14b6fb89e66316368656ae26f2250a25760951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37619 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 diff --git a/tpl/gperftools/doc/heap_checker.html b/tpl/gperftools/doc/heap_checker.html deleted file mode 100644 index ea2ade6..0000000 --- a/tpl/gperftools/doc/heap_checker.html +++ /dev/null @@ -1,534 +0,0 @@ - - - - - - Gperftools Heap Leak Checker - - - - -

- Last modified - -

- -

This is the heap checker we use at Google to detect memory leaks in -C++ programs. There are three parts to using it: linking the library -into an application, running the code, and analyzing the output.

- - -

Linking in the Library

- -

The heap-checker is part of tcmalloc, so to install the heap -checker into your executable, add -ltcmalloc to the -link-time step for your executable. Also, while we don't necessarily -recommend this form of usage, it's possible to add in the profiler at -run-time using LD_PRELOAD:

-
% env LD_PRELOAD="/usr/lib/libtcmalloc.so" 
- -

This does not turn on heap checking; it just inserts the -code. For that reason, it's practical to just always link --ltcmalloc into a binary while developing; that's what we -do at Google. (However, since any user can turn on the profiler by -setting an environment variable, it's not necessarily recommended to -install heapchecker-linked binaries into a production, running -system.) Note that if you wish to use the heap checker, you must -also use the tcmalloc memory-allocation library. There is no way -currently to use the heap checker separate from tcmalloc.

- - -

Running the Code

- -

Note: For security reasons, heap profiling will not write to a file --- and is thus not usable -- for setuid programs.

- -

Whole-program Heap Leak Checking

- -

The recommended way to use the heap checker is in "whole program" -mode. In this case, the heap-checker starts tracking memory -allocations before the start of main(), and checks again -at program-exit. If it finds any memory leaks -- that is, any memory -not pointed to by objects that are still "live" at program-exit -- it -aborts the program (via exit(1)) and prints a message -describing how to track down the memory leak (using pprof).

- -

The heap-checker records the stack trace for each allocation while -it is active. This causes a significant increase in memory usage, in -addition to slowing your program down.

- -

Here's how to run a program with whole-program heap checking:

- -
    -
  1. Define the environment variable HEAPCHECK to the type of heap-checking to do. For instance, - to heap-check - /usr/local/bin/my_binary_compiled_with_tcmalloc:

    -
    % env HEAPCHECK=normal /usr/local/bin/my_binary_compiled_with_tcmalloc
    -
- -

No other action is required.

- -

Note that since the heap-checker uses the heap-profiling framework -internally, it is not possible to run both the heap-checker and heap profiler at the same time.

- - -

Flavors of Heap Checking

- -

These are the legal values when running a whole-program heap -check:

-
    -
  1. minimal -
  2. normal -
  3. strict -
  4. draconian -
- -

"Minimal" heap-checking starts as late as possible in a -initialization, meaning you can leak some memory in your -initialization routines (that run before main(), say), -and not trigger a leak message. If you frequently (and purposefully) -leak data in one-time global initializers, "minimal" mode is useful -for you. Otherwise, you should avoid it for stricter modes.

- -

"Normal" heap-checking tracks live objects and -reports a leak for any data that is not reachable via a live object -when the program exits.

- -

"Strict" heap-checking is much like "normal" but has a few extra -checks that memory isn't lost in global destructors. In particular, -if you have a global variable that allocates memory during program -execution, and then "forgets" about the memory in the global -destructor (say, by setting the pointer to it to NULL) without freeing -it, that will prompt a leak message in "strict" mode, though not in -"normal" mode.

- -

"Draconian" heap-checking is appropriate for those who like to be -very precise about their memory management, and want the heap-checker -to help them enforce it. In "draconian" mode, the heap-checker does -not do "live object" checking at all, so it reports a leak unless -all allocated memory is freed before program exit. (However, -you can use IgnoreObject() to re-enable -liveness-checking on an object-by-object basis.)

- -

"Normal" mode, as the name implies, is the one used most often at -Google. It's appropriate for everyday heap-checking use.

- -

In addition, there are two other possible modes:

-
    -
  • as-is -
  • local -
-

as-is is the most flexible mode; it allows you to -specify the various knobs of the heap checker -explicitly. local activates the explicit heap-check instrumentation, but does not -turn on any whole-program leak checking.

- - -

Tweaking whole-program checking

- -

In some cases you want to check the whole program for memory leaks, -but waiting for after main() exits to do the first -whole-program leak check is waiting too long: e.g. in a long-running -server one might wish to simply periodically check for leaks while the -server is running. In this case, you can call the static method -NoGlobalLeaks(), to verify no global leaks have happened -as of that point in the program.

- -

Alternately, doing the check after main() exits might -be too late. Perhaps you have some objects that are known not to -clean up properly at exit. You'd like to do the "at exit" check -before those objects are destroyed (since while they're live, any -memory they point to will not be considered a leak). In that case, -you can call NoGlobalLeaks() manually, near the end of -main(), and then call CancelGlobalCheck() to -turn off the automatic post-main() check.

- -

Finally, there's a helper macro for "strict" and "draconian" modes, -which require all global memory to be freed before program exit. This -freeing can be time-consuming and is often unnecessary, since libc -cleans up all memory at program-exit for you. If you want the -benefits of "strict"/"draconian" modes without the cost of all that -freeing, look at REGISTER_HEAPCHECK_CLEANUP (in -heap-checker.h). This macro allows you to mark specific -cleanup code as active only when the heap-checker is turned on.

- - -

Explicit (Partial-program) Heap Leak Checking

- -

Instead of whole-program checking, you can check certain parts of your -code to verify they do not have memory leaks. This check verifies that -between two parts of a program, no memory is allocated without being freed.

-

To use this kind of checking code, bracket the code you want -checked by creating a HeapLeakChecker object at the -beginning of the code segment, and call -NoLeaks() at the end. These functions, and all others -referred to in this file, are declared in -<gperftools/heap-checker.h>. -

- -

Here's an example:

-
-  HeapLeakChecker heap_checker("test_foo");
-  {
-    code that exercises some foo functionality;
-    this code should not leak memory;
-  }
-  if (!heap_checker.NoLeaks()) assert(NULL == "heap memory leak");
-
- -

Note that adding in the HeapLeakChecker object merely -instruments the code for leak-checking. To actually turn on this -leak-checking on a particular run of the executable, you must still -run with the heap-checker turned on:

-
% env HEAPCHECK=local /usr/local/bin/my_binary_compiled_with_tcmalloc
-

If you want to do whole-program leak checking in addition to this -manual leak checking, you can run in normal or some other -mode instead: they'll run the "local" checks in addition to the -whole-program check.

- - -

Disabling Heap-checking of Known Leaks

- -

Sometimes your code has leaks that you know about and are willing -to accept. You would like the heap checker to ignore them when -checking your program. You can do this by bracketing the code in -question with an appropriate heap-checking construct:

-
-   ...
-   {
-     HeapLeakChecker::Disabler disabler;
-     <leaky code>
-   }
-   ...
-
-Any objects allocated by leaky code (including inside any -routines called by leaky code) and any objects reachable -from such objects are not reported as leaks. - -

Alternately, you can use IgnoreObject(), which takes a -pointer to an object to ignore. That memory, and everything reachable -from it (by following pointers), is ignored for the purposes of leak -checking. You can call UnIgnoreObject() to undo the -effects of IgnoreObject().

- - -

Tuning the Heap Checker

- -

The heap leak checker has many options, some that trade off running -time and accuracy, and others that increase the sensitivity at the -risk of returning false positives. For most uses, the range covered -by the heap-check flavors is enough, but in -specialized cases more control can be helpful.

- -

-These options are specified via environment varaiables. -

- -

This first set of options controls sensitivity and accuracy. These -options are ignored unless you run the heap checker in as-is mode. - - - - - - - - - - - - - - - - - - - - - -
HEAP_CHECK_AFTER_DESTRUCTORSDefault: false - When true, do the final leak check after all other global - destructors have run. When false, do it after all - REGISTER_HEAPCHECK_CLEANUP, typically much earlier in - the global-destructor process. -
HEAP_CHECK_IGNORE_THREAD_LIVEDefault: true - If true, ignore objects reachable from thread stacks and registers - (that is, do not report them as leaks). -
HEAP_CHECK_IGNORE_GLOBAL_LIVEDefault: true - If true, ignore objects reachable from global variables and data - (that is, do not report them as leaks). -
- -

These options modify the behavior of whole-program leak -checking.

- - - - - - - - - - -
HEAP_CHECK_MAX_LEAKSDefault: 20 - The maximum number of leaks to be printed to stderr (all leaks are still - emitted to file output for pprof to visualize). If negative or zero, - print all the leaks found. -
- -

These options apply to all types of leak checking.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HEAP_CHECK_IDENTIFY_LEAKSDefault: false - If true, generate the addresses of the leaked objects in the - generated memory leak profile files. -
HEAP_CHECK_TEST_POINTER_ALIGNMENTDefault: false - If true, check all leaks to see if they might be due to the use - of unaligned pointers. -
HEAP_CHECK_POINTER_SOURCE_ALIGNMENTDefault: sizeof(void*) - Alignment at which all pointers in memory are supposed to be located. - Use 1 if any alignment is ok. -
PPROF_PATHDefault: pprof - The location of the pprof executable. -
HEAP_CHECK_DUMP_DIRECTORYDefault: /tmp - Where the heap-profile files are kept while the program is running. -
- - -

Tips for Handling Detected Leaks

- -

What do you do when the heap leak checker detects a memory leak? -First, you should run the reported pprof command; -hopefully, that is enough to track down the location where the leak -occurs.

- -

If the leak is a real leak, you should fix it!

- -

If you are sure that the reported leaks are not dangerous and there -is no good way to fix them, then you can use -HeapLeakChecker::Disabler and/or -HeapLeakChecker::IgnoreObject() to disable heap-checking -for certain parts of the codebase.

- -

In "strict" or "draconian" mode, leaks may be due to incomplete -cleanup in the destructors of global variables. If you don't wish to -augment the cleanup routines, but still want to run in "strict" or -"draconian" mode, consider using REGISTER_HEAPCHECK_CLEANUP.

- -

Hints for Debugging Detected Leaks

- -

Sometimes it can be useful to not only know the exact code that -allocates the leaked objects, but also the addresses of the leaked objects. -Combining this e.g. with additional logging in the program -one can then track which subset of the allocations -made at a certain spot in the code are leaked. -
-To get the addresses of all leaked objects - define the environment variable HEAP_CHECK_IDENTIFY_LEAKS - to be 1. -The object addresses will be reported in the form of addresses -of fake immediate callers of the memory allocation routines. -Note that the performance of doing leak-checking in this mode -can be noticeably worse than the default mode. -

- -

One relatively common class of leaks that don't look real -is the case of multiple initialization. -In such cases the reported leaks are typically things that are -linked from some global objects, -which are initialized and say never modified again. -The non-obvious cause of the leak is frequently the fact that -the initialization code for these objects executes more than once. -
-E.g. if the code of some .cc file is made to be included twice -into the binary, then the constructors for global objects defined in that file -will execute twice thus leaking the things allocated on the first run. -
-Similar problems can occur if object initialization is done more explicitly -e.g. on demand by a slightly buggy code -that does not always ensure only-once initialization. -

- -

-A more rare but even more puzzling problem can be use of not properly -aligned pointers (maybe inside of not properly aligned objects). -Normally such pointers are not followed by the leak checker, -hence the objects reachable only via such pointers are reported as leaks. -If you suspect this case - define the environment variable HEAP_CHECK_TEST_POINTER_ALIGNMENT - to be 1 -and then look closely at the generated leak report messages. -

- -

How It Works

- -

When a HeapLeakChecker object is constructed, it dumps -a memory-usage profile named -<prefix>.<name>-beg.heap to a temporary -directory. When NoLeaks() -is called (for whole-program checking, this happens automatically at -program-exit), it dumps another profile, named -<prefix>.<name>-end.heap. -(<prefix> is typically determined automatically, -and <name> is typically argv[0].) It -then compares the two profiles. If the second profile shows -more memory use than the first, the -NoLeaks() function will -return false. For "whole program" profiling, this will cause the -executable to abort (via exit(1)). In all cases, it will -print a message on how to process the dumped profiles to locate -leaks.

- -

Detecting Live Objects

- -

At any point during a program's execution, all memory that is -accessible at that time is considered "live." This includes global -variables, and also any memory that is reachable by following pointers -from a global variable. It also includes all memory reachable from -the current stack frame and from current CPU registers (this captures -local variables). Finally, it includes the thread equivalents of -these: thread-local storage and thread heaps, memory reachable from -thread-local storage and thread heaps, and memory reachable from -thread CPU registers.

- -

In all modes except "draconian," live memory is not -considered to be a leak. We detect this by doing a liveness flood, -traversing pointers to heap objects starting from some initial memory -regions we know to potentially contain live pointer data. Note that -this flood might potentially not find some (global) live data region -to start the flood from. If you find such, please file a bug.

- -

The liveness flood attempts to treat any properly aligned byte -sequences as pointers to heap objects and thinks that it found a good -pointer whenever the current heap memory map contains an object with -the address whose byte representation we found. Some pointers into -not-at-start of object will also work here.

- -

As a result of this simple approach, it's possible (though -unlikely) for the flood to be inexact and occasionally result in -leaked objects being erroneously determined to be live. For instance, -random bit patterns can happen to look like pointers to leaked heap -objects. More likely, stale pointer data not corresponding to any -live program variables can be still present in memory regions, -especially in thread stacks. For instance, depending on how the local -malloc is implemented, it may reuse a heap object -address:

-
-    char* p = new char[1];   // new might return 0x80000000, say.
-    delete p;
-    new char[1];             // new might return 0x80000000 again
-    // This last new is a leak, but doesn't seem it: p looks like it points to it
-
- -

In other words, imprecisions in the liveness flood mean that for -any heap leak check we might miss some memory leaks. This means that -for local leak checks, we might report a memory leak in the local -area, even though the leak actually happened before the -HeapLeakChecker object was constructed. Note that for -whole-program checks, a leak report does always correspond to a -real leak (since there's no "before" to have created a false-live -object).

- -

While this liveness flood approach is not very portable and not -100% accurate, it works in most cases and saves us from writing a lot -of explicit clean up code and other hassles when dealing with thread -data.

- - -

Visualizing Leak with pprof

- -

-The heap checker automatically prints basic leak info with stack traces of -leaked objects' allocation sites, as well as a pprof command line that can be -used to visualize the call-graph involved in these allocations. -The latter can be much more useful for a human -to see where/why the leaks happened, especially if the leaks are numerous. -

- -

Leak-checking and Threads

- -

At the time of HeapLeakChecker's construction and during -NoLeaks() calls, we grab a lock -and then pause all other threads so other threads do not interfere -with recording or analyzing the state of the heap.

- -

In general, leak checking works correctly in the presence of -threads. However, thread stack data liveness determination (via -base/thread_lister.h) does not work when the program is -running under GDB, because the ptrace functionality needed for finding -threads is already hooked to by GDB. Conversely, leak checker's -ptrace attempts might also interfere with GDB. As a result, GDB can -result in potentially false leak reports. For this reason, the -heap-checker turns itself off when running under GDB.

- -

Also, thread_lister only works for Linux pthreads; -leak checking is unlikely to handle other thread implementations -correctly.

- -

As mentioned in the discussion of liveness flooding, thread-stack -liveness determination might mis-classify as reachable objects that -very recently became unreachable (leaked). This can happen when the -pointers to now-logically-unreachable objects are present in the -active thread stack frame. In other words, trivial code like the -following might not produce the expected leak checking outcome -depending on how the compiled code works with the stack:

-
-  int* foo = new int [20];
-  HeapLeakChecker check("a_check");
-  foo = NULL;
-  // May fail to trigger.
-  if (!heap_checker.NoLeaks()) assert(NULL == "heap memory leak");
-
- - -
-
Maxim Lifantsev
- - -Last modified: Fri Jul 13 13:14:33 PDT 2007 - -
- - diff --git a/tpl/gperftools/doc/heapprofile.html b/tpl/gperftools/doc/heapprofile.html deleted file mode 100644 index 3986a25..0000000 --- a/tpl/gperftools/doc/heapprofile.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - - Gperftools Heap Profiler - - - - -

- Last modified - -

- -

This is the heap profiler we use at Google, to explore how C++ -programs manage memory. This facility can be useful for

-
    -
  • Figuring out what is in the program heap at any given time -
  • Locating memory leaks -
  • Finding places that do a lot of allocation -
- -

The profiling system instruments all allocations and frees. It -keeps track of various pieces of information per allocation site. An -allocation site is defined as the active stack trace at the call to -malloc, calloc, realloc, or, -new.

- -

There are three parts to using it: linking the library into an -application, running the code, and analyzing the output.

- - -

Linking in the Library

- -

To install the heap profiler into your executable, add --ltcmalloc to the link-time step for your executable. -Also, while we don't necessarily recommend this form of usage, it's -possible to add in the profiler at run-time using -LD_PRELOAD: -

% env LD_PRELOAD="/usr/lib/libtcmalloc.so" <binary>
- -

This does not turn on heap profiling; it just inserts the -code. For that reason, it's practical to just always link --ltcmalloc into a binary while developing; that's what we -do at Google. (However, since any user can turn on the profiler by -setting an environment variable, it's not necessarily recommended to -install profiler-linked binaries into a production, running -system.) Note that if you wish to use the heap profiler, you must -also use the tcmalloc memory-allocation library. There is no way -currently to use the heap profiler separate from tcmalloc.

- - -

Running the Code

- -

There are several alternatives to actually turn on heap profiling -for a given run of an executable:

- -
    -
  1. Define the environment variable HEAPPROFILE to the filename - to dump the profile to. For instance, to profile - /usr/local/bin/my_binary_compiled_with_tcmalloc:

    -
    % env HEAPPROFILE=/tmp/mybin.hprof /usr/local/bin/my_binary_compiled_with_tcmalloc
    -
  2. In your code, bracket the code you want profiled in calls to - HeapProfilerStart() and HeapProfilerStop(). - (These functions are declared in <gperftools/heap-profiler.h>.) - HeapProfilerStart() will take the - profile-filename-prefix as an argument. Then, as often as - you'd like before calling HeapProfilerStop(), you - can use HeapProfilerDump() or - GetHeapProfile() to examine the profile. In case - it's useful, IsHeapProfilerRunning() will tell you - whether you've already called HeapProfilerStart() or not.

    -
- - -

For security reasons, heap profiling will not write to a file -- -and is thus not usable -- for setuid programs.

- -

Modifying Runtime Behavior

- -

You can more finely control the behavior of the heap profiler via -environment variables.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HEAP_PROFILE_ALLOCATION_INTERVALdefault: 1073741824 (1 Gb) - Dump heap profiling information each time the specified number of - bytes has been allocated by the program. -
HEAP_PROFILE_INUSE_INTERVALdefault: 104857600 (100 Mb) - Dump heap profiling information whenever the high-water memory - usage mark increases by the specified number of bytes. -
HEAP_PROFILE_TIME_INTERVALdefault: 0 - Dump heap profiling information each time the specified - number of seconds has elapsed. -
HEAP_PROFILE_MMAPdefault: false - Profile mmap, mremap and sbrk - calls in addition - to malloc, calloc, realloc, - and new. NOTE: this causes the profiler to - profile calls internal to tcmalloc, since tcmalloc and friends use - mmap and sbrk internally for allocations. One partial solution is - to filter these allocations out when running pprof, - with something like - pprof --ignore='DoAllocWithArena|SbrkSysAllocator::Alloc|MmapSysAllocator::Alloc. -
HEAP_PROFILE_ONLY_MMAPdefault: false - Only profile mmap, mremap, and sbrk - calls; do not profile - malloc, calloc, realloc, - or new. -
HEAP_PROFILE_MMAP_LOGdefault: false - Log mmap/munmap calls. -
- -

Checking for Leaks

- -

You can use the heap profiler to manually check for leaks, for -instance by reading the profiler output and looking for large -allocations. However, for that task, it's easier to use the automatic heap-checking facility built -into tcmalloc.

- - -

Analyzing the Output

- -

If heap-profiling is turned on in a program, the program will -periodically write profiles to the filesystem. The sequence of -profiles will be named:

-
-           <prefix>.0000.heap
-           <prefix>.0001.heap
-           <prefix>.0002.heap
-           ...
-
-

where <prefix> is the filename-prefix supplied -when running the code (e.g. via the HEAPPROFILE -environment variable). Note that if the supplied prefix -does not start with a /, the profile files will be -written to the program's working directory.

- -

The profile output can be viewed by passing it to the -pprof tool -- the same tool that's used to analyze CPU profiles. - -

Here are some examples. These examples assume the binary is named -gfs_master, and a sequence of heap profile files can be -found in files named:

-
-  /tmp/profile.0001.heap
-  /tmp/profile.0002.heap
-  ...
-  /tmp/profile.0100.heap
-
- -

Why is a process so big

- -
-    % pprof --gv gfs_master /tmp/profile.0100.heap
-
- -

This command will pop-up a gv window that displays -the profile information as a directed graph. Here is a portion -of the resulting output:

- -

- -

- -A few explanations: -
    -
  • GFS_MasterChunk::AddServer accounts for 255.6 MB - of the live memory, which is 25% of the total live memory. -
  • GFS_MasterChunkTable::UpdateState is directly - accountable for 176.2 MB of the live memory (i.e., it directly - allocated 176.2 MB that has not been freed yet). Furthermore, - it and its callees are responsible for 729.9 MB. The - labels on the outgoing edges give a good indication of the - amount allocated by each callee. -
- -

Comparing Profiles

- -

You often want to skip allocations during the initialization phase -of a program so you can find gradual memory leaks. One simple way to -do this is to compare two profiles -- both collected after the program -has been running for a while. Specify the name of the first profile -using the --base option. For example:

-
-   % pprof --base=/tmp/profile.0004.heap gfs_master /tmp/profile.0100.heap
-
- -

The memory-usage in /tmp/profile.0004.heap will be -subtracted from the memory-usage in -/tmp/profile.0100.heap and the result will be -displayed.

- -

Text display

- -
-% pprof --text gfs_master /tmp/profile.0100.heap
-   255.6  24.7%  24.7%    255.6  24.7% GFS_MasterChunk::AddServer
-   184.6  17.8%  42.5%    298.8  28.8% GFS_MasterChunkTable::Create
-   176.2  17.0%  59.5%    729.9  70.5% GFS_MasterChunkTable::UpdateState
-   169.8  16.4%  75.9%    169.8  16.4% PendingClone::PendingClone
-    76.3   7.4%  83.3%     76.3   7.4% __default_alloc_template::_S_chunk_alloc
-    49.5   4.8%  88.0%     49.5   4.8% hashtable::resize
-   ...
-
- -

-

    -
  • The first column contains the direct memory use in MB. -
  • The fourth column contains memory use by the procedure - and all of its callees. -
  • The second and fifth columns are just percentage - representations of the numbers in the first and fourth columns. -
  • The third column is a cumulative sum of the second column - (i.e., the kth entry in the third column is the - sum of the first k entries in the second column.) -
- -

Ignoring or focusing on specific regions

- -

The following command will give a graphical display of a subset of -the call-graph. Only paths in the call-graph that match the regular -expression DataBuffer are included:

-
-% pprof --gv --focus=DataBuffer gfs_master /tmp/profile.0100.heap
-
- -

Similarly, the following command will omit all paths subset of the -call-graph. All paths in the call-graph that match the regular -expression DataBuffer are discarded:

-
-% pprof --gv --ignore=DataBuffer gfs_master /tmp/profile.0100.heap
-
- -

Total allocations + object-level information

- -

All of the previous examples have displayed the amount of in-use -space. I.e., the number of bytes that have been allocated but not -freed. You can also get other types of information by supplying a -flag to pprof:

- -
- - - - - - - - - - - - - - - - - - - - - -
--inuse_space - Display the number of in-use megabytes (i.e. space that has - been allocated but not freed). This is the default. -
--inuse_objects - Display the number of in-use objects (i.e. number of - objects that have been allocated but not freed). -
--alloc_space - Display the number of allocated megabytes. This includes - the space that has since been de-allocated. Use this - if you want to find the main allocation sites in the - program. -
--alloc_objects - Display the number of allocated objects. This includes - the objects that have since been de-allocated. Use this - if you want to find the main allocation sites in the - program. -
-
- - -

Interactive mode

- -

By default -- if you don't specify any flags to the contrary -- -pprof runs in interactive mode. At the (pprof) prompt, -you can run many of the commands described above. You can type -help for a list of what commands are available in -interactive mode.

- - -

Caveats

- -
    -
  • Heap profiling requires the use of libtcmalloc. This - requirement may be removed in a future version of the heap - profiler, and the heap profiler separated out into its own - library. - -
  • If the program linked in a library that was not compiled - with enough symbolic information, all samples associated - with the library may be charged to the last symbol found - in the program before the library. This will artificially - inflate the count for that symbol. - -
  • If you run the program on one machine, and profile it on - another, and the shared libraries are different on the two - machines, the profiling output may be confusing: samples that - fall within the shared libaries may be assigned to arbitrary - procedures. - -
  • Several libraries, such as some STL implementations, do their - own memory management. This may cause strange profiling - results. We have code in libtcmalloc to cause STL to use - tcmalloc for memory management (which in our tests is better - than STL's internal management), though it only works for some - STL implementations. - -
  • If your program forks, the children will also be profiled - (since they inherit the same HEAPPROFILE setting). Each - process is profiled separately; to distinguish the child - profiles from the parent profile and from each other, all - children will have their process-id attached to the HEAPPROFILE - name. - -
  • Due to a hack we make to work around a possible gcc bug, your - profiles may end up named strangely if the first character of - your HEAPPROFILE variable has ascii value greater than 127. - This should be exceedingly rare, but if you need to use such a - name, just set prepend ./ to your filename: - HEAPPROFILE=./Ägypten. -
- -
-
Sanjay Ghemawat - -
- - diff --git a/tpl/gperftools/doc/index.html b/tpl/gperftools/doc/index.html deleted file mode 100644 index 7b93ed3..0000000 --- a/tpl/gperftools/doc/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - -Gperftools - - - - - -
-Last modified: Thu Feb 2 14:40:47 PST 2012 - - - - diff --git a/tpl/gperftools/doc/overview.dot b/tpl/gperftools/doc/overview.dot deleted file mode 100644 index 9966f56..0000000 --- a/tpl/gperftools/doc/overview.dot +++ /dev/null @@ -1,15 +0,0 @@ -digraph Overview { -node [shape = box] - -{rank=same -T1 [label="Thread Cache"] -Tsep [label="...", shape=plaintext] -Tn [label="Thread Cache"] -T1 -> Tsep -> Tn [style=invis] -} - -C [label="Central\nHeap"] -T1 -> C [dir=both] -Tn -> C [dir=both] - -} diff --git a/tpl/gperftools/doc/overview.gif b/tpl/gperftools/doc/overview.gif deleted file mode 100644 index 43828dadec8b7ceb5e85e863fb35d78f172d5f46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6472 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 O0 -> O1 -> sep1 -heap:f1 -> O2 -> O3 -> sep2 -heap:f2 -> O4 -> O5 -> sep3 -heap:f255 -> O6 -> O7 -> sep4 -heap:frest -> O8 -> O9 -> sep5 - -} diff --git a/tpl/gperftools/doc/pageheap.gif b/tpl/gperftools/doc/pageheap.gif deleted file mode 100644 index 66329812ae1d4c3a0d3e930f0d04c26891ec4b48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15486 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 diff --git a/tpl/gperftools/doc/pprof-test-big.gif b/tpl/gperftools/doc/pprof-test-big.gif deleted file mode 100644 index 67a1240fc10321edacdf8313197120cdddbd07ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111566 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

c0)to@d<|m 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 Q@q6wc82N^=NMPxCx_;{X5v diff --git a/tpl/gperftools/doc/pprof-test.gif b/tpl/gperftools/doc/pprof-test.gif deleted file mode 100644 index 9eeab8ad2305f48acdd8a1c94e52f47240a3237c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56995 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 AzW@LL diff --git a/tpl/gperftools/doc/pprof-vsnprintf-big.gif b/tpl/gperftools/doc/pprof-vsnprintf-big.gif deleted file mode 100644 index 2ab292abac5594335f9931dacbe020adedf8de30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100721 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%0A+pPT|Fj zKvA`FOBI(-S91)1X!Wlh3?^WC=e|C84botYy2>T*@Z!g;L2ObIVL{@|zR>@Z#l7EU zil-ixp_IxS%GipxnCURmI@%BUj;R=xdj`c|)dL2I9c^aRSv_U$D@4^v3+S`eZOo-W zq3iRzND37z4>@FSz@km-$NMEy0C05DL0an=9!_0qH#rCVauuaeLRfBZ)RCkkpBx$> zS)-6ST}$AGMJfFSp%d)C0NTddA3loDRiYu8^fTLTJ59A?t1bVTpY;L$12CB>-E9m-@x?JQ;e8wJC^JTWjuG@l239qGkEw*|5gsDf4i zO0m1m)6h!rq{0-iqksWhtVe}_ISoYWXa|Us|7s5g?CAyg#QG`l7#TV1@GS8EZCaJA zjS>pOnH4>~K~6<8bQUR_N!Uf77p6qs^I>$l%O))##)N)?#*`_{?L$m{*KKm@?u2ec z;&){YDitvZP5G3*MuD@SHJ;`X5l<_lDsUY#cDn zxTi2|^sd1GozF0MKBsEAJ%W7By|h}BN}*Y-{st9XnQq`J;&}9~96Ay{^PJIU#Udqe z3E%gzPC2PfSpGlFkds56Ed}9dbls2K=CQDCZ-z#4SK zkqRshalu-;TO`y!p}bR%;lB0jMZVI@Y1#adt~q%fy0>hma>O4A26?s3=an253ZE{Z z&)+i_D45>qf0&vosH&MyT`pU`)#+P7j4=ubkrvQWydLfS#Apq6GXd{R1AfomfJcnc z2NwG6T0HfO<-Sj<>-&-LAYTDW6KrYcQTUCq_5|<#e6u=wRsP{OiZW7&j^iWolUm1X1RB}Mut*ry z_N#~nh#6bo`;`{$St50eV!d{!VdGwOjN=;=4?6~r*QC4T*&i@VNF|y}VaLQv{6hVs zh(3R3YBAmGtYf{g#%+;S|6@z!Ed=t}?D^yhLZ{80xYBy+EyaN#J$z5Ps!Ma%0Udc@ z3C1!?upzQa3}vPs!&-U6aClvOzvfJ|8XxO6Je$pXIegmY$68jHg$%;jeS+g4^=Es& z7+B8hEF#h@U)L-cF@ftOo!=9i2~fOi&E&CKvL8$R5w99Z@9aNd zWZ80@8oEwWEEE>_aGM;FuNIht(FrbNZ?4~?BGi{LL0@7f`$QbCOZ39!7ytO9(Ya%R zDZkc6&BUgcKA~3@XR{(n+OIwT$T#qQKYRY6c~6gd)X#rBLEQ3R(ocUCKb1Cntj+o6 z_3$r*hV@arm0=wJjokGsG#Z8R_)Dm7>j`L>1bZGq-ry9RMx;dPh(o(weSKpaG zqkZv%=l?CRu)BKQh3~%lVDJ6kzW7D*t(ck>J_4Jj-@BZ@-PLKX! z^AFw&r)n~DOtXsS%`ad2x2wOo@q2-7p^mO&Cw~6Q**D+0{JY=(yCQAUl-${K?_RKE z+1Km7^~m>Ie$>9NyQlZ$t8ZNR=PMum;m^tGlc&!3o3DIzVMWaY4}Sa6A53lCzH9%X lpFIDIQ$r)~Uj6uw|B-Urm%f~L$DQ{qy7z0N%};Q5{U3`G#lHXm diff --git a/tpl/gperftools/doc/pprof-vsnprintf.gif b/tpl/gperftools/doc/pprof-vsnprintf.gif deleted file mode 100644 index 42a85472caee7aee2846e9dd09800588f4e7d041..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31054 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 diff --git a/tpl/gperftools/doc/pprof.1 b/tpl/gperftools/doc/pprof.1 deleted file mode 100644 index 4662281..0000000 --- a/tpl/gperftools/doc/pprof.1 +++ /dev/null @@ -1,131 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. -.TH PPROF "1" "February 2005" "pprof (part of gperftools)" Google -.SH NAME -pprof \- manual page for pprof (part of gperftools) -.SH SYNOPSIS -.B pprof -[\fIoptions\fR] \fI \fR -.SH DESCRIPTION -.IP -Prints specified cpu- or heap-profile -.SH OPTIONS -.TP -\fB\-\-cum\fR -Sort by cumulative data -.TP -\fB\-\-base=\fR -Subtract from before display -.SS "Reporting Granularity:" -.TP -\fB\-\-addresses\fR -Report at address level -.TP -\fB\-\-lines\fR -Report at source line level -.TP -\fB\-\-functions\fR -Report at function level [default] -.TP -\fB\-\-files\fR -Report at source file level -.SS "Output type:" -.TP -\fB\-\-text\fR -Generate text report [default] -.TP -\fB\-\-gv\fR -Generate Postscript and display -.TP -\fB\-\-list=\fR -Generate source listing of matching routines -.TP -\fB\-\-disasm=\fR -Generate disassembly of matching routines -.TP -\fB\-\-dot\fR -Generate DOT file to stdout -.TP -\fB\-\-ps\fR -Generate Postcript to stdout -.TP -\fB\-\-pdf\fR -Generate PDF to stdout -.TP -\fB\-\-gif\fR -Generate GIF to stdout -.SS "Heap-Profile Options:" -.TP -\fB\-\-inuse_space\fR -Display in-use (mega)bytes [default] -.TP -\fB\-\-inuse_objects\fR -Display in-use objects -.TP -\fB\-\-alloc_space\fR -Display allocated (mega)bytes -.TP -\fB\-\-alloc_objects\fR -Display allocated objects -.TP -\fB\-\-show_bytes\fR -Display space in bytes -.TP -\fB\-\-drop_negative\fR -Ignore negaive differences -.SS "Call-graph Options:" -.TP -\fB\-\-nodecount=\fR -Show at most so many nodes [default=80] -.TP -\fB\-\-nodefraction=\fR -Hide nodes below *total [default=.005] -.TP -\fB\-\-edgefraction=\fR -Hide edges below *total [default=.001] -.TP -\fB\-\-focus=\fR -Focus on nodes matching -.TP -\fB\-\-ignore=\fR -Ignore nodes matching -.TP -\fB\-\-scale=\fR -Set GV scaling [default=0] -.SH EXAMPLES - -pprof /bin/ls ls.prof -.IP -Outputs one line per procedure -.PP -pprof \fB\-\-gv\fR /bin/ls ls.prof -.IP -Displays annotated call-graph via 'gv' -.PP -pprof \fB\-\-gv\fR \fB\-\-focus\fR=\fIMutex\fR /bin/ls ls.prof -.IP -Restricts to code paths including a .*Mutex.* entry -.PP -pprof \fB\-\-gv\fR \fB\-\-focus\fR=\fIMutex\fR \fB\-\-ignore\fR=\fIstring\fR /bin/ls ls.prof -.IP -Code paths including Mutex but not string -.PP -pprof \fB\-\-list\fR=\fIgetdir\fR /bin/ls ls.prof -.IP -Dissassembly (with per-line annotations) for getdir() -.PP -pprof \fB\-\-disasm\fR=\fIgetdir\fR /bin/ls ls.prof -.IP -Dissassembly (with per-PC annotations) for getdir() -.SH COPYRIGHT -Copyright \(co 2005 Google Inc. -.SH "SEE ALSO" -Further documentation for -.B pprof -is maintained as a web page called -.B cpu_profiler.html -and is likely installed at one of the following locations: -.IP -.B /usr/share/gperftools/cpu_profiler.html -.br -.B /usr/local/share/gperftools/cpu_profiler.html -.PP diff --git a/tpl/gperftools/doc/pprof.see_also b/tpl/gperftools/doc/pprof.see_also deleted file mode 100644 index f2caf52..0000000 --- a/tpl/gperftools/doc/pprof.see_also +++ /dev/null @@ -1,11 +0,0 @@ -[see also] -Further documentation for -.B pprof -is maintained as a web page called -.B cpu_profiler.html -and is likely installed at one of the following locations: -.IP -.B /usr/share/gperftools/cpu_profiler.html -.br -.B /usr/local/share/gperftools/cpu_profiler.html -.PP diff --git a/tpl/gperftools/doc/pprof_remote_servers.html b/tpl/gperftools/doc/pprof_remote_servers.html deleted file mode 100644 index e30e612..0000000 --- a/tpl/gperftools/doc/pprof_remote_servers.html +++ /dev/null @@ -1,260 +0,0 @@ - - - -pprof and Remote Servers - - - - -

pprof and Remote Servers

- -

In mid-2006, we added an experimental facility to pprof, the tool that analyzes CPU and -heap profiles. This facility allows you to collect profile -information from running applications. It makes it easy to collect -profile information without having to stop the program first, and -without having to log into the machine where the application is -running. This is meant to be used on webservers, but will work on any -application that can be modified to accept TCP connections on a port -of its choosing, and to respond to HTTP requests on that port.

- -

We do not currently have infrastructure, such as apache modules, -that you can pop into a webserver or other application to get the -necessary functionality "for free." However, it's easy to generate -the necessary data, which should allow the interested developer to add -the necessary support into his or her applications.

- -

To use pprof in this experimental "server" mode, you -give the script a host and port it should query, replacing the normal -commandline arguments of application + profile file:

-
-   % pprof internalweb.mycompany.com:80
-
- -

The host must be listening on that port, and be able to accept HTTP/1.0 -requests -- sent via wget and curl -- for -several urls. The following sections list the urls that -pprof can send, and the responses it expects in -return.

- -

Here are examples that pprof will recognize, when you give them -on the commandline, are urls. In general, you -specify the host and a port (the port-number is required), and put -the service-name at the end of the url.:

-
-http://myhost:80/pprof/heap            # retrieves a heap profile
-http://myhost:8008/pprof/profile       # retrieves a CPU profile
-http://myhost:80                       # retrieves a CPU profile (the default)
-http://myhost:8080/                    # retrieves a CPU profile (the default)
-myhost:8088/pprof/growth               # "http://" is optional, but port is not
-http://myhost:80/myservice/pprof/heap  # /pprof/heap just has to come at the end
-http://myhost:80/pprof/pmuprofile      # CPU profile using performance counters
-
- -

/pprof/heap

- -

pprof asks for the url /pprof/heap to -get heap information. The actual url is controlled via the variable -HEAP_PAGE in the pprof script, so you -can change it if you'd like.

- -

There are two ways to get this data. The first is to call

-
-    MallocExtension::instance()->GetHeapSample(&output);
-
-

and have the server send output back as an HTTP -response to pprof. MallocExtension is -defined in the header file gperftools/malloc_extension.h.

- -

Note this will only only work if the binary is being run with -sampling turned on (which is not the default). To do this, set the -environment variable TCMALLOC_SAMPLE_PARAMETER to a -positive value, such as 524288, before running.

- -

The other way is to call HeapProfileStart(filename) -(from heap-profiler.h), continue to do work, and then, -some number of seconds later, call GetHeapProfile() -(followed by HeapProfilerStop()). The server can send -the output of GetHeapProfile back as the HTTP response to -pprof. (Note you must free() this data after using it.) -This is similar to how profile requests are -handled, below. This technique does not require the application to -run with sampling turned on.

- -

Here's an example of what the output should look like:

-
-heap profile:   1923: 127923432 [  1923: 127923432] @ heap_v2/524288
-     1:      312 [     1:      312] @ 0x2aaaabaf5ccc 0x2aaaaba4cd2c 0x2aaaac08c09a
-   928: 122586016 [   928: 122586016] @ 0x2aaaabaf682c 0x400680 0x400bdd 0x2aaaab1c368a 0x2aaaab1c8f77 0x2aaaab1c0396 0x2aaaab1c86ed 0x4007ff 0x2aaaaca62afa
-     1:       16 [     1:       16] @ 0x2aaaabaf5ccc 0x2aaaabb04bac 0x2aaaabc1b262 0x2aaaabc21496 0x2aaaabc214bb
-[...]
-
- - -

Older code may produce "version 1" heap profiles which look like this:

-

-heap profile:  14933: 791700132 [ 14933: 791700132] @ heap
-     1:   848688 [     1:   848688] @ 0xa4b142 0x7f5bfc 0x87065e 0x4056e9 0x4125f8 0x42b4f1 0x45b1ba 0x463248 0x460871 0x45cb7c 0x5f1744 0x607cee 0x5f4a5e 0x40080f 0x2aaaabad7afa
-     1:  1048576 [     1:  1048576] @ 0xa4a9b2 0x7fd025 0x4ca6d8 0x4ca814 0x4caa88 0x2aaaab104cf0 0x404e20 0x4125f8 0x42b4f1 0x45b1ba 0x463248 0x460871 0x45cb7c 0x5f1744 0x607cee 0x5f4a5e 0x40080f 0x2aaaabad7afa
-  2942: 388629374 [  2942: 388629374] @ 0xa4b142 0x4006a0 0x400bed 0x5f0cfa 0x5f1744 0x607cee 0x5f4a5e 0x40080f 0x2aaaabad7afa
-[...]
-
-

pprof accepts both old and new heap profiles and automatically -detects which one you are using.

- -

/pprof/growth

- -

pprof asks for the url /pprof/growth to -get heap-profiling delta (growth) information. The actual url is -controlled via the variable GROWTH_PAGE in the -pprof script, so you can change it if you'd like.

- -

The server should respond by calling

-
-    MallocExtension::instance()->GetHeapGrowthStacks(&output);
-
-

and sending output back as an HTTP response to -pprof. MallocExtension is defined in the -header file gperftools/malloc_extension.h.

- -

Here's an example, from an actual Google webserver, of what the -output should look like:

-
-heap profile:    741: 812122112 [   741: 812122112] @ growth
-     1:  1572864 [     1:  1572864] @ 0x87da564 0x87db8a3 0x84787a4 0x846e851 0x836d12f 0x834cd1c 0x8349ba5 0x10a3177 0x8349961
-     1:  1048576 [     1:  1048576] @ 0x87d92e8 0x87d9213 0x87d9178 0x87d94d3 0x87da9da 0x8a364ff 0x8a437e7 0x8ab7d23 0x8ab7da9 0x8ac7454 0x8348465 0x10a3161 0x8349961
-[...]
-
- - -

/pprof/profile

- -

pprof asks for the url -/pprof/profile?seconds=XX to get cpu-profiling -information. The actual url is controlled via the variable -PROFILE_PAGE in the pprof script, so you can -change it if you'd like.

- -

The server should respond by calling -ProfilerStart(filename), continuing to do its work, and -then, XX seconds later, calling ProfilerStop(). (These -functions are declared in gperftools/profiler.h.) The -application is responsible for picking a unique filename for -ProfilerStart(). After calling -ProfilerStop(), the server should read the contents of -filename and send them back as an HTTP response to -pprof.

- -

Obviously, to get useful profile information the application must -continue to run in the XX seconds that the profiler is running. Thus, -the profile start-stop calls should be done in a separate thread, or -be otherwise non-blocking.

- -

The profiler output file is binary, but near the end of it, it -should have lines of text somewhat like this:

-
-01016000-01017000 rw-p 00015000 03:01 59314      /lib/ld-2.2.2.so
-
- -

/pprof/pmuprofile

- -pprof asks for a url of the form -/pprof/pmuprofile?event=hw_event:unit_mask&period=nnn&seconds=xxx -to get cpu-profiling information. The actual url is controlled via the variable -PMUPROFILE_PAGE in the pprof script, so you can -change it if you'd like.

- -

-This is similar to pprof, but is meant to be used with your CPU's hardware -performance counters. The server could be implemented on top of a library -such as -libpfm. It should collect a sample every nnn occurrences -of the event and stop the sampling after xxx seconds. Much of the code -for /pprof/profile can be reused for this purpose. -

- -

The server side routines (the equivalent of -ProfilerStart/ProfilerStart) are not available as part of perftools, -so this URL is unlikely to be that useful.

- -

/pprof/contention

- -

This is intended to be able to profile (thread) lock contention in -addition to CPU and memory use. It's not yet usable.

- - -

/pprof/cmdline

- -

pprof asks for the url /pprof/cmdline to -figure out what application it's profiling. The actual url is -controlled via the variable PROGRAM_NAME_PAGE in the -pprof script, so you can change it if you'd like.

- -

The server should respond by reading the contents of -/proc/self/cmdline, converting all internal NUL (\0) -characters to newlines, and sending the result back as an HTTP -response to pprof.

- -

Here's an example return value:

-

-/root/server/custom_webserver
-80
---configfile=/root/server/ws.config
-
- - -

/pprof/symbol

- -

pprof asks for the url /pprof/symbol to -map from hex addresses to variable names. The actual url is -controlled via the variable SYMBOL_PAGE in the -pprof script, so you can change it if you'd like.

- -

When the server receives a GET request for -/pprof/symbol, it should return a line formatted like -so:

-
-   num_symbols: ###
-
-

where ### is the number of symbols found in the -binary. (For now, the only important distinction is whether the value -is 0, which it is for executables that lack debug information, or -not-0).

- -

This is perhaps the hardest request to write code for, because in -addition to the GET request for this url, the server must accept POST -requests. This means that after the HTTP headers, pprof will pass in -a list of hex addresses connected by +, like so:

-
-   curl -d '0x0824d061+0x0824d1cf' http://remote_host:80/pprof/symbol
-
- -

The server should read the POST data, which will be in one line, -and for each hex value, should write one line of output to the output -stream, like so:

-
-<hex address><tab><function name>
-
-

For instance:

-
-0x08b2dabd    _Update
-
- -

The other reason this is the most difficult request to implement, -is that the application will have to figure out for itself how to map -from address to function name. One possibility is to run nm -C --n <program name> to get the mappings at -program-compile-time. Another, at least on Linux, is to call out to -addr2line for every pprof/symbol call, for instance -addr2line -Cfse /proc//exe 0x12345678 0x876543210 -(presumably with some caching!)

- -

pprof itself does just this for local profiles (not -ones that talk to remote servers); look at the subroutine -GetProcedureBoundaries.

- - -
-Last modified: Mon Jun 12 21:30:14 PDT 2006 - - diff --git a/tpl/gperftools/doc/spanmap.dot b/tpl/gperftools/doc/spanmap.dot deleted file mode 100644 index 3cb42ab..0000000 --- a/tpl/gperftools/doc/spanmap.dot +++ /dev/null @@ -1,22 +0,0 @@ -digraph SpanMap { -node [shape=box, width=0.3, height=0.3] -nodesep=.05 - -map [shape=record, width=6, label="||||||||||"] -S0 [label="a"] -S1 [label="b"] -S2 [label="c"] -S3 [label="d"] -map:f0 -> S0 -map:f1 -> S0 -map:f2 -> S1 -map:f3 -> S2 -map:f4 -> S2 -map:f5 -> S2 -map:f6 -> S2 -map:f7 -> S2 -map:f8 -> S3 -map:f9 -> S3 -map:f10 -> S3 - -} diff --git a/tpl/gperftools/doc/spanmap.gif b/tpl/gperftools/doc/spanmap.gif deleted file mode 100644 index a0627f6a71a0e2ad21b3043f9d5fc9047afd5fb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8482 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 diff --git a/tpl/gperftools/doc/t-test1.times.txt b/tpl/gperftools/doc/t-test1.times.txt deleted file mode 100644 index 0163693..0000000 --- a/tpl/gperftools/doc/t-test1.times.txt +++ /dev/null @@ -1,480 +0,0 @@ -time.1.ptmalloc.64:0.56 user 0.02 system 0.57 elapsed 100% CPU -time.1.tcmalloc.64:0.38 user 0.02 system 0.40 elapsed 98% CPU -time.1.ptmalloc.128:0.61 user 0.01 system 0.61 elapsed 101% CPU -time.1.tcmalloc.128:0.35 user 0.00 system 0.35 elapsed 99% CPU -time.1.ptmalloc.256:0.59 user 0.01 system 0.60 elapsed 100% CPU -time.1.tcmalloc.256:0.27 user 0.02 system 0.28 elapsed 102% CPU -time.1.ptmalloc.512:0.57 user 0.00 system 0.57 elapsed 100% CPU -time.1.tcmalloc.512:0.25 user 0.01 system 0.25 elapsed 101% CPU -time.1.ptmalloc.1024:0.52 user 0.00 system 0.52 elapsed 99% CPU -time.1.tcmalloc.1024:0.22 user 0.02 system 0.24 elapsed 97% CPU -time.1.ptmalloc.2048:0.47 user 0.00 system 0.47 elapsed 99% CPU -time.1.tcmalloc.2048:0.22 user 0.02 system 0.25 elapsed 95% CPU -time.1.ptmalloc.4096:0.48 user 0.01 system 0.48 elapsed 100% CPU -time.1.tcmalloc.4096:0.25 user 0.01 system 0.25 elapsed 100% CPU -time.1.ptmalloc.8192:0.49 user 0.02 system 0.49 elapsed 102% CPU -time.1.tcmalloc.8192:0.27 user 0.02 system 0.28 elapsed 101% CPU -time.1.ptmalloc.16384:0.51 user 0.04 system 0.55 elapsed 99% CPU -time.1.tcmalloc.16384:0.35 user 0.02 system 0.37 elapsed 100% CPU -time.1.ptmalloc.32768:0.53 user 0.14 system 0.66 elapsed 100% CPU -time.1.tcmalloc.32768:0.67 user 0.02 system 0.69 elapsed 99% CPU -time.1.ptmalloc.65536:0.68 user 0.31 system 0.98 elapsed 100% CPU -time.1.tcmalloc.65536:0.71 user 0.01 system 0.72 elapsed 99% CPU -time.1.ptmalloc.131072:0.90 user 0.72 system 1.62 elapsed 99% CPU -time.1.tcmalloc.131072:0.94 user 0.03 system 0.97 elapsed 99% CPU -time.2.ptmalloc.64:1.05 user 0.00 system 0.53 elapsed 196% CPU -time.2.tcmalloc.64:0.66 user 0.03 system 0.37 elapsed 185% CPU -time.2.ptmalloc.128:1.77 user 0.01 system 0.89 elapsed 198% CPU -time.2.tcmalloc.128:0.53 user 0.01 system 0.29 elapsed 184% CPU -time.2.ptmalloc.256:1.14 user 0.01 system 0.62 elapsed 182% CPU -time.2.tcmalloc.256:0.45 user 0.02 system 0.26 elapsed 180% CPU -time.2.ptmalloc.512:1.26 user 0.40 system 1.79 elapsed 92% CPU -time.2.tcmalloc.512:0.43 user 0.02 system 0.27 elapsed 166% CPU -time.2.ptmalloc.1024:0.98 user 0.03 system 0.56 elapsed 179% CPU -time.2.tcmalloc.1024:0.44 user 0.02 system 0.34 elapsed 134% CPU -time.2.ptmalloc.2048:0.87 user 0.02 system 0.44 elapsed 199% CPU -time.2.tcmalloc.2048:0.49 user 0.02 system 0.34 elapsed 148% CPU -time.2.ptmalloc.4096:0.92 user 0.03 system 0.48 elapsed 196% CPU -time.2.tcmalloc.4096:0.50 user 0.02 system 0.49 elapsed 105% CPU -time.2.ptmalloc.8192:1.05 user 0.04 system 0.55 elapsed 196% CPU -time.2.tcmalloc.8192:0.59 user 0.01 system 0.51 elapsed 116% CPU -time.2.ptmalloc.16384:1.30 user 0.14 system 0.72 elapsed 198% CPU -time.2.tcmalloc.16384:0.63 user 0.03 system 0.68 elapsed 96% CPU -time.2.ptmalloc.32768:1.33 user 0.56 system 1.00 elapsed 189% CPU -time.2.tcmalloc.32768:1.16 user 0.01 system 1.17 elapsed 99% CPU -time.2.ptmalloc.65536:1.86 user 1.79 system 2.01 elapsed 181% CPU -time.2.tcmalloc.65536:1.35 user 0.01 system 1.35 elapsed 100% CPU -time.2.ptmalloc.131072:2.61 user 5.19 system 4.81 elapsed 162% CPU -time.2.tcmalloc.131072:1.86 user 0.04 system 1.90 elapsed 100% CPU -time.3.ptmalloc.64:1.79 user 0.03 system 0.67 elapsed 268% CPU -time.3.tcmalloc.64:1.58 user 0.04 system 0.62 elapsed 260% CPU -time.3.ptmalloc.128:2.77 user 1.34 system 3.07 elapsed 133% CPU -time.3.tcmalloc.128:1.19 user 0.01 system 0.50 elapsed 236% CPU -time.3.ptmalloc.256:2.14 user 0.02 system 0.85 elapsed 252% CPU -time.3.tcmalloc.256:0.96 user 0.01 system 0.41 elapsed 236% CPU -time.3.ptmalloc.512:3.37 user 1.31 system 3.33 elapsed 140% CPU -time.3.tcmalloc.512:0.93 user 0.04 system 0.39 elapsed 243% CPU -time.3.ptmalloc.1024:1.66 user 0.01 system 0.64 elapsed 260% CPU -time.3.tcmalloc.1024:0.81 user 0.02 system 0.44 elapsed 187% CPU -time.3.ptmalloc.2048:2.07 user 0.01 system 0.82 elapsed 252% CPU -time.3.tcmalloc.2048:1.10 user 0.04 system 0.59 elapsed 191% CPU -time.3.ptmalloc.4096:2.01 user 0.03 system 0.79 elapsed 258% CPU -time.3.tcmalloc.4096:0.87 user 0.03 system 0.65 elapsed 137% CPU -time.3.ptmalloc.8192:2.22 user 0.11 system 0.83 elapsed 280% CPU -time.3.tcmalloc.8192:0.96 user 0.06 system 0.75 elapsed 135% CPU -time.3.ptmalloc.16384:2.56 user 0.47 system 1.02 elapsed 295% CPU -time.3.tcmalloc.16384:0.99 user 0.04 system 1.03 elapsed 99% CPU -time.3.ptmalloc.32768:3.29 user 1.75 system 1.96 elapsed 256% CPU -time.3.tcmalloc.32768:1.67 user 0.02 system 1.69 elapsed 99% CPU -time.3.ptmalloc.65536:4.04 user 6.62 system 4.92 elapsed 216% CPU -time.3.tcmalloc.65536:1.91 user 0.02 system 1.98 elapsed 97% CPU -time.3.ptmalloc.131072:5.55 user 17.86 system 12.44 elapsed 188% CPU -time.3.tcmalloc.131072:2.78 user 0.02 system 2.82 elapsed 99% CPU -time.4.ptmalloc.64:3.42 user 1.36 system 3.20 elapsed 149% CPU -time.4.tcmalloc.64:2.42 user 0.02 system 0.71 elapsed 341% CPU -time.4.ptmalloc.128:3.98 user 1.79 system 3.89 elapsed 148% CPU -time.4.tcmalloc.128:1.87 user 0.02 system 0.58 elapsed 325% CPU -time.4.ptmalloc.256:4.06 user 2.14 system 4.12 elapsed 150% CPU -time.4.tcmalloc.256:1.69 user 0.02 system 0.51 elapsed 331% CPU -time.4.ptmalloc.512:4.48 user 2.15 system 4.39 elapsed 150% CPU -time.4.tcmalloc.512:1.62 user 0.03 system 0.52 elapsed 314% CPU -time.4.ptmalloc.1024:3.18 user 0.03 system 0.84 elapsed 381% CPU -time.4.tcmalloc.1024:1.53 user 0.02 system 0.56 elapsed 274% CPU -time.4.ptmalloc.2048:3.24 user 0.02 system 0.84 elapsed 384% CPU -time.4.tcmalloc.2048:1.44 user 0.04 system 0.66 elapsed 221% CPU -time.4.ptmalloc.4096:3.50 user 0.04 system 0.91 elapsed 389% CPU -time.4.tcmalloc.4096:1.31 user 0.01 system 0.89 elapsed 148% CPU -time.4.ptmalloc.8192:6.77 user 3.85 system 4.14 elapsed 256% CPU -time.4.tcmalloc.8192:1.20 user 0.05 system 0.97 elapsed 127% CPU -time.4.ptmalloc.16384:7.08 user 5.06 system 4.63 elapsed 262% CPU -time.4.tcmalloc.16384:1.27 user 0.03 system 1.25 elapsed 103% CPU -time.4.ptmalloc.32768:5.57 user 4.22 system 3.31 elapsed 295% CPU -time.4.tcmalloc.32768:2.17 user 0.03 system 2.25 elapsed 97% CPU -time.4.ptmalloc.65536:6.11 user 15.05 system 9.19 elapsed 230% CPU -time.4.tcmalloc.65536:2.51 user 0.02 system 2.57 elapsed 98% CPU -time.4.ptmalloc.131072:7.58 user 33.15 system 21.28 elapsed 191% CPU -time.4.tcmalloc.131072:3.57 user 0.07 system 3.66 elapsed 99% CPU -time.5.ptmalloc.64:4.44 user 2.08 system 4.37 elapsed 148% CPU -time.5.tcmalloc.64:2.87 user 0.02 system 0.79 elapsed 361% CPU -time.5.ptmalloc.128:4.77 user 2.77 system 5.14 elapsed 146% CPU -time.5.tcmalloc.128:2.65 user 0.03 system 0.72 elapsed 367% CPU -time.5.ptmalloc.256:5.82 user 2.88 system 5.49 elapsed 158% CPU -time.5.tcmalloc.256:2.33 user 0.01 system 0.66 elapsed 352% CPU -time.5.ptmalloc.512:6.27 user 3.11 system 5.34 elapsed 175% CPU -time.5.tcmalloc.512:2.14 user 0.03 system 0.70 elapsed 307% CPU -time.5.ptmalloc.1024:6.82 user 3.18 system 5.23 elapsed 191% CPU -time.5.tcmalloc.1024:2.20 user 0.02 system 0.70 elapsed 313% CPU -time.5.ptmalloc.2048:6.57 user 3.46 system 5.22 elapsed 192% CPU -time.5.tcmalloc.2048:2.15 user 0.03 system 0.82 elapsed 264% CPU -time.5.ptmalloc.4096:8.75 user 5.09 system 5.26 elapsed 263% CPU -time.5.tcmalloc.4096:1.68 user 0.03 system 1.08 elapsed 158% CPU -time.5.ptmalloc.8192:4.48 user 0.61 system 1.51 elapsed 335% CPU -time.5.tcmalloc.8192:1.47 user 0.07 system 1.18 elapsed 129% CPU -time.5.ptmalloc.16384:5.71 user 1.98 system 2.14 elapsed 358% CPU -time.5.tcmalloc.16384:1.58 user 0.03 system 1.52 elapsed 105% CPU -time.5.ptmalloc.32768:7.19 user 7.81 system 5.53 elapsed 270% CPU -time.5.tcmalloc.32768:2.63 user 0.05 system 2.72 elapsed 98% CPU -time.5.ptmalloc.65536:8.45 user 23.51 system 14.30 elapsed 223% CPU -time.5.tcmalloc.65536:3.12 user 0.05 system 3.21 elapsed 98% CPU -time.5.ptmalloc.131072:10.22 user 43.63 system 27.84 elapsed 193% CPU -time.5.tcmalloc.131072:4.42 user 0.07 system 4.51 elapsed 99% CPU -time.6.ptmalloc.64:5.57 user 2.56 system 5.08 elapsed 159% CPU -time.6.tcmalloc.64:3.20 user 0.01 system 0.89 elapsed 360% CPU -time.6.ptmalloc.128:5.98 user 3.52 system 5.71 elapsed 166% CPU -time.6.tcmalloc.128:2.76 user 0.02 system 0.78 elapsed 355% CPU -time.6.ptmalloc.256:4.61 user 0.02 system 1.19 elapsed 389% CPU -time.6.tcmalloc.256:2.65 user 0.02 system 0.74 elapsed 356% CPU -time.6.ptmalloc.512:8.28 user 3.88 system 6.61 elapsed 183% CPU -time.6.tcmalloc.512:2.60 user 0.02 system 0.72 elapsed 362% CPU -time.6.ptmalloc.1024:4.75 user 0.00 system 1.22 elapsed 387% CPU -time.6.tcmalloc.1024:2.56 user 0.02 system 0.79 elapsed 325% CPU -time.6.ptmalloc.2048:8.90 user 4.59 system 6.15 elapsed 219% CPU -time.6.tcmalloc.2048:2.37 user 0.06 system 0.96 elapsed 250% CPU -time.6.ptmalloc.4096:11.41 user 7.02 system 6.31 elapsed 291% CPU -time.6.tcmalloc.4096:1.82 user 0.03 system 1.19 elapsed 154% CPU -time.6.ptmalloc.8192:11.64 user 8.25 system 5.97 elapsed 332% CPU -time.6.tcmalloc.8192:1.83 user 0.07 system 1.38 elapsed 136% CPU -time.6.ptmalloc.16384:7.44 user 2.98 system 3.01 elapsed 345% CPU -time.6.tcmalloc.16384:1.83 user 0.08 system 1.80 elapsed 105% CPU -time.6.ptmalloc.32768:8.69 user 12.35 system 8.04 elapsed 261% CPU -time.6.tcmalloc.32768:3.14 user 0.06 system 3.24 elapsed 98% CPU -time.6.ptmalloc.65536:10.52 user 35.43 system 20.75 elapsed 221% CPU -time.6.tcmalloc.65536:3.62 user 0.03 system 3.72 elapsed 98% CPU -time.6.ptmalloc.131072:11.74 user 59.00 system 36.93 elapsed 191% CPU -time.6.tcmalloc.131072:5.33 user 0.04 system 5.42 elapsed 98% CPU -time.7.ptmalloc.64:6.60 user 3.45 system 6.01 elapsed 167% CPU -time.7.tcmalloc.64:3.50 user 0.04 system 0.94 elapsed 376% CPU -time.7.ptmalloc.128:7.09 user 4.25 system 6.69 elapsed 169% CPU -time.7.tcmalloc.128:3.13 user 0.03 system 0.84 elapsed 374% CPU -time.7.ptmalloc.256:9.28 user 4.85 system 7.20 elapsed 196% CPU -time.7.tcmalloc.256:3.06 user 0.02 system 0.82 elapsed 375% CPU -time.7.ptmalloc.512:9.13 user 4.78 system 6.79 elapsed 204% CPU -time.7.tcmalloc.512:2.99 user 0.03 system 0.83 elapsed 359% CPU -time.7.ptmalloc.1024:10.85 user 6.41 system 7.52 elapsed 229% CPU -time.7.tcmalloc.1024:3.05 user 0.04 system 0.89 elapsed 345% CPU -time.7.ptmalloc.2048:5.65 user 0.08 system 1.47 elapsed 388% CPU -time.7.tcmalloc.2048:3.01 user 0.01 system 0.98 elapsed 306% CPU -time.7.ptmalloc.4096:6.09 user 0.08 system 1.58 elapsed 389% CPU -time.7.tcmalloc.4096:2.25 user 0.03 system 1.32 elapsed 171% CPU -time.7.ptmalloc.8192:6.73 user 0.85 system 1.99 elapsed 379% CPU -time.7.tcmalloc.8192:2.22 user 0.08 system 1.61 elapsed 142% CPU -time.7.ptmalloc.16384:8.87 user 4.66 system 4.04 elapsed 334% CPU -time.7.tcmalloc.16384:2.07 user 0.07 system 2.07 elapsed 103% CPU -time.7.ptmalloc.32768:10.61 user 17.85 system 11.22 elapsed 253% CPU -time.7.tcmalloc.32768:3.68 user 0.06 system 3.79 elapsed 98% CPU -time.7.ptmalloc.65536:13.05 user 45.97 system 27.28 elapsed 216% CPU -time.7.tcmalloc.65536:4.16 user 0.07 system 4.31 elapsed 98% CPU -time.7.ptmalloc.131072:13.22 user 62.67 system 41.33 elapsed 183% CPU -time.7.tcmalloc.131072:6.10 user 0.06 system 6.25 elapsed 98% CPU -time.8.ptmalloc.64:7.31 user 3.92 system 6.39 elapsed 175% CPU -time.8.tcmalloc.64:4.00 user 0.01 system 1.04 elapsed 383% CPU -time.8.ptmalloc.128:9.40 user 5.41 system 7.67 elapsed 192% CPU -time.8.tcmalloc.128:3.61 user 0.02 system 0.94 elapsed 386% CPU -time.8.ptmalloc.256:10.61 user 6.35 system 7.96 elapsed 212% CPU -time.8.tcmalloc.256:3.30 user 0.02 system 0.99 elapsed 335% CPU -time.8.ptmalloc.512:12.42 user 7.10 system 8.79 elapsed 221% CPU -time.8.tcmalloc.512:3.35 user 0.04 system 0.94 elapsed 358% CPU -time.8.ptmalloc.1024:13.63 user 8.54 system 8.95 elapsed 247% CPU -time.8.tcmalloc.1024:3.44 user 0.02 system 0.96 elapsed 359% CPU -time.8.ptmalloc.2048:6.45 user 0.03 system 1.67 elapsed 386% CPU -time.8.tcmalloc.2048:3.55 user 0.05 system 1.09 elapsed 328% CPU -time.8.ptmalloc.4096:6.83 user 0.26 system 1.80 elapsed 393% CPU -time.8.tcmalloc.4096:2.78 user 0.06 system 1.53 elapsed 185% CPU -time.8.ptmalloc.8192:7.59 user 1.29 system 2.36 elapsed 376% CPU -time.8.tcmalloc.8192:2.57 user 0.07 system 1.84 elapsed 142% CPU -time.8.ptmalloc.16384:10.15 user 6.20 system 5.20 elapsed 314% CPU -time.8.tcmalloc.16384:2.40 user 0.05 system 2.42 elapsed 101% CPU -time.8.ptmalloc.32768:11.82 user 24.48 system 14.60 elapsed 248% CPU -time.8.tcmalloc.32768:4.37 user 0.05 system 4.47 elapsed 98% CPU -time.8.ptmalloc.65536:15.41 user 58.94 system 34.42 elapsed 215% CPU -time.8.tcmalloc.65536:4.90 user 0.04 system 4.96 elapsed 99% CPU -time.8.ptmalloc.131072:16.07 user 82.93 system 52.51 elapsed 188% CPU -time.8.tcmalloc.131072:7.13 user 0.04 system 7.19 elapsed 99% CPU -time.9.ptmalloc.64:8.44 user 4.59 system 6.92 elapsed 188% CPU -time.9.tcmalloc.64:4.00 user 0.02 system 1.05 elapsed 382% CPU -time.9.ptmalloc.128:10.92 user 6.14 system 8.31 elapsed 205% CPU -time.9.tcmalloc.128:3.88 user 0.02 system 1.01 elapsed 382% CPU -time.9.ptmalloc.256:13.01 user 7.75 system 9.12 elapsed 227% CPU -time.9.tcmalloc.256:3.89 user 0.01 system 1.00 elapsed 386% CPU -time.9.ptmalloc.512:14.96 user 8.89 system 9.73 elapsed 244% CPU -time.9.tcmalloc.512:3.80 user 0.03 system 1.01 elapsed 377% CPU -time.9.ptmalloc.1024:15.42 user 10.20 system 9.80 elapsed 261% CPU -time.9.tcmalloc.1024:3.86 user 0.03 system 1.19 elapsed 325% CPU -time.9.ptmalloc.2048:7.24 user 0.02 system 1.87 elapsed 388% CPU -time.9.tcmalloc.2048:3.98 user 0.05 system 1.26 elapsed 319% CPU -time.9.ptmalloc.4096:7.96 user 0.18 system 2.06 elapsed 394% CPU -time.9.tcmalloc.4096:3.27 user 0.04 system 1.69 elapsed 195% CPU -time.9.ptmalloc.8192:9.00 user 1.63 system 2.79 elapsed 380% CPU -time.9.tcmalloc.8192:3.00 user 0.06 system 2.05 elapsed 148% CPU -time.9.ptmalloc.16384:12.07 user 8.13 system 6.55 elapsed 308% CPU -time.9.tcmalloc.16384:2.85 user 0.05 system 2.75 elapsed 105% CPU -time.9.ptmalloc.32768:13.99 user 29.65 system 18.02 elapsed 242% CPU -time.9.tcmalloc.32768:4.98 user 0.06 system 5.13 elapsed 98% CPU -time.9.ptmalloc.65536:16.89 user 70.42 system 42.11 elapsed 207% CPU -time.9.tcmalloc.65536:5.55 user 0.04 system 5.65 elapsed 98% CPU -time.9.ptmalloc.131072:18.53 user 94.11 system 61.17 elapsed 184% CPU -time.9.tcmalloc.131072:8.06 user 0.04 system 8.16 elapsed 99% CPU -time.10.ptmalloc.64:9.81 user 5.70 system 7.42 elapsed 208% CPU -time.10.tcmalloc.64:4.43 user 0.03 system 1.20 elapsed 370% CPU -time.10.ptmalloc.128:12.69 user 7.81 system 9.02 elapsed 227% CPU -time.10.tcmalloc.128:4.27 user 0.02 system 1.13 elapsed 378% CPU -time.10.ptmalloc.256:15.04 user 9.53 system 9.92 elapsed 247% CPU -time.10.tcmalloc.256:4.23 user 0.02 system 1.09 elapsed 388% CPU -time.10.ptmalloc.512:17.30 user 10.46 system 10.61 elapsed 261% CPU -time.10.tcmalloc.512:4.14 user 0.05 system 1.10 elapsed 379% CPU -time.10.ptmalloc.1024:16.96 user 9.38 system 9.30 elapsed 283% CPU -time.10.tcmalloc.1024:4.27 user 0.06 system 1.18 elapsed 366% CPU -time.10.ptmalloc.2048:8.07 user 0.03 system 2.06 elapsed 393% CPU -time.10.tcmalloc.2048:4.49 user 0.07 system 1.33 elapsed 342% CPU -time.10.ptmalloc.4096:8.66 user 0.25 system 2.25 elapsed 394% CPU -time.10.tcmalloc.4096:3.61 user 0.05 system 1.78 elapsed 205% CPU -time.10.ptmalloc.8192:21.52 user 17.43 system 10.41 elapsed 374% CPU -time.10.tcmalloc.8192:3.59 user 0.10 system 2.33 elapsed 158% CPU -time.10.ptmalloc.16384:20.55 user 24.85 system 12.55 elapsed 361% CPU -time.10.tcmalloc.16384:3.29 user 0.04 system 3.22 elapsed 103% CPU -time.10.ptmalloc.32768:15.23 user 38.13 system 22.49 elapsed 237% CPU -time.10.tcmalloc.32768:5.62 user 0.05 system 5.72 elapsed 99% CPU -time.10.ptmalloc.65536:19.80 user 85.42 system 49.98 elapsed 210% CPU -time.10.tcmalloc.65536:6.23 user 0.09 system 6.36 elapsed 99% CPU -time.10.ptmalloc.131072:20.91 user 106.97 system 69.08 elapsed 185% CPU -time.10.tcmalloc.131072:8.94 user 0.09 system 9.09 elapsed 99% CPU -time.11.ptmalloc.64:10.82 user 6.34 system 7.92 elapsed 216% CPU -time.11.tcmalloc.64:4.80 user 0.03 system 1.24 elapsed 387% CPU -time.11.ptmalloc.128:14.58 user 8.61 system 9.81 elapsed 236% CPU -time.11.tcmalloc.128:4.65 user 0.03 system 1.21 elapsed 384% CPU -time.11.ptmalloc.256:17.38 user 10.98 system 10.75 elapsed 263% CPU -time.11.tcmalloc.256:4.51 user 0.03 system 1.18 elapsed 384% CPU -time.11.ptmalloc.512:19.18 user 11.71 system 10.95 elapsed 282% CPU -time.11.tcmalloc.512:4.57 user 0.02 system 1.19 elapsed 384% CPU -time.11.ptmalloc.1024:19.94 user 12.41 system 10.48 elapsed 308% CPU -time.11.tcmalloc.1024:4.71 user 0.05 system 1.29 elapsed 367% CPU -time.11.ptmalloc.2048:8.70 user 0.04 system 2.35 elapsed 371% CPU -time.11.tcmalloc.2048:4.97 user 0.07 system 1.43 elapsed 350% CPU -time.11.ptmalloc.4096:22.47 user 18.43 system 10.82 elapsed 377% CPU -time.11.tcmalloc.4096:4.22 user 0.03 system 1.91 elapsed 221% CPU -time.11.ptmalloc.8192:11.61 user 2.38 system 3.73 elapsed 374% CPU -time.11.tcmalloc.8192:3.74 user 0.09 system 2.46 elapsed 155% CPU -time.11.ptmalloc.16384:14.13 user 13.38 system 9.60 elapsed 286% CPU -time.11.tcmalloc.16384:3.61 user 0.03 system 3.63 elapsed 100% CPU -time.11.ptmalloc.32768:17.92 user 43.84 system 26.74 elapsed 230% CPU -time.11.tcmalloc.32768:6.31 user 0.03 system 6.45 elapsed 98% CPU -time.11.ptmalloc.65536:22.40 user 96.38 system 58.30 elapsed 203% CPU -time.11.tcmalloc.65536:6.92 user 0.12 system 6.98 elapsed 100% CPU -time.11.ptmalloc.131072:21.03 user 108.04 system 72.78 elapsed 177% CPU -time.11.tcmalloc.131072:9.79 user 0.08 system 9.94 elapsed 99% CPU -time.12.ptmalloc.64:12.23 user 7.16 system 8.38 elapsed 231% CPU -time.12.tcmalloc.64:5.21 user 0.05 system 1.41 elapsed 371% CPU -time.12.ptmalloc.128:16.97 user 10.19 system 10.47 elapsed 259% CPU -time.12.tcmalloc.128:5.10 user 0.02 system 1.31 elapsed 390% CPU -time.12.ptmalloc.256:19.99 user 12.10 system 11.57 elapsed 277% CPU -time.12.tcmalloc.256:5.01 user 0.03 system 1.29 elapsed 390% CPU -time.12.ptmalloc.512:21.85 user 12.66 system 11.46 elapsed 300% CPU -time.12.tcmalloc.512:5.05 user 0.00 system 1.32 elapsed 379% CPU -time.12.ptmalloc.1024:9.40 user 0.04 system 2.40 elapsed 393% CPU -time.12.tcmalloc.1024:5.14 user 0.02 system 1.39 elapsed 369% CPU -time.12.ptmalloc.2048:9.72 user 0.04 system 2.49 elapsed 391% CPU -time.12.tcmalloc.2048:5.74 user 0.05 system 1.62 elapsed 355% CPU -time.12.ptmalloc.4096:10.64 user 0.20 system 2.75 elapsed 393% CPU -time.12.tcmalloc.4096:4.45 user 0.03 system 2.04 elapsed 218% CPU -time.12.ptmalloc.8192:12.66 user 3.30 system 4.30 elapsed 371% CPU -time.12.tcmalloc.8192:4.21 user 0.13 system 2.65 elapsed 163% CPU -time.12.ptmalloc.16384:15.73 user 15.68 system 11.14 elapsed 281% CPU -time.12.tcmalloc.16384:4.17 user 0.06 system 4.10 elapsed 102% CPU -time.12.ptmalloc.32768:19.45 user 56.00 system 32.74 elapsed 230% CPU -time.12.tcmalloc.32768:6.96 user 0.08 system 7.14 elapsed 98% CPU -time.12.ptmalloc.65536:23.33 user 110.45 system 65.06 elapsed 205% CPU -time.12.tcmalloc.65536:7.77 user 0.15 system 7.72 elapsed 102% CPU -time.12.ptmalloc.131072:24.03 user 124.74 system 82.94 elapsed 179% CPU -time.12.tcmalloc.131072:10.81 user 0.06 system 10.94 elapsed 99% CPU -time.13.ptmalloc.64:14.08 user 7.60 system 8.85 elapsed 244% CPU -time.13.tcmalloc.64:5.51 user 0.01 system 1.47 elapsed 375% CPU -time.13.ptmalloc.128:18.20 user 10.98 system 10.99 elapsed 265% CPU -time.13.tcmalloc.128:5.34 user 0.01 system 1.39 elapsed 382% CPU -time.13.ptmalloc.256:21.48 user 13.94 system 12.25 elapsed 289% CPU -time.13.tcmalloc.256:5.33 user 0.01 system 1.39 elapsed 381% CPU -time.13.ptmalloc.512:24.22 user 14.84 system 12.97 elapsed 301% CPU -time.13.tcmalloc.512:5.49 user 0.02 system 1.41 elapsed 389% CPU -time.13.ptmalloc.1024:25.26 user 17.03 system 12.85 elapsed 328% CPU -time.13.tcmalloc.1024:5.65 user 0.04 system 1.50 elapsed 378% CPU -time.13.ptmalloc.2048:10.41 user 0.03 system 2.69 elapsed 387% CPU -time.13.tcmalloc.2048:5.93 user 0.10 system 1.77 elapsed 339% CPU -time.13.ptmalloc.4096:11.37 user 0.52 system 3.04 elapsed 391% CPU -time.13.tcmalloc.4096:5.08 user 0.11 system 2.22 elapsed 233% CPU -time.13.ptmalloc.8192:21.76 user 18.54 system 10.58 elapsed 380% CPU -time.13.tcmalloc.8192:5.04 user 0.16 system 2.93 elapsed 177% CPU -time.13.ptmalloc.16384:26.35 user 34.47 system 17.01 elapsed 357% CPU -time.13.tcmalloc.16384:4.66 user 0.04 system 4.66 elapsed 100% CPU -time.13.ptmalloc.32768:21.41 user 63.59 system 38.14 elapsed 222% CPU -time.13.tcmalloc.32768:7.71 user 0.03 system 7.83 elapsed 98% CPU -time.13.ptmalloc.65536:24.99 user 120.80 system 71.59 elapsed 203% CPU -time.13.tcmalloc.65536:8.87 user 0.64 system 8.37 elapsed 113% CPU -time.13.ptmalloc.131072:25.97 user 142.27 system 96.00 elapsed 175% CPU -time.13.tcmalloc.131072:11.48 user 0.06 system 11.67 elapsed 98% CPU -time.14.ptmalloc.64:15.01 user 9.11 system 9.41 elapsed 256% CPU -time.14.tcmalloc.64:5.98 user 0.02 system 1.58 elapsed 378% CPU -time.14.ptmalloc.128:20.34 user 12.72 system 11.62 elapsed 284% CPU -time.14.tcmalloc.128:5.88 user 0.04 system 1.51 elapsed 392% CPU -time.14.ptmalloc.256:24.26 user 14.95 system 12.92 elapsed 303% CPU -time.14.tcmalloc.256:5.72 user 0.02 system 1.50 elapsed 381% CPU -time.14.ptmalloc.512:27.28 user 16.45 system 13.89 elapsed 314% CPU -time.14.tcmalloc.512:5.99 user 0.02 system 1.54 elapsed 388% CPU -time.14.ptmalloc.1024:25.84 user 16.99 system 12.61 elapsed 339% CPU -time.14.tcmalloc.1024:5.94 user 0.06 system 1.59 elapsed 375% CPU -time.14.ptmalloc.2048:11.96 user 0.01 system 3.12 elapsed 382% CPU -time.14.tcmalloc.2048:6.39 user 0.07 system 1.79 elapsed 359% CPU -time.14.ptmalloc.4096:20.19 user 11.77 system 8.26 elapsed 386% CPU -time.14.tcmalloc.4096:5.65 user 0.05 system 2.32 elapsed 244% CPU -time.14.ptmalloc.8192:22.01 user 16.39 system 9.89 elapsed 387% CPU -time.14.tcmalloc.8192:5.44 user 0.11 system 3.07 elapsed 180% CPU -time.14.ptmalloc.16384:18.15 user 22.40 system 15.02 elapsed 269% CPU -time.14.tcmalloc.16384:5.29 user 0.08 system 5.34 elapsed 100% CPU -time.14.ptmalloc.32768:24.29 user 72.07 system 42.63 elapsed 225% CPU -time.14.tcmalloc.32768:8.47 user 0.02 system 8.62 elapsed 98% CPU -time.14.ptmalloc.65536:27.63 user 130.56 system 78.64 elapsed 201% CPU -time.14.tcmalloc.65536:9.85 user 1.61 system 9.04 elapsed 126% CPU -time.14.ptmalloc.131072:28.87 user 146.38 system 100.54 elapsed 174% CPU -time.14.tcmalloc.131072:12.46 user 0.11 system 12.71 elapsed 98% CPU -time.15.ptmalloc.64:16.25 user 10.05 system 9.82 elapsed 267% CPU -time.15.tcmalloc.64:6.30 user 0.02 system 1.64 elapsed 385% CPU -time.15.ptmalloc.128:22.33 user 13.23 system 12.24 elapsed 290% CPU -time.15.tcmalloc.128:6.08 user 0.03 system 1.59 elapsed 384% CPU -time.15.ptmalloc.256:26.56 user 16.57 system 13.70 elapsed 314% CPU -time.15.tcmalloc.256:6.14 user 0.03 system 1.61 elapsed 382% CPU -time.15.ptmalloc.512:29.68 user 18.08 system 14.56 elapsed 327% CPU -time.15.tcmalloc.512:6.12 user 0.04 system 1.68 elapsed 364% CPU -time.15.ptmalloc.1024:17.07 user 6.22 system 6.26 elapsed 371% CPU -time.15.tcmalloc.1024:6.38 user 0.02 system 1.75 elapsed 364% CPU -time.15.ptmalloc.2048:26.64 user 17.25 system 11.51 elapsed 381% CPU -time.15.tcmalloc.2048:6.77 user 0.18 system 1.92 elapsed 361% CPU -time.15.ptmalloc.4096:13.21 user 0.74 system 3.57 elapsed 390% CPU -time.15.tcmalloc.4096:6.03 user 0.09 system 2.36 elapsed 258% CPU -time.15.ptmalloc.8192:22.92 user 17.51 system 10.50 elapsed 385% CPU -time.15.tcmalloc.8192:5.96 user 0.12 system 3.36 elapsed 180% CPU -time.15.ptmalloc.16384:19.37 user 24.87 system 16.69 elapsed 264% CPU -time.15.tcmalloc.16384:5.88 user 0.07 system 5.84 elapsed 101% CPU -time.15.ptmalloc.32768:25.43 user 82.30 system 48.98 elapsed 219% CPU -time.15.tcmalloc.32768:9.11 user 0.05 system 9.30 elapsed 98% CPU -time.15.ptmalloc.65536:29.31 user 140.07 system 83.78 elapsed 202% CPU -time.15.tcmalloc.65536:8.51 user 1.59 system 9.75 elapsed 103% CPU -time.15.ptmalloc.131072:30.22 user 163.15 system 109.50 elapsed 176% CPU -time.15.tcmalloc.131072:13.35 user 0.10 system 13.54 elapsed 99% CPU -time.16.ptmalloc.64:17.69 user 10.11 system 10.11 elapsed 274% CPU -time.16.tcmalloc.64:6.63 user 0.04 system 1.72 elapsed 387% CPU -time.16.ptmalloc.128:23.05 user 14.37 system 12.75 elapsed 293% CPU -time.16.tcmalloc.128:6.61 user 0.02 system 1.71 elapsed 387% CPU -time.16.ptmalloc.256:29.11 user 19.35 system 14.57 elapsed 332% CPU -time.16.tcmalloc.256:6.62 user 0.03 system 1.73 elapsed 382% CPU -time.16.ptmalloc.512:31.65 user 18.71 system 14.71 elapsed 342% CPU -time.16.tcmalloc.512:6.63 user 0.04 system 1.73 elapsed 383% CPU -time.16.ptmalloc.1024:31.99 user 21.22 system 14.87 elapsed 357% CPU -time.16.tcmalloc.1024:6.81 user 0.04 system 1.79 elapsed 382% CPU -time.16.ptmalloc.2048:30.35 user 21.36 system 13.30 elapsed 388% CPU -time.16.tcmalloc.2048:6.91 user 0.50 system 2.01 elapsed 367% CPU -time.16.ptmalloc.4096:18.85 user 7.18 system 6.61 elapsed 393% CPU -time.16.tcmalloc.4096:6.70 user 0.10 system 2.62 elapsed 259% CPU -time.16.ptmalloc.8192:22.19 user 14.30 system 9.37 elapsed 389% CPU -time.16.tcmalloc.8192:6.18 user 0.19 system 3.58 elapsed 177% CPU -time.16.ptmalloc.16384:31.22 user 46.78 system 22.92 elapsed 340% CPU -time.16.tcmalloc.16384:6.79 user 0.07 system 6.86 elapsed 99% CPU -time.16.ptmalloc.32768:27.31 user 87.32 system 52.00 elapsed 220% CPU -time.16.tcmalloc.32768:9.85 user 0.06 system 10.07 elapsed 98% CPU -time.16.ptmalloc.65536:32.83 user 160.62 system 95.67 elapsed 202% CPU -time.16.tcmalloc.65536:10.18 user 0.09 system 10.41 elapsed 98% CPU -time.16.ptmalloc.131072:31.99 user 173.41 system 115.98 elapsed 177% CPU -time.16.tcmalloc.131072:14.52 user 0.05 system 14.67 elapsed 99% CPU -time.17.ptmalloc.64:19.38 user 11.61 system 10.61 elapsed 291% CPU -time.17.tcmalloc.64:7.11 user 0.02 system 1.84 elapsed 386% CPU -time.17.ptmalloc.128:26.25 user 16.15 system 13.53 elapsed 313% CPU -time.17.tcmalloc.128:6.97 user 0.02 system 1.78 elapsed 390% CPU -time.17.ptmalloc.256:30.66 user 18.36 system 14.97 elapsed 327% CPU -time.17.tcmalloc.256:6.94 user 0.04 system 1.80 elapsed 387% CPU -time.17.ptmalloc.512:33.71 user 22.79 system 15.95 elapsed 354% CPU -time.17.tcmalloc.512:7.00 user 0.02 system 1.83 elapsed 381% CPU -time.17.ptmalloc.1024:33.49 user 22.47 system 15.00 elapsed 373% CPU -time.17.tcmalloc.1024:7.20 user 0.03 system 1.90 elapsed 380% CPU -time.17.ptmalloc.2048:23.87 user 11.92 system 9.26 elapsed 386% CPU -time.17.tcmalloc.2048:6.01 user 1.83 system 2.15 elapsed 363% CPU -time.17.ptmalloc.4096:14.69 user 0.95 system 3.98 elapsed 392% CPU -time.17.tcmalloc.4096:7.25 user 0.10 system 2.62 elapsed 279% CPU -time.17.ptmalloc.8192:22.44 user 13.52 system 9.39 elapsed 382% CPU -time.17.tcmalloc.8192:7.21 user 0.24 system 3.95 elapsed 188% CPU -time.17.ptmalloc.16384:23.33 user 33.67 system 21.89 elapsed 260% CPU -time.17.tcmalloc.16384:7.28 user 0.06 system 7.10 elapsed 103% CPU -time.17.ptmalloc.32768:29.35 user 103.11 system 60.36 elapsed 219% CPU -time.17.tcmalloc.32768:10.53 user 0.07 system 10.71 elapsed 98% CPU -time.17.ptmalloc.65536:33.21 user 170.89 system 100.84 elapsed 202% CPU -time.17.tcmalloc.65536:10.85 user 0.05 system 11.04 elapsed 98% CPU -time.17.ptmalloc.131072:34.98 user 182.87 system 122.05 elapsed 178% CPU -time.17.tcmalloc.131072:15.27 user 0.09 system 15.49 elapsed 99% CPU -time.18.ptmalloc.64:21.08 user 12.15 system 11.43 elapsed 290% CPU -time.18.tcmalloc.64:7.45 user 0.03 system 1.95 elapsed 383% CPU -time.18.ptmalloc.128:27.65 user 17.26 system 14.03 elapsed 320% CPU -time.18.tcmalloc.128:7.46 user 0.03 system 1.92 elapsed 389% CPU -time.18.ptmalloc.256:32.78 user 20.55 system 15.70 elapsed 339% CPU -time.18.tcmalloc.256:7.31 user 0.02 system 1.88 elapsed 389% CPU -time.18.ptmalloc.512:33.31 user 20.06 system 15.05 elapsed 354% CPU -time.18.tcmalloc.512:7.33 user 0.02 system 1.91 elapsed 383% CPU -time.18.ptmalloc.1024:35.46 user 24.83 system 16.30 elapsed 369% CPU -time.18.tcmalloc.1024:7.60 user 0.06 system 2.05 elapsed 373% CPU -time.18.ptmalloc.2048:19.98 user 6.80 system 6.76 elapsed 395% CPU -time.18.tcmalloc.2048:6.89 user 1.29 system 2.28 elapsed 357% CPU -time.18.ptmalloc.4096:15.99 user 0.93 system 4.32 elapsed 391% CPU -time.18.tcmalloc.4096:7.70 user 0.10 system 2.77 elapsed 280% CPU -time.18.ptmalloc.8192:23.51 user 14.84 system 9.97 elapsed 384% CPU -time.18.tcmalloc.8192:8.16 user 0.27 system 4.25 elapsed 197% CPU -time.18.ptmalloc.16384:35.79 user 52.41 system 26.47 elapsed 333% CPU -time.18.tcmalloc.16384:7.81 user 0.07 system 7.61 elapsed 103% CPU -time.18.ptmalloc.32768:33.17 user 116.07 system 68.64 elapsed 217% CPU -time.18.tcmalloc.32768:11.34 user 0.13 system 11.57 elapsed 99% CPU -time.18.ptmalloc.65536:35.91 user 177.82 system 106.75 elapsed 200% CPU -time.18.tcmalloc.65536:11.54 user 0.06 system 11.74 elapsed 98% CPU -time.18.ptmalloc.131072:36.38 user 187.18 system 126.91 elapsed 176% CPU -time.18.tcmalloc.131072:16.34 user 0.05 system 16.43 elapsed 99% CPU -time.19.ptmalloc.64:22.90 user 13.23 system 11.82 elapsed 305% CPU -time.19.tcmalloc.64:7.81 user 0.02 system 2.01 elapsed 388% CPU -time.19.ptmalloc.128:30.13 user 18.58 system 14.77 elapsed 329% CPU -time.19.tcmalloc.128:7.74 user 0.02 system 2.01 elapsed 386% CPU -time.19.ptmalloc.256:35.33 user 21.41 system 16.35 elapsed 347% CPU -time.19.tcmalloc.256:7.79 user 0.04 system 2.04 elapsed 382% CPU -time.19.ptmalloc.512:39.30 user 26.22 system 17.84 elapsed 367% CPU -time.19.tcmalloc.512:7.80 user 0.06 system 2.05 elapsed 381% CPU -time.19.ptmalloc.1024:35.70 user 23.90 system 15.66 elapsed 380% CPU -time.19.tcmalloc.1024:8.08 user 0.06 system 2.16 elapsed 376% CPU -time.19.ptmalloc.2048:18.33 user 3.28 system 5.47 elapsed 394% CPU -time.19.tcmalloc.2048:8.71 user 0.05 system 2.40 elapsed 363% CPU -time.19.ptmalloc.4096:16.94 user 0.89 system 4.64 elapsed 383% CPU -time.19.tcmalloc.4096:8.21 user 0.07 system 2.85 elapsed 289% CPU -time.19.ptmalloc.8192:25.61 user 17.15 system 11.33 elapsed 377% CPU -time.19.tcmalloc.8192:8.79 user 0.30 system 4.58 elapsed 198% CPU -time.19.ptmalloc.16384:27.11 user 46.66 system 29.67 elapsed 248% CPU -time.19.tcmalloc.16384:8.64 user 0.05 system 8.58 elapsed 101% CPU -time.19.ptmalloc.32768:33.80 user 117.69 system 70.65 elapsed 214% CPU -time.19.tcmalloc.32768:11.88 user 0.07 system 12.04 elapsed 99% CPU -time.19.ptmalloc.65536:36.90 user 180.21 system 109.01 elapsed 199% CPU -time.19.tcmalloc.65536:12.17 user 0.07 system 12.40 elapsed 98% CPU -time.19.ptmalloc.131072:38.50 user 195.15 system 132.81 elapsed 175% CPU -time.19.tcmalloc.131072:17.44 user 0.10 system 17.65 elapsed 99% CPU -time.20.ptmalloc.64:23.37 user 13.74 system 11.86 elapsed 312% CPU -time.20.tcmalloc.64:8.18 user 0.02 system 2.10 elapsed 389% CPU -time.20.ptmalloc.128:31.29 user 19.97 system 15.53 elapsed 329% CPU -time.20.tcmalloc.128:8.03 user 0.02 system 2.12 elapsed 378% CPU -time.20.ptmalloc.256:38.40 user 25.65 system 18.25 elapsed 350% CPU -time.20.tcmalloc.256:8.05 user 0.05 system 2.12 elapsed 380% CPU -time.20.ptmalloc.512:40.60 user 27.70 system 18.46 elapsed 369% CPU -time.20.tcmalloc.512:8.22 user 0.08 system 2.20 elapsed 375% CPU -time.20.ptmalloc.1024:40.02 user 28.52 system 17.56 elapsed 390% CPU -time.20.tcmalloc.1024:8.50 user 0.07 system 2.19 elapsed 391% CPU -time.20.ptmalloc.2048:16.13 user 0.23 system 4.23 elapsed 386% CPU -time.20.tcmalloc.2048:8.98 user 0.03 system 2.45 elapsed 367% CPU -time.20.ptmalloc.4096:17.14 user 0.87 system 4.60 elapsed 391% CPU -time.20.tcmalloc.4096:8.93 user 0.20 system 2.97 elapsed 306% CPU -time.20.ptmalloc.8192:25.24 user 17.16 system 11.14 elapsed 380% CPU -time.20.tcmalloc.8192:9.78 user 0.30 system 5.14 elapsed 195% CPU -time.20.ptmalloc.16384:39.93 user 60.36 system 30.24 elapsed 331% CPU -time.20.tcmalloc.16384:9.57 user 0.09 system 9.43 elapsed 102% CPU -time.20.ptmalloc.32768:36.44 user 130.23 system 76.79 elapsed 217% CPU -time.20.tcmalloc.32768:12.71 user 0.09 system 12.97 elapsed 98% CPU -time.20.ptmalloc.65536:39.79 user 202.09 system 120.34 elapsed 200% CPU -time.20.tcmalloc.65536:12.93 user 0.06 system 13.15 elapsed 98% CPU -time.20.ptmalloc.131072:41.91 user 202.76 system 138.51 elapsed 176% CPU -time.20.tcmalloc.131072:18.23 user 0.07 system 18.42 elapsed 99% CPU diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.1024.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.1024.bytes.png deleted file mode 100644 index 8c0ae6b59c2950c54db6f0c4f9b7fd585bcfdb71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1882 zcmV-g2c`IlP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3i*hxe|RCt{2o6m3CHWbG_1VwMleJiSeVA#$>h6(#4jNX>NVEu7v zvSup_6j*P`A5fsVH^X}9AqBFQ3wH3SFoqq1p>UWXK#SIBxPzL^kphPe`bMN&o z1w6Xef;IWW@|*4E>U#gny?xkiw#3WU?X~$5@MarAxw3gP46a}4MNtcOe_0UETfz0R zHeLF?!sAfE?rYGBZ4s1!S6guB;#bR^ zUSl;lUe;|~yWokJe}}6jiW5>aeu4__bHPG71K&9?o-Zvn0c0zBOb~)j%`KhJz{I~O z+{moz$#9r*KB+1f1dj_&EAW4U4#@$GKVFcBFBl*^{>$$r#d@w__faowtZ7&849b;o zYw1|q%|~uv?~w)tL-E-rD!RAv$-<*+``AWs1as-b1mg>z@=PuczE4fg8N892oGa+v zNKCGK|Ni|AClFRrlXF#smDJ?i!84Qlz(K_YA8^4-T<{Af*l#kyLx---flV&x_jq6* znBZY?hCl-2CKHT&mzPKb>`EpW2>`(XQH}*-k~0UDGpM-W`T!iCv6q93OfXg*KRu+S z_H21xZ*Cj*;V)Y=u? z$l~Ofq!S2iaD6ZfAcKQhK*Lm24vJv5(G4_u)dk!H<319-Rn|GD2@<_k0zTt{x$ulHTF+qS;QExcd- zn%C~vY62BCd(O5{BH`ROdbgMTXh8ftAHm!rB;n$ujj7@|xYLPaPjx=Y6hY^I8!fU! z*2V((YF^8sO4dew=ofpq2>yYaxm>yK7-t9DxXBO8f`7My>5ZP6tRN+w5-7N!d_<067F?~qQQG4Ei9yV=2Kw=k(0KyU)t@l_LaQrs~>=>p#3DuMwj zF6g9a-VW*oPS&d?=oDUKE1GR|ijo0WF$W4dIQ~vfR}>~F9l-TU6cruhw-HRxC7zQ5 z*@`Z)j%pOyAq%!5JJw-KiUbOhEKSieQMZ5+(n(nbikl=*=}%8qqqsx@lNar36l06w zTR_YOsuIP>V)zyi>!nKlKnBXGiWUpZKz{^AW!IrA_NFMx(JTN<$uiHA%>t$?hL$Ih zt*W||DD^y~xm93!64?v@X9Hx#sg@I1oQ@5|Fi5G>At@@NVu0ovT@=+&D>vFFfkj1S z9#9nrHg9y#WKb3^kgEelQI5VIv6I1_@e_GlnhZvZ$*vCBdN>z_E8PzBKDVJ3eL z^_U?0pR;k33qD}}J2<`q?rG3QCP-Y%w}71V-hq9gH*YYPyi+BA+4A%!#K5G&oRqrv z@dwTk7Ta&6U=lJoz}z8j@F=-QFEA8(4|Be+FOOG!i^W=78R$c5QOQE7$iVO+q*LZ6 zsE8IAAs9VO!LH1zic1PGKdkCTR@KJ{c7|2mNWr%VCib(9o28-RUA@`GQ@?tsC{jgr z*KA`j?3@v}m4Qj;jKD3kSNDeCT2|G4?A2AQxu?PYvd$c1Rks`OpXHo^vjSt;LAN={ z15N{@fCcuw=;fb{wa-0<;`66l|32j?9;n+t9rz5z10}CF6$@CW~x&QzG diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.128.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.128.bytes.png deleted file mode 100644 index 24b2a27410fdd5b14b0b59ab74eaef125e097108..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1731 zcmV;!20ZzRP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3iK}keGRCt{2oK0&RM;OOdObN6k`T%v=V=tx0P{={_3p6L;Lrc|D zkA1n=ZcM!?f#w!}fIxgL_Q8ix5|ERL$w7w_d?+E5;Es!+A-HHAg1fS%-KR6NGpm)f zFY`Q-*Kw;c8jqL#?f;pXXWmz?05Mc%;0!!#a7lb#zYx)?{EGtwuiD^>pCa%B@#*;@ zGPfRg_N>5#_j$MHx7Oz8yD#DKolCVK+=Ea5o?mv0zz%~Izq#bC`dX?3XX*t_{M=@l?sv-9U=(cVFed17*KXGvR?vV z#`Hi&IoFk`R1h30n0Lczf{1_s!%J6~8W^x3Jovq)1_tP1!IS%PU;sEmgG>$#UEw!x z;lSQ~IWQ0upWl;;_&h$ma$gP%VD3NldOSy9IR75aZDpHac427_K%U`vBNEC>b+$^sv2(8q$z zfQVhTA<)BuoC@AB;MlHx0jLu~xh&9l4#R@RSD7l`GC>y$nmoFQF(6s0A!hKH4Agy2 zka9GU4>r#V>|w^`WOd*{o$$*8ccWeF$XX1z8SRsM1Q5vJx>yBpy_0PdqE$dEQpAAq zx7$pL{Vw6w?!%!fHxIvW< z@;4lW0chQ^P6W!fkO*rkF;ZvM@2Xpev= zc`65Siox5-wUr1#hN2o99?3|mz~`0r=yIN=gVK`l-MLUMP6O!9ja5fhgOqz=fBOB4 z?)6IbWdA}V0+$kCdh-b1c?1s~!B^a^!-^y9Ql;9<1?3cPCxE$6KotRx3UCY@byn3; z|11pcS7G!3dI%i$IR*#)=nNmAG4eo>$3*KG8Iv3Pl{ulf6+L7OOqw_}LWULRAY&Rk z1`Z87-9}~u9S4&ZwT8{h2l?Q4zn00RfTkjaYqhhv6i_EZ;aUzJ4B7wKYzAfp`a__c zOENnU%)2Wp$j;og1@+r9DuhfsxB=7ZXrVxG#_}XTg zWWeO5oj|@N~}9@A+=ObA2j

52$?VnwDv!cl2$GDYn9 zqXda>1ZhR=5!(Q?4O>Hro?Su( zP99{67PgLhJ>3OunpLXbTeZhP$d1}<-;nE3aM7R+PqjL&>k#b1*{sTE;0*l#fLull zO8IiALj=j+oDFxW;1==Q!6D+c-Z(^gB!c7LsJ8_v;oO1hMWRga{s3cVv!wVHUmeCn~hZLev-8fcGFXAiCc z{ku|8rXDi=Zge2Q;I0PMt4qpMd87c<*wjs9>H`TL#-?s+@K}P%e|BWF6f52q(I4{C zwj&i?t;laD^XSEeGXR?gRE0ADo5@n$kHIx#YFn1-Eor$U!2YsGbfu{~t@GzOGq5Z$ zWQVfb%<2L2z`!GceJ6PNTWjqjhoJb^ljc89D2g3^#>R diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.131072.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.131072.bytes.png deleted file mode 100644 index 183a77b9c9f2c969793de0185503e6c6daf2806c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1314 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 FV1iolL~;NC diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.2048.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.2048.bytes.png deleted file mode 100644 index 169546f2406351f10c7c227524cb0f938561b3bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1877 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 diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.32768.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.32768.bytes.png deleted file mode 100644 index 18715e3a5abfbb2d8534c2ee3809057a39a872b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1516 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 diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.512.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.512.bytes.png deleted file mode 100644 index aea1d676471c03d918d78838004355f524d1e74c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1683 zcmV;E25k9>P)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3i5lKWrRCt{2o6l<-M-<05nBqgI^#{~tkG+&0Lm>xIdT&p{hnDKr zW7=M9*P>oSpt;3=Kp?)`P#|$SC+K#T0L@ArEcKTLexm-nj*7xZ zo%1s-aOq>&?mD&2#l`k3c=~Xm*zCIS)jx}?RvOq4P;sg&_PX;u2ze$!W%ZU_*-N#t zV6_!!71o_QP?HBHR3t3htDmQV%MIZuaM}J&fnE6vI10RFe=}m`1jP?!`=@$svv9E5 zfP42BitRT?@O9zf$27&s!5;lW7c{xxShxnS9OxCeAY0L7f?zidT9^+(>|(HA0uW)= zU5paG_p{0c!Q_HT54=r~5D1{Ra9j8T0~LhFe-!6Lbk)kPX<)9dx=((9Ru{Zr-5C z9kdAZ-8kq`Cd!$BE(er8IJWRk2e%x5`a%cGj-Mujo@^E|Ep$0=K3wTI{vuc#94Z_( zC^=wYpv)EYEGFo3!H$@)EupK(0Q2f=$45S>J4G4;b(=?q zpdf&d2`KeJY|Sh%JZk9&&E}CIsM$QmZ6tuh&ucc1>A+$f)NCHUjWQEZvt4n(=%_Td zw!%EM%o zfs6z(^l2g(tAb0zM9|T2*omM{_}_pQ7ZhAjazUF5I$RK9^Hm}^m?2XY*`OJ&0yIN( z7QihWK3JHM))8NKkihsV;EIFsbq5KA+`;|^0`0P$GqPpD0xdFl%2nLxCnOmJu40fN z+&>tq4u)b-sw6g(dL$g`Pite4AUW$)^%%{`Evll$RD`Vn6nQB|HcF}@Fcl+^0%xO0 zan@`U3HL!VQlvDGj0PQ%yQ0ipk$abnugJYF#aHBBUg9VUA7or5Do&@!xGf`?tAJ2Z z(iNv%Td}X8&=muaeanKa$iDNyRwUj)$Uu>J$ex9w%w3Uoa-D@D?Qk{=MYqgVY%MWC zUCjwL>YWHrDx6a;=;}yLtd0{6a&kSMl$jv3N~IL`a?fdT!Gx{-6fjvb$;lHXlfYII znb@q796q@{x|dfS9iea4B>;V)%_~84Me%H~LE?H2r&~|d4bAr-tskAg>`{dW) z)dVF*0&;md)MSF}H)p+LF1X8lJJ=(>6J`$VSt@#iDSsY#jFGAS z9o(3dm=Z#jK3-CD@v(X>0K4jIXkh9bBmCso(M#M4eTb=IQT;sr>IFV_Rt>=-1RnJo zBP#@*N2rLYLsUc$bP;qP1t7k_(9bF!DL^60>Q0!|#|So~tnLKh1%kf+9AUH)DLzoq zpZL{*iHcUBC?CXaw4=fqfYlK63ugdU5x;6{!neMCKYnz{$48^~nRsVg)QEbS&zcz{t#fB8i$C3rs?tMyD dObPje{{dYTv%BVIT4?|P002ovPDHLkV1fWh2Rr}( diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.64.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.64.bytes.png deleted file mode 100644 index 3a080de753381d204783087f8a7f387ea25e2d0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1656 zcmV-;28a2HP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3h_DMuRRCt{2+|N$jMi>BaM^S~;L-qk$zgi@KKn;HrizNY*Ev};l z|LV5EUh1E8S6Z`9|I3cY7)y5_^5nl49Q~9Ug-0@xn%~!twnhOF^>51 ziHK-^jL+_FebM#eD^C^uc!MB&|2k;5i;)r4B!)p`*G6{ZY1y?7OpAwE`^^O!j~~*zH0XzgsZ~HpffYq#}EqxEc8Pp zq*zEILkL%et|1g>vshs!uOVf`_*F}Ug&`L5f36Wsj?p)T$qKy!BxcZ&D~y;zM&(N6 zBH;xw8CY`>4dGyJC3;^M66j{H$g0sQc{$tMf5!~+!=Z2imSxL`3z{&1eaqeKSr#-0 z#~2P_>1ZX-)T8BDciC!Oh-1`+a%anB;)*tW0iA-SqgBs>&#fb|Mw4puW1LY!_AXer zGxf>cAdQ2(dbA_DAhR_G$BkCJ3x04QpT`PFWEY6fDddX+Hf0x#yEn+e+-Sv?E!hRs z81jfkqX8pQniBc@RX6k9(-NhG6LMZcw+yxvkc=S7eAk82lDV+v;iOB6gnkJ;SHWNe zY3`eoA&h1L8Mv*6!#m_OjEF|W1de)+k7lk;RpVK}2yXUnO@PC90ZBuQ+Cz?J%7{gH z`SIA{pa2_r*rk(LG0BtBO8NPz63B`l{Ne!uSD#FW9A6SJ!j^ z<3|=m)|R6`)GiIgGDHg_44|6E*dr>gb2|2b^U))1Nh~}C& z$XiGiv@`*tIbJYOmbvn{ij<2=gDP{CSGb9SP|{FZXUDkS(LAlr3N5UEKF$q_YYgM^ zBCa-K1d!Dh4Tcyxco$7`%&B1wYgohU4yjlWsN|PJZ6rkh<}BOB!cF9F2ebQNUIxdA zgkWPKrrbDq=|cQh2jp~4zIn3wS0EltQn5w|Z+-DJJQBNgy%>gkv;12WgLjAGf3lAb z#iP*g#hT;rA1{9Dx!7&Doj?cTjFRs--2?`YMMSaoOhgnJ7zr3Xj-mMc!Jw+*l>#(| zRozIc`c%N)u&Ns|JQq;rk9@qoFpT(!FZRTyA8irQjU&=W>0|VVjWdGn1j@!4!FIY; z_lIyTscKiW>UW~%HiwrF_)1??wd38q%&B2jkddAkHp@o9NigzJ@Y0U{{KZ@Q$VNo` z{dxPJ=a`7cbmiM)2NCg@lCQQY3VN#_8b?%=>fygRRLM6Hh^D{*0000aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3hSV=@dRCt{2+)-=WRulkWj}Q!dXnw#p^asZFT4;yZFW4U#WPe~= z3uDgWttu31*hBKL$FgQ@4;wZt4|#CFOD?4kdML$=J#?e9mCUfPgjp(KlVeHuOh=X! zOSUZC6IY8{U9h4k`gHC+I#Oj>ZV8C3RKNoM&#>jb&Q9G4Q{0ITz;AreJL168;8*U; zu@L$T98>RT8DL{Wx4*D@>c*ANKY_z`S+a~nhw-m}y=tUDa~1ZiPRnG~9Yxdo`arpL zTH_yf&5u*YSY!4(R)^JCwW;*e$;8X5t}>( z-qr1^&w6UDd%dDORLhdR=KjX-OKXR-VN%m#zh{M9Cc=qUz)J`D3KfzgN<;{{Q4(wA z0Gc?}aKeJFJ2;uK-0Lb8f<%S0R(PIJpb0>Jt3F=*|&P$5Ko+Qf+BW8A;Cd$WgQ1VI>SFDB%#zeX!Lu7Bp2oG@JXOHLF@ z>t4wX>-Bm=2tw5_Injtv^-E417A04}0v51#Ij zDyM)2Ea2sUe2E(I2^F?-hbk39?(iWMvfQCYg?FeBa);!~$-l^7hNEMF&N(l`?q~;h ztwmuYxKAz)feg1u?*de6M4*lls99|SaX&T|2R@}j$Q^10Ooos*#G{!O(OMWtA0y-r zWg_I-;z06yE65#Ih>(+OHFpRLbaWK(QbC0Z85L?&s8gXyg+RSty~q_Z{nDEHSpYRT zbV&a6A?Kr`c;7JtLhf*Z?pZyd!rK#&Pd=?W4Z;L;6xb%g0Y3HRoIi=204I0HvI0Mr z)&W*$!)R6+n1(mT+ojS{V$$N!!6?V02R4TQ@0U&@Phol+^o9_JL+~DI3pT=_wB#l# zY+llaxbgTHjgq_YWGu#sU0ONcHG|+mOQ{#j>Z^V?#>V2@SPFN|auD%gISLJDG7TK; zvcD1Ie2l%wHMy4X80&YVAID>Vj$Q+NGu^@OiudE^$Piq(6~J-=^wt1)6o)~?gT(sm zdDCVL`eWo=(vqOdYtPXVIi)hg^vyRp6h1%}7!O;Au1RYj$YAM}+)sB8;6X1t6osXe(d_ z$f!`hKt!=G6@-xZF2EZjBSpN(dn=6sFH`_Rb}kXEly&DHQK)q0b66?4N7QnUsOKJ0 zq;%#JamqF4bB-udI&+H1a*rrbI#DQ40dwlAz{w*Pkx?NzV)nGgVD_}mtE<`58jr}R zkQ^~{TH`QtTH`Qtb|%J{a*HDw4yiC}$Vr$nx4jL1ANedG4bJhKv%F1(+r)1N^XNDK zv$fF}i4Y_zq_l;DwgTS07ys1i-Sm;%mx(tfHJoG2%|6~@ad5Ss>O;$241Ju#|BPZ( zCb*A|@K)%3oRejD^SISBT&>dzpbUORdG|V%0Gc}(5$E}n(E>K;NP>}tb@X9&Ied4Q8j81bGv>EfcFB#fx|5w&~cW7NasoWoWCz2uz3)_7JA zLbw@pwTD^Vz@AGkwAb930e1C)x_FgSz`P)52A(#P&VaL^qm!U5Ie-14ZoVZEBL4Zf z_3vX!#J+atZeJ!u>}%}Pwnl<#?M>l`DJ_5a9|iT%w(Yo^82|tP07*qoM6N<$f{c;L A(f|Me diff --git a/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.8192.bytes.png b/tpl/gperftools/doc/tcmalloc-opspercpusec.vs.threads.8192.bytes.png deleted file mode 100644 index 3a99cbc8b1fa25b069a6cb0e5f701bfdae71d215..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1912 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 diff --git a/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.12.threads.png b/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.12.threads.png deleted file mode 100644 index d45458ac1a0280496a5b47a63d9deac91661c0bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2216 zcmV;Z2v_%sP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3kCP_p=RCt{2o6m0)Hx$QHAvK4p$*CvOz7t ztzwBVJN9S3&wk&Tv1fM2z_W~16be1R1Zuo(tZi_VphZkAUVbfE0n<`q$DR>G3Y_&YW1>p&9uKI%Q-LXbx zJOdwyzWf_Pe7%q*4m^)hM`#FE%7*|( zzTk)vO{1x^M$7HCD6oGuvm6TUe*P5+Udt?}gCRVeiQo(Y&Rk0@ce1v&cDn{1KaArX zPU0XeWtP(wv;Mtq2^`EUr-M(jHV^L*ij?K%@TL<|uX@!hNAPjI>evyyU9U1?TCg)eBlo0veObxdIB3yyq2 zVBRZ~99Gb`Z> z8uDQV4f!yGh7-#S8cr-TXh@`GzRG+-?h6WEkWQopq;EVH@Wfk@y>CTwNnl;Dc!S&* z6uuzo02@f2$~KTZmERdiUIT0(c@40xSeY-#4PbP}01A`hQ(v%S04eKOLCQJ_xNQI_ z2Utgya)1@29FTyIeL-#jDeKrlZUBV|6bA6R38eFDd7eW!;NO8J=TO$MdX=({9V8rJ z2MGt*`$#yz;qjU`C}=Rg=AiRc!x8hriH!Fp1302V+yOZ_#8v@mK^>5;0=B8idsE_5@3r^EC&dE zhB_7rv@Y4(E}FT6Dxo{T0P^@xmU`U**0og{tL^~9sB$${)&R_cOcCwXSpqJiU?w7x zzfUnRGM*i2Dz!HS29T-$dnl%{YA-*MKB8wwk%1&u%>gdgoH$m^0W?@`syQG5nORZf zvKq+kirO{6zM9KwP_U1IwQ&>}7?>5&G#8`Fks8Ct5~L@(X(~`~J)X^46zkT>=Q$%z zt^!c&q^kf44rl6$wa<0y6f-L=RHcYqwhneNP$2QvK`b=2VAi3*x?=6K%bXmw4!uUM zg@!oEwz$TvLq8>}E7q)&fInIkG3(Hy%CLA{K(z?{NMt(n8yf=JMl~H#?gr|L!b1_U z4%tWau3rbirifUF1oa6@b&qw{kroE^;K`egu2>!C1YET$3LL}~k@-8TgARSvtwW9~ zavb3VR*P<(CW@6Qi=$#PqLtTXBq(%x-$OAOQE$b{Ycp5HNS7@(Z@~dPFKY=bY=(fr{mm1Q51iC6qPp4iv6s$4a6TD zD|?+46GrI(1)@(l)kHcgree%fQAwn;Vk#yyu>RDVI83=a2My23)hNpz6rlO^ z^#y%@9Xu4q44@_JqbF$nDHsL%l>ZzUEz6-j+=FO#9^kr|0a)SyH)P_&_={hQ3`TEz z5Y5Ytot}U#xsigSA;8;%9*nxuAp93OL0e(~mj%Glx`cm|wLw?b$_@TAhXuKjf}vcB zjG`XEABzA##m>DZ_r8&;ukOg&d$P9wRC-Zvu7HJ zRfc*=*1p&Qn3o$9&p?KHDObEJd(oGn!a9TSIi!lqQt?r)cpw$K$1?s;8El5RVz>n< z7)r1cgK;01Wgo+=kE4UQk8c1Ldt8EfABS>O2fKSPoVp?>l{Y3Knv-fDE2BC%l%qO( zj=_o?)v2j`RK4?f3jROAv_OA_q=fUNH*#dGY% q_R949mxp_uXnNMUj}GPigZ}_aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3jSV=@dRCt{2o6T<9Mi9qU1gH;1?FWcqzeQspc6I0j^aWb@6$$~E&lNm`;LaDl z_9b_ZUeH>dzPQN)2kpA&2==c*(DLSHAG&%p*g1F(0𝔏F8Y-x7NAf@?Oo!riS1X zzlVQAd`0ocIPd^pgZP7oFR4_drKv|QI9F$ag`&zp2Xg?U>C58XYX%5gzt_V!{v>86 z@N_c_cGdunok6h@v>GR)5X?dHCT01@^8FC0Fn`Jo9uoq7dMXq2y#RYmPze z@h%0<-$*2P+H5xO6JT1$83Kfrq2y?fsU4EwP;xpD=1GqhlG`u4luQXmWtVm!><6Wb z?ZD_WCJ4{D;2SRZHy2EXeikUW;4T-`Hlz;J4qZC1&jqz<)q;Wx>e8wOb!pXty0mJ+ z0T+xsCa6oR9`w23YvP<0s7b3H)TC7pYSNm4QJtJT2qQS~Nzmtle;C22zUL9erz$R* z(tA9nzRf07rAJd%)x(ZyF6VJUQ90;yLFFuVVxV$_G=a*lJTXu?2AIHz3o11?IZ$vx zzj9EeIU}gj+~h!&=8T|9bCUyAVl^*yy~;tM2BQl)(ARi;!3A47P^~BfsL)&j?r=d} zNGh?KL51cL@K-JBHI1oX9_P=mL)AZS6K3o8HP8kD00wOUnZ&H|)0cQ#O8 zM_O|hS*2(6HlR>~@t8x;S7RG2E5TOW2RI&EaR$_zmRgT}dKHkEPlYixY!1a_J~6Vy zunNeoQG<$AKw_kH5^z@;P-3KX67c?nKysY30m*UBqIb~4mDP5)|Ixt>14tEFC5MuZ z6-Y_P3ZxW}fNd@a29T1D-Om;t7ZhC3uN?}Y+I(8r-oil;alnErHlXwhGAR!$) zkdTfgC`iy;M+KN3X+fV0lAGu?s2jnubdoyyMzE}aNrPqSnAcW`m8D~u)v|PKK|z8I zGx&f4BePc1l_Cec|4}`Hxp$Oz%x$w;lFr#c&kU9{XR(?~tfV=M)tm3DG(Jr5b9MxVukNbR{;q2=M5f<#iN;(Db(V~I+|IrKr)ts z6;*Bx7FBLFCs%^{HF9c2m7fjN?viEZ5#UWO2sWVLf<6J-GzSUVWVKjc0T`4%TGp|k za{H{Z!Gg*yXB{+X^G~Ow@&sJB0SjWa_eg+tbu6gdKC67tuN;&wZ~%kir-_5I%Buin zl{@|@zHDJzHoez5lpLB&W>eA&ayMUnaK!-xq#8? zjvhy)zBO1l{imzPQ3vsCL6N>?**UF2@o9^L5<}U*tJS<^*<)#wDDNrn`R0ras_#@w zigU+b$sl>7Jo%f|6Hf-UE~Rf+D`b^$N$2{g0P0*yQ-`QtXveZkK^#Fm_{iX?RI0I~ zqH+!jWG+Cdwo$z(xdNHn$CDdor=-<9dD>LQdQukoK{LDjK+08N!E%A>9nI9_s0*ur z=MIwx$+{Ix=4Gp5YR#YMvO&QG*^@xYTtF$UE}(QPf>WsZOPs*`nBmKLae7>pBe>uwJ+5GsjeO1`&&FCt?`S?KgLcn&p2kBi2gKAKH1A2P0LD|L z=_-H&u5&@|)4_dT+XSQy>&OIg0>T41_MZczRZQ(s7ovqlfLs1Nz={C4jSU~hH-0rd z7<}kJw1@}09Ur#wAO#1w7WweD1A{gSMEu-?XvG7#>f`EW1L0q=G-zWf9*Ca=Ea5>4 zhByulq7J}s4S*kG<=()v@6qcU?0?}SEbYBOE#iUqtp}q0Xb7HM9fn$eh}XrTUdOYy z(QA7hL%o8f`?z3T#DnRVfT3Q=JU+x)^e|M|@F08zsmE3H_$>4I7Cp9)G5*gz*a|a` z;WngTh+r!Q<2tTl9m8QA2XEs#z6WS@1cF%|`?7D;3Og;BUp=%2+mE4xV0&u?!QKzWUv)Je sFM0brYjcZV9(7yM+=6u-Ev$O*ABX<5^a%U>egFUf07*qoM6N<$g4lYn`Tzg` diff --git a/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.2.threads.png b/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.2.threads.png deleted file mode 100644 index 52d7aee49039dda55cd45704f04de7ec060406b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2163 zcmV-(2#oiMP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3j@kvBMRCt{2o6m0)Hx$P~QOltMjy*I={tTj$bSv>^v{n9uwn8O9 z)VLJ~ZsZTB+8isv0WLHVVPhdw<P=u<2$R-M5lFZJ$KF_bUJ$^G|)24;) zld_r_`!nC?=Qm#4vpE5tc47z)!T$`n8lT3`$Q@jUwjUTm`@_q-Paisit8jl~ac+Y< zgB;qOoiDnpUSP1j5jum{5Iop39IyaKrFVE9Gfvs^|-jwLC$i=-5C9a{%Kr=lR|r9w4+gTA}nm@!1JHS_*^J zd4PRqkk1CoGY6PAC*8ns2|?ze*jbc{?rmH-zx3snY$G^=n!TN1JoLvRQV!M6~MTb^JfjvI_yo6Loa zPyLIozA*_f0ApV;VxHhp%M(1j;0eC!HTR=>s0hkF&xZOs2p) zx81?D&{?QTy_Na(i36}uL8_kN^Nk`;6i#*|4^DB%dRt9#~puw69P}`_?T}=k4 zQPrzDfVy)vsZ)$OIA7JnQIk4(aAL%SUc?%K5rSMV;J0*WRGe=Tm_hS|5a`>vofN9$!w};_ILxuTM1|;r#$bMiFz=xYFAUx z`W*2}Fnv0NJD6C{TE}`f17x;xavVs&MR~MMl~;gCa9;eqAUO^Uz~|O-GRqUNBaiIw zwX4CzZduF80b~FYZ_-&UNc5OD$bCU^>>!n_wIG#qwIG#q$wc>*CzuuzAeD2qAeD2k z1y;6^%DMV&8U9azttwE^U@WRYI-_bpBIjNYEY?;wBXX`jszlDc9ymjS5t(yI8!K{k zn##EvFxdA51!9~8q3Y>aCvvVPsth9L1In&gx4V*ntin}@>^Y~c=4_<^pU|MFQiNR^ z)b~deOYUgja81B~0uv;jCP8VObTwYA1Z~^h>4OoyWAiniS;aQRv?JoB5emc!N5tuO z0qo^SDFW##0KxvaMN#A~_fMAg6pNXa&DFvO7Qbf&I|Qgm+Z7WlgM-m#AO3U!6=S85 zN8isWl?{AlHn7-1(oLwaF-m(D}yJWhl-Z9HM=mdNtk4jT(Z3q|US4hLC zLSTLqRIEER=c1UEO3Cif%v=a7-64#s#+{7fs(#ctqg(+^QkMw!^wP#{jEt}!(JVH|<9ZYpVoivkvpgVfv{MWkuFR(WsDs3+eS#A|LTh2Le6K%Zk*rKRPUd`OHeB zmv~muBgbG!$92VmK(*!Ss$1>2!Z^)h(*&x{n5>L);3seHBGD$bkyeU}}8yL`gTwEBS zb=_gHoU`|1M1S`b`}dBS1Lf{e!I-|yQ66qr#V8M}9c1JfQv6=XS@C001#)r+IEE2F zMcLyOVChwq>TLRg%Uu!R+i4OEs@~|a_^h9zfD0rTR=<3_&%6{7nWw<0>OE#L8V90gVhP}IQ(K8i_pjQDg1!@r+>K!JZ-K41Wz znG^YfvXOqE@C5^3F!BYlk)ELN1p{9&@&#eq7YuyC$QP8unIE|B3wpcy{{xTnpF`7C zfCrea0(`&)U(olzgOvqdW6Rp;3F17$5A?A&4vgn8w7XjnPfP+_6XO809N-3ad?_R+=2WwpcD|nECQ6J!Ny9-eV6NLZJgLsw!ToM3#iwOUOr9lTv@qmBNVG0jY zFg%jm?_Gf3aNxTxGxsW9eTAyeahet0#nK**YyuD1XAF4fZU!D)%3N*T!FtlwD|qz= zs&*F8)r(mAWDQ^v56(OSboFAccn4e2LswyuLHHO_#W_@bkSlJZVrL)yKh2;W=8EA8 zq+p2PvIJ!t=dg`o*2ZXCw(%7X;$4nl-o`#g^Q5$t_!*{7it&#@<~ p^J9}A?QSi{V-t@-qMc diff --git a/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.20.threads.png b/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.20.threads.png deleted file mode 100644 index da0328a6a7bf899533096001d79f9a2608a02660..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2147 zcmV-p2%PtcP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3j;Ymb6RCt{2+)Zy2M;HKLS5=!+lT%Mb$xrC5L?WpV{Q*5iTw3KX zv_d67)J!D~y^$Y)TK9%z4sd})m}G>i%D3i7D`O_es;EH4L=e_wz590d!|~3?J74*r ztuj)Q{d)4w%)YbUSvJ%lu+&cBDg3v<;U#!L`A_N#<8=}RLAXnz8(+-KZxBy7{FX%5 zKkawU`bLajk*Kvgd1k{KMj1>xb>ayJa}c%2%=B&Fh?a5+Pa(ioJrFLiK)Bfmg-bg% zuQE}>`>fBu$NVeJR;2>%lH(qeBBF%-_!1EI9*#Gpsn4^Hk+bY0K8RjcV4(r+X=Q4zqGUd>S&}INHKTu(rayT4(vQwMVMbxF7 zFgj|p0I$JR5O^#5MgkzqzR>_U)*@mmlJ z#LvgLsyo_W&JdSQSR!K3T2*vlS$Y*8Shm|vbYR&a`uSml=!MRDhfG2t4TUTeVkh$W zAeI4Eh-H8kVi{nCSO(aqNF)@}P{=|dewvO8A{k(ZNCwy;k^y#zWPrmQnS??b3Rx&b zuhcfEKam+s-w%ZF&OYkoU`kQgHb-1SA($i91EGH2lEYujkfM;9Av=1= z%uu^0hgU-(zI$}cQA2a)=(F4*SKk5DURhn*h(j<#?IrneFuUd;_p5m!7;I&~H4r8y zNDXj^%mOf2%mUi@&3s-M77-2su~~o_Zf8pdh)mdPaPzpJZX_QLT1JO;Q%2IQe&IuRN%wn5EEK4z#H$!={# zV6kL^ngMw@ShqpVfXv&-jvkJBg?dL^GD3WAHTp+9JuH@xr(`7m!F*v{5(F;e5%YgN z)$PpNi05ilc8@oNaxAI?(gOQs_vj&*M+XDlfOL4kz83=XLUaQ%hr}2$D+r|Kh!lkk zg|z&sj^rHn%@G+2BNP&4L4bLs%=1Sa>Th%pj;C6W6?C zQV}cAWf~~woViyk5#^k7o(Ae5e%C?9i*n9cK6EOGh}&}5K%fvs2W=7a1E>noHVP4$ zCL-7(Qgy!(3a7va3e}Wy&In_4#b;9H1BKcp%09m|KYlctnikHJ7(Z3CO8qgj87bfa0$ZbIu5fuqNi5V?^c$iDA_-V!o5QLiytD z48_Bwd@w=zb4EDAF=Br5U%5j0vvf}fjZdx1TK}bky65C_#MeYuRnr6Z^322yZB^A- zfHF<3-0hCv2r~C-k#5yDlp_OFx71Z=ns;P{=HDx`57T*6w^UNVF>}g19TF$VRNwH9 zSe}}v)eR`5svn<_F??o>I2vMgO++Ptuz?gme$znt6=`kRC7}GZg)%20)fHC!7}RRT zBc5vK+;WJR5UACrBq+Vdr(A#!HhwNa^UKwha-T?0?4AT)Ta%#ijb&0<-jy>)=pMrx z(`j9T_-l?*%5TfL+N|s}z1+R4B)Kx*KPbYbE3L~gy`?`=E^TF4?T_TXlt^M<4b<_t z-^?2NtZ7NW_jLog*=$nvP$I1T>Uik@`4ZG~hhlNPSV7>u+@T_R+>#wp#GpH?ys%Ix z6dxE#E6z~pR=>mO^1vS@43% z`9KKCq7FL!jpyPL4TG@v@#5m*d0`e1@K>e9EFc6nLSg7%2M?Ly6ddyvk}16*aZ04o&W8b9%I_Ttwvf#hu$(s{nH-DR-F7YdjR0p7moLek+0 zLVxN*x);B!rbAHDY z-{E`v{8GjE0{Myn?c6Eh;9^op#=)8JLT!1q4kleu}mF!>00RWFPp z-sY|7^Qyu+f%q{L5m$M{2cw8Dc*G8$nqT~gz-By(7;ixVV-8yx%-Xohvj0P)Xk+ps zYvUV$U|c7rcyo&%fUkQ?YT)7%V+_~kO`_1Vn)*B5#>VDi$OGhDjIN6X{C*=F3mo&s4` zD~rHK3-E-);X>SP!R+cwXV|$1T@E{&D;)N}(@fE@h-b-@_S($+XS=;tIy2|oMu&3p Z@E;<6pkY}U^P>O&002ovPDHLkV1hVe1f~E0 diff --git a/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.3.threads.png b/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.3.threads.png deleted file mode 100644 index 1093e81ca6b7c9507b2c79497928fe39c6921275..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2270 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 AO8@`> diff --git a/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.5.threads.png b/tpl/gperftools/doc/tcmalloc-opspersec.vs.size.5.threads.png deleted file mode 100644 index 779eec60bc04f7e23ebb4804f66c0bb72ca5b5e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1995 zcmV;+2Q>JJP)aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3jNl8RORCt{2+dXd-M;HKLM<}5J$`rBepO6wOwzCDl0nsG#Um!xl z1}mOO6cmy04~UX0QbaNZG{lJGJVFS$ydnj$j$?361PO!>%Q0trZ|BX-e%*e|=ZkOx zx{)|9H~aXVnSJ+ThGhbfWnv7+@P7lZ@l)b6@`n^!ICvdeUoGuEf9MTExWBc&vPBWF z)p>KVyXg&^0uFYzLT|V!;K7^c*S(>+jNuppYy?O+&ycXyz`})R6JF(H3h%Q%$P*WU z%fHHo4u}OOx6pA{I^sNLZ;lHt112AU{1z_g)|%w6=m!wm)vO7d*Nf4mMW- z_Prrp9yAvZMBaSj2gADp5=6w#x{T=G$Bo&$-}Pi4!4sCs#|io57r2z;{VQq8dBdw| z%K5_St8vO5t*x!y^n$RQrkrm?SWZ*UAC6OQ499Q`$M7SBJV3&T9XI5`Gved;$^Wpa zA4~##kdR|xM3C?(K*GZ_Nci_I5;k~%f}DV6ZOZ}=0S-`5KCQd7wFVmiPHnA1(M7km zR$;P0IqeVk;?EvSsytvj{v3WOK3;jjAo+`22Km$n4%4r1`9|DJzh>gkKR&QstRwC) z42pHs9hO_i+~HrPI^*R_LVhCK?NO<2PX$U5A7^m3RHs`XAHik{hvhosg|Y;zZ6h|g znZkCZlG-4oFsM{ke~}Ij6_sZAFoC^lrDjEb1Ze^r)k@RxD8Tq~Q!4duDv-tSuwF|o z@LCLC)N7dq#--n=*Ba7%1b2wC)(GRwd7!9Obh`xOT>Vv1Ye@4ERNgR%15^$KHN))e zs2662poPz|@W>41HAnrtx=)O-DF+@Cu_@0WCDcEkWpJpO1yGGkW&xY}hj|^e&H|VX z>Sh7j6Dx*o{Vv)QD~31qPxrU?IgqcI;4pd@WFu0d}a#fcQc8qzP&=AdVQCpe6(C zP?G`g1kHWaq>jT{2g5?`y|hRRA<2ZgzD#j^4v;UIMu;qOCNC>3Ayt1 zoER<|AuQ=2H|HvVYE8)rs@9a8plVGi?xXqa0Oe!1+e)BfoyKtqu|7O8Bv!avt1V6g z4TXQMgv<)zMn>*N6zG6gg|laETJ-ZDonl*+KW!^*IhSIJDJ zveXj8A6#JNmhSP-77Po^%Qo(ZqjH0FAJxjRdcg9qdcg9qdh{`wo|GWC!P1I}E0nb_ ztI+b)k(d2iL}G`sf29?Z7&h!MzH$&mM5=;LeWXx6kglUjWHm#;+AfZeXD`?JN0h@_ zJh2>MLW(Drdmm}}HsA;caXkX2g?Ph$(oKqqDBH=rA*=1<(MMX!07pn6@27u6C}n^n zBoK%5hqCZe2Drf-5KKflLevvR*&YlPv9yyL471Ns26#psWq=z@>bO75%P0ffAWMFb zLI!w79A$uQ)jBCO4@|4j^=%negZU)VLh1}$V4>u< z^9&m29ob>$g@q5_0zcylNoDICG9uh3Zm@8xVabnGWJI|ZA_$z6)aUzF1H@!kW8a0*Q((J=j(R+nH9#L>FrNMcrmc?&t zSMqeCx0ZMGuO#>`bfE#ixM!xv9NFX}w0-qBb?Jk22v5~-N z2ynR5g{UJEg#OY8zDxivGl0EyfqxgZgN~>b8}w%i^I{``p_r#dybJK>BEWT-xmU%v zZ$;Es;xQ(?BWnAPMJ|dB@+|?{xs$?!%V|@C+hRZ2)GOlK8zO3_A)0zg)NZ!{=ETP7 zCm@=7DT{bp^rA1C3hM;Ir;tQk5fL9`5qCty&c0~>DFUr9ix_S|0z(0t63RZVh(3mC zAEO=F$F~5BT`FMK$DtV2gJ(S$PA-Xu${S~Z&x&ZD6-M=7SB&b+SputKR3|61QAHoe zG5k1TeA#O^#6~aBOdMAa-SPb7^mGATusEAaG%HV`yw;c4cfJGazqnAWUg)b$B2$E;KGO zGA(CkIctq$djJ3j>PbXFRCt{2o6m0?H5A8-Dv?v0KY%Ft2e>z)DSPBEsJMXg2XLUM zQqwjfMnb5!=EwoT9+9?(_EHixO$@5g9ysKLO10fO(NYy!HJhlUyUFa%oi~T3LET zLlir^m%8mBaIo_z@&?-o4tJm4@CNPW1Uvx&HX4E81r`XdH$%bsty5lQA_ecWKECn` zFXo5b@ByUD4Z*)kFHL$A#ZYj%5eQ0M#|C;74=_G^p1yh(0K)pCM#SwGHhY1GtI?pn z46yGF(#65r!Vu%;Ek7_?MUVt2c2>Be{}?yUuYT6!V+2pIQr=83o_Z%NuJdOf-~1e$Q`y?ty^9oET$pns|br}$oYdOA$J1)f5AL!XP)4G)_mI&{3B~V?Fs(g zNa~|^C;Y%px~J)};fxp9I7sS9Z8ZiaMg?Ptg{?#W^5ZA&-~rzhLHYt;xr5JAF(C^t z+(8Cnc*CUOFL#hD^)gG7RLlt!dDHH{Y|CQ zwhWc#k?J}fz#XD=9yoz7)lQpR3s86OMKwYL-qQ3oT|nKv7mHi%9|0D(+CfX0+rd#N zXmM)|NJBvu3O)@54@1Fya@>mMiLA>y$gzVo6lBK^8ZYG|1C47y9cWwwI2fM}1mU3> zG=4@3aP0^{nSl6#UY1hAGNxy&4RfqP#Y8 zOu1=8>vycdAhm#e&1slGW&zi@4+6ol5j2OQ07s22;sYkTfOfL) zFut`>5p19#%FBBze^6{3OrRmk1$gU7Ktq(*gN9(OzdCFnSV4LO;EEMwX3*{&V>)tV zpgzh4*a-zK7J(_s9YB4s9wiu)P>=?K~Mh&Q2TE!Td5lpAB=?%g>82W0!D7ka43A(zy zRU)Ygx*D%W$<2CA(A7_ekfeHW!3Yl0?+2I`jP0??CDov-0kf|p)Sz=vc|_Fb;l*h8UB6k39|GY^#-E=iCvMGhgy&tLD9nl?oTbqY7~hkzY<{Iu9(3pIH*x% zphj!%Ra$fJk(oeaqMMwgL~E8=#WqFmRykU;JZDTzyMVMvi$JmqKyWmpQ_LoTsPmC$ zR(ey@!a$jiJhRe*W&4^MP?VZ)4CH=0IIfeaGs9Ad7ZT*{|d!SeOMpn7?FDLRAlvKm#7-4&%bGJ}y+FMHWbu^a);iuq%tL5i+m zWm)BBvQWj+IaaXJM5`wFDVDtDuE-Bc5#Xoj3bOJ6A75NK^m$y0xPi2M5LH_|6ujk#p1necdaEwaRQM+^L_GaQ9#guQvNPHN>x*9 zb^&Q&>Hp%1AdPdR)K(afpEa<6x-uwL1gP70NQ!Z#&2t`$1i}W&w{i)Tp2G50oOA%? z7RD7XO@=APusc!v69%~@JswWX2Pg`1vIb<*aqi_00u^D^Q?a;O8lH+ItF-MPHNL}v zry|QLal;A{r6n($p}E|ipJL(Gm=vJ>;ZeCP7vRoqsZaCWT4BShlBtKZR)^)a(L!mA zlCjnYE%`G9#bcU$| z{DwKx4Ia7IvGp~ozQB*>=q~p5ae-r4As-W zyS)i8hn2IB0ZqM}Dc-?R^wCsUArL)*L~#iff65ehP_cvS#+5S!)}u@@+JFR%5M1LR zAL9~^F-pfc-r-|>4Y1Iq2xem(p;w1nJs3@0#Vr5kB*f=Y?d_6RhkNMN*>ePz(W_Ha znOB47@dSJ`L9y&@HnAd7Y~l=#kbyq4Z3YaAA1#nxpH0tQy4?F7&R(5$2IueL)$-&e zTaUWm#6T8QV-4Wr3$Tsg=t9(8gPEmWXRvb*x(IgG7ZL1#uGtfq6wi_E_VV=H`+L2$ iczV`(j1J}a!M_32oUZ3qtF3SV0000 - - - -TCMalloc : Thread-Caching Malloc - - - - - -

TCMalloc : Thread-Caching Malloc

- -
Sanjay Ghemawat
- -

Motivation

- -

TCMalloc is faster than the glibc 2.3 malloc (available as a -separate library called ptmalloc2) and other mallocs that I have -tested. ptmalloc2 takes approximately 300 nanoseconds to execute a -malloc/free pair on a 2.8 GHz P4 (for small objects). The TCMalloc -implementation takes approximately 50 nanoseconds for the same -operation pair. Speed is important for a malloc implementation -because if malloc is not fast enough, application writers are inclined -to write their own custom free lists on top of malloc. This can lead -to extra complexity, and more memory usage unless the application -writer is very careful to appropriately size the free lists and -scavenge idle objects out of the free list.

- -

TCMalloc also reduces lock contention for multi-threaded programs. -For small objects, there is virtually zero contention. For large -objects, TCMalloc tries to use fine grained and efficient spinlocks. -ptmalloc2 also reduces lock contention by using per-thread arenas but -there is a big problem with ptmalloc2's use of per-thread arenas. In -ptmalloc2 memory can never move from one arena to another. This can -lead to huge amounts of wasted space. For example, in one Google -application, the first phase would allocate approximately 300MB of -memory for its URL canonicalization data structures. When the first -phase finished, a second phase would be started in the same address -space. If this second phase was assigned a different arena than the -one used by the first phase, this phase would not reuse any of the -memory left after the first phase and would add another 300MB to the -address space. Similar memory blowup problems were also noticed in -other applications.

- -

Another benefit of TCMalloc is space-efficient representation of -small objects. For example, N 8-byte objects can be allocated while -using space approximately 8N * 1.01 bytes. I.e., a -one-percent space overhead. ptmalloc2 uses a four-byte header for -each object and (I think) rounds up the size to a multiple of 8 bytes -and ends up using 16N bytes.

- - -

Usage

- -

To use TCMalloc, just link TCMalloc into your application via the -"-ltcmalloc" linker flag.

- -

You can use TCMalloc in applications you didn't compile yourself, -by using LD_PRELOAD:

-
-   $ LD_PRELOAD="/usr/lib/libtcmalloc.so" 
-
-

LD_PRELOAD is tricky, and we don't necessarily recommend this mode -of usage.

- -

TCMalloc includes a heap checker -and heap profiler as well.

- -

If you'd rather link in a version of TCMalloc that does not include -the heap profiler and checker (perhaps to reduce binary size for a -static binary), you can link in libtcmalloc_minimal -instead.

- - -

Overview

- -

TCMalloc assigns each thread a thread-local cache. Small -allocations are satisfied from the thread-local cache. Objects are -moved from central data structures into a thread-local cache as -needed, and periodic garbage collections are used to migrate memory -back from a thread-local cache into the central data structures.

-
- -

TCMalloc treats objects with size <= 256K ("small" objects) -differently from larger objects. Large objects are allocated directly -from the central heap using a page-level allocator (a page is a 8K -aligned region of memory). I.e., a large object is always -page-aligned and occupies an integral number of pages.

- -

A run of pages can be carved up into a sequence of small objects, -each equally sized. For example a run of one page (4K) can be carved -up into 32 objects of size 128 bytes each.

- - -

Small Object Allocation

- -

Each small object size maps to one of approximately 88 allocatable -size-classes. For example, all allocations in the range 961 to 1024 -bytes are rounded up to 1024. The size-classes are spaced so that -small sizes are separated by 8 bytes, larger sizes by 16 bytes, even -larger sizes by 32 bytes, and so forth. The maximal spacing is -controlled so that not too much space is wasted when an allocation -request falls just past the end of a size class and has to be rounded -up to the next class.

- -

A thread cache contains a singly linked list of free objects per -size-class.

-
- -

When allocating a small object: (1) We map its size to the -corresponding size-class. (2) Look in the corresponding free list in -the thread cache for the current thread. (3) If the free list is not -empty, we remove the first object from the list and return it. When -following this fast path, TCMalloc acquires no locks at all. This -helps speed-up allocation significantly because a lock/unlock pair -takes approximately 100 nanoseconds on a 2.8 GHz Xeon.

- -

If the free list is empty: (1) We fetch a bunch of objects from a -central free list for this size-class (the central free list is shared -by all threads). (2) Place them in the thread-local free list. (3) -Return one of the newly fetched objects to the applications.

- -

If the central free list is also empty: (1) We allocate a run of -pages from the central page allocator. (2) Split the run into a set -of objects of this size-class. (3) Place the new objects on the -central free list. (4) As before, move some of these objects to the -thread-local free list.

- -

- Sizing Thread Cache Free Lists

- -

It is important to size the thread cache free lists correctly. If -the free list is too small, we'll need to go to the central free list -too often. If the free list is too big, we'll waste memory as objects -sit idle in the free list.

- -

Note that the thread caches are just as important for deallocation -as they are for allocation. Without a cache, each deallocation would -require moving the memory to the central free list. Also, some threads -have asymmetric alloc/free behavior (e.g. producer and consumer threads), -so sizing the free list correctly gets trickier.

- -

To size the free lists appropriately, we use a slow-start algorithm -to determine the maximum length of each individual free list. As the -free list is used more frequently, its maximum length grows. However, -if a free list is used more for deallocation than allocation, its -maximum length will grow only up to a point where the whole list can -be efficiently moved to the central free list at once.

- -

The psuedo-code below illustrates this slow-start algorithm. Note -that num_objects_to_move is specific to each size class. -By moving a list of objects with a well-known length, the central -cache can efficiently pass these lists between thread caches. If -a thread cache wants fewer than num_objects_to_move, -the operation on the central free list has linear time complexity. -The downside of always using num_objects_to_move as -the number of objects to transfer to and from the central cache is -that it wastes memory in threads that don't need all of those objects. - -

-Start each freelist max_length at 1.
-
-Allocation
-  if freelist empty {
-    fetch min(max_length, num_objects_to_move) from central list;
-    if max_length < num_objects_to_move {  // slow-start
-      max_length++;
-    } else {
-      max_length += num_objects_to_move;
-    }
-  }
-
-Deallocation
-  if length > max_length {
-    // Don't try to release num_objects_to_move if we don't have that many.
-    release min(max_length, num_objects_to_move) objects to central list
-    if max_length < num_objects_to_move {
-      // Slow-start up to num_objects_to_move.
-      max_length++;
-    } else if max_length > num_objects_to_move {
-      // If we consistently go over max_length, shrink max_length.
-      overages++;
-      if overages > kMaxOverages {
-        max_length -= num_objects_to_move;
-        overages = 0;
-      }
-    }
-  }
-
- -See also the section on Garbage Collection -to see how it affects the max_length. - -

Large Object Allocation

- -

A large object size (> 256K) is rounded up to a page size (8K) -and is handled by a central page heap. The central page heap is again -an array of free lists. For i < 128, the -kth entry is a free list of runs that consist of -k pages. The 128th entry is a free list of -runs that have length >= 128 pages:

-
- -

An allocation for k pages is satisfied by looking in -the kth free list. If that free list is empty, we look -in the next free list, and so forth. Eventually, we look in the last -free list if necessary. If that fails, we fetch memory from the -system (using sbrk, mmap, or by mapping in -portions of /dev/mem).

- -

If an allocation for k pages is satisfied by a run -of pages of length > k, the remainder of the -run is re-inserted back into the appropriate free list in the -page heap.

- - -

Spans

- -

The heap managed by TCMalloc consists of a set of pages. A run of -contiguous pages is represented by a Span object. A span -can either be allocated, or free. If free, the span -is one of the entries in a page heap linked-list. If allocated, it is -either a large object that has been handed off to the application, or -a run of pages that have been split up into a sequence of small -objects. If split into small objects, the size-class of the objects -is recorded in the span.

- -

A central array indexed by page number can be used to find the span to -which a page belongs. For example, span a below occupies 2 -pages, span b occupies 1 page, span c occupies 5 -pages and span d occupies 3 pages.

-
- -

In a 32-bit address space, the central array is represented by a a -2-level radix tree where the root contains 32 entries and each leaf -contains 2^14 entries (a 32-bit address space has 2^19 8K pages, and -the first level of tree divides the 2^19 pages by 2^5). This leads to -a starting memory usage of 64KB of space (2^14*4 bytes) for the -central array, which seems acceptable.

- -

On 64-bit machines, we use a 3-level radix tree.

- - -

Deallocation

- -

When an object is deallocated, we compute its page number and look -it up in the central array to find the corresponding span object. The -span tells us whether or not the object is small, and its size-class -if it is small. If the object is small, we insert it into the -appropriate free list in the current thread's thread cache. If the -thread cache now exceeds a predetermined size (2MB by default), we run -a garbage collector that moves unused objects from the thread cache -into central free lists.

- -

If the object is large, the span tells us the range of pages covered -by the object. Suppose this range is [p,q]. We also -lookup the spans for pages p-1 and q+1. If -either of these neighboring spans are free, we coalesce them with the -[p,q] span. The resulting span is inserted into the -appropriate free list in the page heap.

- - -

Central Free Lists for Small Objects

- -

As mentioned before, we keep a central free list for each -size-class. Each central free list is organized as a two-level data -structure: a set of spans, and a linked list of free objects per -span.

- -

An object is allocated from a central free list by removing the -first entry from the linked list of some span. (If all spans have -empty linked lists, a suitably sized span is first allocated from the -central page heap.)

- -

An object is returned to a central free list by adding it to the -linked list of its containing span. If the linked list length now -equals the total number of small objects in the span, this span is now -completely free and is returned to the page heap.

- - -

Garbage Collection of Thread Caches

- -

Garbage collecting objects from a thread cache keeps the size of -the cache under control and returns unused objects to the central free -lists. Some threads need large caches to perform well while others -can get by with little or no cache at all. When a thread cache goes -over its max_size, garbage collection kicks in and then the -thread competes with the other threads for a larger cache.

- -

Garbage collection is run only during a deallocation. We walk over -all free lists in the cache and move some number of objects from the -free list to the corresponding central list.

- -

The number of objects to be moved from a free list is determined -using a per-list low-water-mark L. L -records the minimum length of the list since the last garbage -collection. Note that we could have shortened the list by -L objects at the last garbage collection without -requiring any extra accesses to the central list. We use this past -history as a predictor of future accesses and move L/2 -objects from the thread cache free list to the corresponding central -free list. This algorithm has the nice property that if a thread -stops using a particular size, all objects of that size will quickly -move from the thread cache to the central free list where they can be -used by other threads.

- -

If a thread consistently deallocates more objects of a certain size -than it allocates, this L/2 behavior will cause at least -L/2 objects to always sit in the free list. To avoid -wasting memory this way, we shrink the maximum length of the freelist -to converge on num_objects_to_move (see also -Sizing Thread Cache Free Lists). - -

-Garbage Collection
-  if (L != 0 && max_length > num_objects_to_move) {
-    max_length = max(max_length - num_objects_to_move, num_objects_to_move)
-  }
-
- -

The fact that the thread cache went over its max_size is -an indication that the thread would benefit from a larger cache. Simply -increasing max_size would use an inordinate amount of memory -in programs that have lots of active threads. Developers can bound the -memory used with the flag --tcmalloc_max_total_thread_cache_bytes.

- -

Each thread cache starts with a small max_size -(e.g. 64KB) so that idle threads won't pre-allocate memory they don't -need. Each time the cache runs a garbage collection, it will also try -to grow its max_size. If the sum of the thread cache -sizes is less than --tcmalloc_max_total_thread_cache_bytes, -max_size grows easily. If not, thread cache 1 will try -to steal from thread cache 2 (picked round-robin) by decreasing thread -cache 2's max_size. In this way, threads that are more -active will steal memory from other threads more often than they are -have memory stolen from themselves. Mostly idle threads end up with -small caches and active threads end up with big caches. Note that -this stealing can cause the sum of the thread cache sizes to be -greater than --tcmalloc_max_total_thread_cache_bytes until thread -cache 2 deallocates some memory to trigger a garbage collection.

- -

Performance Notes

- -

PTMalloc2 unittest

- -

The PTMalloc2 package (now part of glibc) contains a unittest -program t-test1.c. This forks a number of threads and -performs a series of allocations and deallocations in each thread; the -threads do not communicate other than by synchronization in the memory -allocator.

- -

t-test1 (included in -tests/tcmalloc/, and compiled as -ptmalloc_unittest1) was run with a varying numbers of -threads (1-20) and maximum allocation sizes (64 bytes - -32Kbytes). These tests were run on a 2.4GHz dual Xeon system with -hyper-threading enabled, using Linux glibc-2.3.2 from RedHat 9, with -one million operations per thread in each test. In each case, the test -was run once normally, and once with -LD_PRELOAD=libtcmalloc.so. - -

The graphs below show the performance of TCMalloc vs PTMalloc2 for -several different metrics. Firstly, total operations (millions) per -elapsed second vs max allocation size, for varying numbers of -threads. The raw data used to generate these graphs (the output of the -time utility) is available in -t-test1.times.txt.

- - - - - - - - - - - - - - - - - -
- - -
    -
  • TCMalloc is much more consistently scalable than PTMalloc2 - for - all thread counts >1 it achieves ~7-9 million ops/sec for small - allocations, falling to ~2 million ops/sec for larger - allocations. The single-thread case is an obvious outlier, - since it is only able to keep a single processor busy and hence - can achieve fewer ops/sec. PTMalloc2 has a much higher variance - on operations/sec - peaking somewhere around 4 million ops/sec - for small allocations and falling to <1 million ops/sec for - larger allocations. - -
  • TCMalloc is faster than PTMalloc2 in the vast majority of - cases, and particularly for small allocations. Contention - between threads is less of a problem in TCMalloc. - -
  • TCMalloc's performance drops off as the allocation size - increases. This is because the per-thread cache is - garbage-collected when it hits a threshold (defaulting to - 2MB). With larger allocation sizes, fewer objects can be stored - in the cache before it is garbage-collected. - -
  • There is a noticeable drop in TCMalloc's performance at ~32K - maximum allocation size; at larger sizes performance drops less - quickly. This is due to the 32K maximum size of objects in the - per-thread caches; for objects larger than this TCMalloc - allocates from the central page heap. -
- -

Next, operations (millions) per second of CPU time vs number of -threads, for max allocation size 64 bytes - 128 Kbytes.

- - - - - - - - - - - - - - - - - -
- -

Here we see again that TCMalloc is both more consistent and more -efficient than PTMalloc2. For max allocation sizes <32K, TCMalloc -typically achieves ~2-2.5 million ops per second of CPU time with a -large number of threads, whereas PTMalloc achieves generally 0.5-1 -million ops per second of CPU time, with a lot of cases achieving much -less than this figure. Above 32K max allocation size, TCMalloc drops -to 1-1.5 million ops per second of CPU time, and PTMalloc drops almost -to zero for large numbers of threads (i.e. with PTMalloc, lots of CPU -time is being burned spinning waiting for locks in the heavily -multi-threaded case).

- - -

Modifying Runtime Behavior

- -

You can more finely control the behavior of the tcmalloc via -environment variables.

- -

Generally useful flags:

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
TCMALLOC_SAMPLE_PARAMETERdefault: 0 - The approximate gap between sampling actions. That is, we - take one sample approximately once every - tcmalloc_sample_parmeter bytes of allocation. - This sampled heap information is available via - MallocExtension::GetHeapSample() or - MallocExtension::ReadStackTraces(). A reasonable - value is 524288. -
TCMALLOC_RELEASE_RATEdefault: 1.0 - Rate at which we release unused memory to the system, via - madvise(MADV_DONTNEED), on systems that support - it. Zero means we never release memory back to the system. - Increase this flag to return memory faster; decrease it - to return memory slower. Reasonable rates are in the - range [0,10]. -
TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLDdefault: 1073741824 - Allocations larger than this value cause a stack trace to be - dumped to stderr. The threshold for dumping stack traces is - increased by a factor of 1.125 every time we print a message so - that the threshold automatically goes up by a factor of ~1000 - every 60 messages. This bounds the amount of extra logging - generated by this flag. Default value of this flag is very large - and therefore you should see no extra logging unless the flag is - overridden. -
TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTESdefault: 16777216 - Bound on the total amount of bytes allocated to thread caches. This - bound is not strict, so it is possible for the cache to go over this - bound in certain circumstances. This value defaults to 16MB. For - applications with many threads, this may not be a large enough cache, - which can affect performance. If you suspect your application is not - scaling to many threads due to lock contention in TCMalloc, you can - try increasing this value. This may improve performance, at a cost - of extra memory use by TCMalloc. See - Garbage Collection for more details. -
- -

Advanced "tweaking" flags, that control more precisely how tcmalloc -tries to allocate memory from the kernel.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TCMALLOC_SKIP_MMAPdefault: false - If true, do not try to use mmap to obtain memory - from the kernel. -
TCMALLOC_SKIP_SBRKdefault: false - If true, do not try to use sbrk to obtain memory - from the kernel. -
TCMALLOC_DEVMEM_STARTdefault: 0 - Physical memory starting location in MB for /dev/mem - allocation. Setting this to 0 disables /dev/mem - allocation. -
TCMALLOC_DEVMEM_LIMITdefault: 0 - Physical memory limit location in MB for /dev/mem - allocation. Setting this to 0 means no limit. -
TCMALLOC_DEVMEM_DEVICEdefault: /dev/mem - Device to use for allocating unmanaged memory. -
TCMALLOC_MEMFS_MALLOC_PATHdefault: "" - If set, specify a path where hugetlbfs or tmpfs is mounted. - This may allow for speedier allocations. -
TCMALLOC_MEMFS_LIMIT_MBdefault: 0 - Limit total memfs allocation size to specified number of MB. - 0 means "no limit". -
TCMALLOC_MEMFS_ABORT_ON_FAILdefault: false - If true, abort() whenever memfs_malloc fails to satisfy an allocation. -
TCMALLOC_MEMFS_IGNORE_MMAP_FAILdefault: false - If true, ignore failures from mmap. -
TCMALLOC_MEMFS_MAP_PRVIATEdefault: false - If true, use MAP_PRIVATE when mapping via memfs, not MAP_SHARED. -
- - -

Modifying Behavior In Code

- -

The MallocExtension class, in -malloc_extension.h, provides a few knobs that you can -tweak in your program, to affect tcmalloc's behavior.

- -

Releasing Memory Back to the System

- -

By default, tcmalloc will release no-longer-used memory back to the -kernel gradually, over time. The tcmalloc_release_rate flag controls how quickly -this happens. You can also force a release at a given point in the -progam execution like so:

-
-   MallocExtension::instance()->ReleaseFreeMemory();
-
- -

You can also call SetMemoryReleaseRate() to change the -tcmalloc_release_rate value at runtime, or -GetMemoryReleaseRate to see what the current release rate -is.

- -

Memory Introspection

- -

There are several routines for getting a human-readable form of the -current memory usage:

-
-   MallocExtension::instance()->GetStats(buffer, buffer_length);
-   MallocExtension::instance()->GetHeapSample(&string);
-   MallocExtension::instance()->GetHeapGrowthStacks(&string);
-
- -

The last two create files in the same format as the heap-profiler, -and can be passed as data files to pprof. The first is human-readable -and is meant for debugging.

- -

Generic Tcmalloc Status

- -

TCMalloc has support for setting and retrieving arbitrary -'properties':

-
-   MallocExtension::instance()->SetNumericProperty(property_name, value);
-   MallocExtension::instance()->GetNumericProperty(property_name, &value);
-
- -

It is possible for an application to set and get these properties, -but the most useful is when a library sets the properties so the -application can read them. Here are the properties TCMalloc defines; -you can access them with a call like -MallocExtension::instance()->GetNumericProperty("generic.heap_size", -&value);:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
generic.current_allocated_bytes - Number of bytes used by the application. This will not typically - match the memory use reported by the OS, because it does not - include TCMalloc overhead or memory fragmentation. -
generic.heap_size - Bytes of system memory reserved by TCMalloc. -
tcmalloc.pageheap_free_bytes - Number of bytes in free, mapped pages in page heap. These bytes - can be used to fulfill allocation requests. They always count - towards virtual memory usage, and unless the underlying memory is - swapped out by the OS, they also count towards physical memory - usage. -
tcmalloc.pageheap_unmapped_bytes - Number of bytes in free, unmapped pages in page heap. These are - bytes that have been released back to the OS, possibly by one of - the MallocExtension "Release" calls. They can be used to fulfill - allocation requests, but typically incur a page fault. They - always count towards virtual memory usage, and depending on the - OS, typically do not count towards physical memory usage. -
tcmalloc.slack_bytes - Sum of pageheap_free_bytes and pageheap_unmapped_bytes. Provided - for backwards compatibility only. Do not use. -
tcmalloc.max_total_thread_cache_bytes - A limit to how much memory TCMalloc dedicates for small objects. - Higher numbers trade off more memory use for -- in some situations - -- improved efficiency. -
tcmalloc.current_total_thread_cache_bytes - A measure of some of the memory TCMalloc is using (for - small objects). -
- -

Caveats

- -

For some systems, TCMalloc may not work correctly with -applications that aren't linked against libpthread.so (or -the equivalent on your OS). It should work on Linux using glibc 2.3, -but other OS/libc combinations have not been tested.

- -

TCMalloc may be somewhat more memory hungry than other mallocs, -(but tends not to have the huge blowups that can happen with other -mallocs). In particular, at startup TCMalloc allocates approximately -240KB of internal memory.

- -

Don't try to load TCMalloc into a running binary (e.g., using JNI -in Java programs). The binary will have allocated some objects using -the system malloc, and may try to pass them to TCMalloc for -deallocation. TCMalloc will not be able to handle such objects.

- -
- -
Sanjay Ghemawat, Paul Menage
- - -Last modified: Sat Feb 24 13:11:38 PST 2007 (csilvers) - -
- - - diff --git a/tpl/gperftools/doc/threadheap.dot b/tpl/gperftools/doc/threadheap.dot deleted file mode 100644 index b2dba72..0000000 --- a/tpl/gperftools/doc/threadheap.dot +++ /dev/null @@ -1,21 +0,0 @@ -digraph ThreadHeap { -rankdir=LR -node [shape=box, width=0.3, height=0.3] -nodesep=.05 - -heap [shape=record, height=2, label="class 0|class 1|class 2|..."] -O0 [label=""] -O1 [label=""] -O2 [label=""] -O3 [label=""] -O4 [label=""] -O5 [label=""] -sep1 [shape=plaintext, label="..."] -sep2 [shape=plaintext, label="..."] -sep3 [shape=plaintext, label="..."] - -heap:f0 -> O0 -> O1 -> sep1 -heap:f1 -> O2 -> O3 -> sep2 -heap:f2 -> O4 -> O5 -> sep3 - -} diff --git a/tpl/gperftools/doc/threadheap.gif b/tpl/gperftools/doc/threadheap.gif deleted file mode 100644 index c43d0a31018143264bd5ab8b3d7e36a03e833ff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7571 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 diff --git a/tpl/gperftools/src/base/atomicops.h b/tpl/gperftools/src/base/atomicops.h index be038f3..dac95be 100644 --- a/tpl/gperftools/src/base/atomicops.h +++ b/tpl/gperftools/src/base/atomicops.h @@ -102,8 +102,14 @@ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) +#define CLANG_VERSION (__clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__) + #if defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__GNUC__) && GCC_VERSION >= 40700 #include "base/atomicops-internals-gcc.h" +#elif defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__clang__) && CLANG_VERSION >= 30400 +#include "base/atomicops-internals-gcc.h" #elif defined(__MACH__) && defined(__APPLE__) #include "base/atomicops-internals-macosx.h" #elif defined(__GNUC__) && defined(ARMV6) @@ -120,6 +126,8 @@ #include "base/atomicops-internals-mips.h" #elif defined(__GNUC__) && GCC_VERSION >= 40700 #include "base/atomicops-internals-gcc.h" +#elif defined(__clang__) && CLANG_VERSION >= 30400 +#include "base/atomicops-internals-gcc.h" #else #error You need to implement atomic operations for this architecture #endif diff --git a/tpl/gperftools/src/base/basictypes.h b/tpl/gperftools/src/base/basictypes.h index 997f12c..356b3d5 100644 --- a/tpl/gperftools/src/base/basictypes.h +++ b/tpl/gperftools/src/base/basictypes.h @@ -83,7 +83,7 @@ const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max ); const int8 kint8min = ( ( int8) 0x80); const int16 kint16min = ( ( int16) 0x8000); const int32 kint32min = ( ( int32) 0x80000000); -const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 ); +const int64 kint64min = ( (((uint64) kint32min) << 32) | 0 ); // Define the "portable" printf and scanf macros, if they're not // already there (via the inttypes.h we #included above, hopefully). @@ -117,6 +117,14 @@ const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 ); #define PRINTABLE_PTHREAD(pthreadt) pthreadt #endif +#if defined(__GNUC__) +#define PREDICT_TRUE(x) __builtin_expect(!!(x), 1) +#define PREDICT_FALSE(x) __builtin_expect(!!(x), 0) +#else +#define PREDICT_TRUE(x) (x) +#define PREDICT_FALSE(x) (x) +#endif + // A macro to disallow the evil copy constructor and operator= functions // This should be used in the private: declarations for a class #define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ @@ -192,6 +200,12 @@ struct CompileAssert { # define ATTRIBUTE_UNUSED #endif +#if defined(HAVE___ATTRIBUTE__) && defined(HAVE_TLS) +#define ATTR_INITIAL_EXEC __attribute__ ((tls_model ("initial-exec"))) +#else +#define ATTR_INITIAL_EXEC +#endif + #define COMPILE_ASSERT(expr, msg) \ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ATTRIBUTE_UNUSED @@ -223,6 +237,16 @@ inline Dest bit_cast(const Source& source) { return dest; } +// bit_store implements the equivalent of +// "dest = *reinterpret_cast(&source)". +// +// This prevents undefined behavior when the dest pointer is unaligned. +template +inline void bit_store(Dest *dest, const Source *source) { + COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), bitcasting_unequal_sizes); + memcpy(dest, source, sizeof(Dest)); +} + #ifdef HAVE___ATTRIBUTE__ # define ATTRIBUTE_WEAK __attribute__((weak)) # define ATTRIBUTE_NOINLINE __attribute__((noinline)) @@ -257,7 +281,7 @@ inline Dest bit_cast(const Source& source) { // ATTRIBUTE_SECTION are guaranteed to be between START and STOP. #if defined(HAVE___ATTRIBUTE__) && defined(__ELF__) -# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name))) +# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name))) __attribute__((noinline)) // Weak section declaration to be used as a global declaration // for ATTRIBUTE_SECTION_START|STOP(name) to compile and link @@ -357,12 +381,20 @@ class AssignAttributeStartEnd { # elif (defined(__aarch64__)) # define CACHELINE_ALIGNED __attribute__((aligned(64))) // implementation specific, Cortex-A53 and 57 should have 64 bytes +# elif (defined(__s390__)) +# define CACHELINE_ALIGNED __attribute__((aligned(256))) # else # error Could not determine cache line length - unknown architecture # endif #else # define CACHELINE_ALIGNED -#endif // defined(HAVE___ATTRIBUTE__) && (__i386__ || __x86_64__) +#endif // defined(HAVE___ATTRIBUTE__) + +#if defined(HAVE___ATTRIBUTE__ALIGNED_FN) +# define CACHELINE_ALIGNED_FN CACHELINE_ALIGNED +#else +# define CACHELINE_ALIGNED_FN +#endif // Structure for discovering alignment union MemoryAligner { @@ -371,6 +403,20 @@ union MemoryAligner { size_t s; } CACHELINE_ALIGNED; +#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__) +#define ATTRIBUTE_HIDDEN __attribute__((visibility("hidden"))) +#else +#define ATTRIBUTE_HIDDEN +#endif + +#if defined(__GNUC__) +#define ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define ATTRIBUTE_ALWAYS_INLINE +#endif + // The following enum should be used only as a constructor argument to indicate // that the variable has static storage class, and that the constructor should // do nothing to its state. It indicates to the reader that it is legal to diff --git a/tpl/gperftools/src/base/commandlineflags.h b/tpl/gperftools/src/base/commandlineflags.h index f54776a..49c904f 100644 --- a/tpl/gperftools/src/base/commandlineflags.h +++ b/tpl/gperftools/src/base/commandlineflags.h @@ -119,7 +119,16 @@ namespace tcmalloc { if (!value) { return def; } - return memchr("tTyY1\0", value[0], 6) != NULL; + switch (value[0]) { + case 't': + case 'T': + case 'y': + case 'Y': + case '1': + case '\0': + return true; + } + return false; } inline int StringToInt(const char *value, int def) { diff --git a/tpl/gperftools/src/base/elfcore.h b/tpl/gperftools/src/base/elfcore.h index d9599ed..8193d42 100644 --- a/tpl/gperftools/src/base/elfcore.h +++ b/tpl/gperftools/src/base/elfcore.h @@ -41,7 +41,7 @@ extern "C" { /* We currently only support x86-32, x86-64, ARM, MIPS, PPC on Linux. * Porting to other related platforms should not be difficult. */ -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ +#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ defined(__mips__) || defined(__PPC__)) && defined(__linux) #include @@ -89,7 +89,7 @@ extern "C" { uint16_t ss, __ss; #endif } i386_regs; -#elif defined(__ARM_ARCH_3__) +#elif defined(__arm__) typedef struct arm_regs { /* General purpose registers */ #define BP uregs[11] /* Frame pointer */ #define SP uregs[13] /* Stack pointer */ @@ -245,7 +245,7 @@ extern "C" { (f).uregs.gs_base = (r).gs_base; \ (r) = (f).uregs; \ } while (0) -#elif defined(__ARM_ARCH_3__) && defined(__GNUC__) +#elif defined(__arm__) && defined(__GNUC__) /* ARM calling conventions are a little more tricky. A little assembly * helps in obtaining an accurate snapshot of all registers. */ diff --git a/tpl/gperftools/src/base/linux_syscall_support.h b/tpl/gperftools/src/base/linux_syscall_support.h index 56b8fac..199061a 100644 --- a/tpl/gperftools/src/base/linux_syscall_support.h +++ b/tpl/gperftools/src/base/linux_syscall_support.h @@ -130,11 +130,14 @@ #ifndef SYS_LINUX_SYSCALL_SUPPORT_H #define SYS_LINUX_SYSCALL_SUPPORT_H -/* We currently only support x86-32, x86-64, ARM, MIPS, PPC/PPC64 and Aarch64 on Linux. +/* We currently only support x86-32, x86-64, ARM, MIPS, PPC/PPC64, Aarch64, s390 and s390x + * on Linux. * Porting to other related platforms should not be difficult. */ #if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ - defined(__mips__) || defined(__PPC__) || defined(__aarch64__)) && defined(__linux) + defined(__mips__) || defined(__PPC__) || \ + defined(__aarch64__) || defined(__s390__)) \ + && (defined(__linux)) #ifndef SYS_CPLUSPLUS #ifdef __cplusplus @@ -159,6 +162,7 @@ extern "C" { #include #include #include +#include #ifdef __mips__ /* Include definitions of the ABI currently in use. */ @@ -245,7 +249,8 @@ struct kernel_rusage { long ru_nivcsw; }; -#if defined(__i386__) || defined(__arm__) || defined(__PPC__) +#if defined(__i386__) || defined(__arm__) \ + || defined(__PPC__) || (defined(__s390__) && !defined(__s390x__)) /* include/asm-{arm,i386,mips,ppc}/signal.h */ struct kernel_old_sigaction { @@ -259,6 +264,8 @@ struct kernel_old_sigaction { } __attribute__((packed,aligned(4))); #elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) #define kernel_old_sigaction kernel_sigaction +#elif defined(__aarch64__) + // No kernel_old_sigaction defined for arm64. #endif /* Some kernel functions (e.g. sigaction() in 2.6.23) require that the @@ -302,9 +309,9 @@ struct kernel_sigaction { #endif }; -/* include/asm-{arm,i386,mips,ppc}/stat.h */ +/* include/asm-{arm,i386,mips,ppc,s390}/stat.h */ #ifdef __mips__ -#if _MIPS_SIM == _MIPS_SIM_ABI64 +#if (_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32) struct kernel_stat { #else struct kernel_stat64 { @@ -373,7 +380,7 @@ struct kernel_stat64 { }; #endif -/* include/asm-{arm,generic,i386,mips,x86_64,ppc}/stat.h */ +/* include/asm-{arm,generic,i386,mips,x86_64,ppc,s390}/stat.h */ #if defined(__i386__) || defined(__arm__) struct kernel_stat { /* The kernel headers suggest that st_dev and st_rdev should be 32bit @@ -443,7 +450,8 @@ struct kernel_stat { unsigned long __unused5; unsigned long __unused6; }; -#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +#elif defined(__mips__) \ + && !(_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32) struct kernel_stat { unsigned st_dev; int st_pad1[3]; @@ -489,6 +497,50 @@ struct kernel_stat { unsigned int __unused4; unsigned int __unused5; }; +#elif defined(__s390x__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad1; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long st_blksize; + long st_blocks; + unsigned long __unused[3]; +}; +#elif defined(__s390__) +struct kernel_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; #endif @@ -632,7 +684,7 @@ struct kernel_stat { #define __NR_getcpu (__NR_Linux + 312) #endif /* End of MIPS (old 32bit API) definitions */ -#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#elif (_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32) #ifndef __NR_gettid #define __NR_gettid (__NR_Linux + 178) #endif @@ -703,6 +755,189 @@ struct kernel_stat { #define __NR_fstatat 79 #endif /* End of aarch64 defininitions */ +#elif defined(__s390__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_rt_sigreturn +#define __NR_rt_sigreturn 173 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 174 +#endif +#ifndef __NR_rt_sigprocmask +#define __NR_rt_sigprocmask 175 +#endif +#ifndef __NR_rt_sigpending +#define __NR_rt_sigpending 176 +#endif +#ifndef __NR_rt_sigsuspend +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_readahead +#define __NR_readahead 222 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 224 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 225 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 227 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 228 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 230 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 231 +#endif +#ifndef __NR_gettid +#define __NR_gettid 236 +#endif +#ifndef __NR_tkill +#define __NR_tkill 237 +#endif +#ifndef __NR_futex +#define __NR_futex 238 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 239 +#endif +#ifndef __NR_sched_getaffinity +#define __NR_sched_getaffinity 240 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 252 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 260 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 261 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 265 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 266 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 282 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 283 +#endif +#ifndef __NR_openat +#define __NR_openat 288 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 294 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 310 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 311 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 314 +#endif +/* Some syscalls are named/numbered differently between s390 and s390x. */ +#ifdef __s390x__ +# ifndef __NR_getrlimit +# define __NR_getrlimit 191 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 208 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 209 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 210 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 211 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 215 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 216 +# endif +# ifndef __NR_fadvise64 +# define __NR_fadvise64 253 +# endif +# ifndef __NR_newfstatat +# define __NR_newfstatat 293 +# endif +#else /* __s390x__ */ +# ifndef __NR_getrlimit +# define __NR_getrlimit 76 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 138 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 139 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 164 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 165 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 170 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 171 +# endif +# ifndef __NR_ugetrlimit +# define __NR_ugetrlimit 191 +# endif +# ifndef __NR_mmap2 +# define __NR_mmap2 192 +# endif +# ifndef __NR_setresuid32 +# define __NR_setresuid32 208 +# endif +# ifndef __NR_getresuid32 +# define __NR_getresuid32 209 +# endif +# ifndef __NR_setresgid32 +# define __NR_setresgid32 210 +# endif +# ifndef __NR_getresgid32 +# define __NR_getresgid32 211 +# endif +# ifndef __NR_setfsuid32 +# define __NR_setfsuid32 215 +# endif +# ifndef __NR_setfsgid32 +# define __NR_setfsgid32 216 +# endif +# ifndef __NR_fadvise64_64 +# define __NR_fadvise64_64 264 +# endif +# ifndef __NR_fstatat64 +# define __NR_fstatat64 293 +# endif +#endif /* __s390__ */ +/* End of s390/s390x definitions */ #endif @@ -766,7 +1001,7 @@ struct kernel_stat { #undef LSS_RETURN #if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ - defined(__aarch64__)) + defined(__aarch64__) || defined(__s390__)) /* Failing system calls return a negative result in the range of * -1..-4095. These are "errno" values with the sign inverted. */ @@ -1250,7 +1485,7 @@ struct kernel_stat { "d"(LSS_SYSCALL_ARG(parent_tidptr)), "r"(LSS_SYSCALL_ARG(newtls)), "r"(LSS_SYSCALL_ARG(child_tidptr)) - : "rsp", "memory", "r8", "r10", "r11", "rcx"); + : "memory", "r8", "r10", "r11", "rcx"); } LSS_RETURN(int, __res); } @@ -1582,8 +1817,8 @@ struct kernel_stat { ".set reorder\n" \ : "=&r"(__v0), "+r" (__r7) \ : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ - "r"(__r6), "r" ((unsigned long)arg5), \ - "r" ((unsigned long)arg6) \ + "r"(__r6), "m" ((unsigned long)arg5), \ + "m" ((unsigned long)arg6) \ : MIPS_SYSCALL_CLOBBERS); \ LSS_RETURN(type, __v0, __r7); \ } @@ -1986,17 +2221,23 @@ struct kernel_stat { LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__x0)); \ } #undef _syscall2 - #define _syscall2(type, name, type1, arg1, type2, arg2) \ + #define _syscall2_long(type, name, svc, type1, arg1, type2, arg2) \ type LSS_NAME(name)(type1 arg1, type2 arg2) { \ LSS_REG(0, arg1); LSS_REG(1, arg2); \ - LSS_BODY(type, name, "r"(__x0), "r"(__x1)); \ + LSS_BODY(type, svc, "r"(__x0), "r"(__x1)); \ } + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + _syscall2_long(type, name, name, type1, arg1, type2, arg2) #undef _syscall3 - #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + #define _syscall3_long(type, name, svc, type1, arg1, type2, arg2, \ + type3, arg3) \ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ - LSS_BODY(type, name, "r"(__x0), "r"(__x1), "r"(__x2)); \ + LSS_BODY(type, svc, "r"(__x0), "r"(__x1), "r"(__x2)); \ } + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + _syscall3_long(type, name, name, type1, arg1, type2, arg2, \ + type3, arg3) #undef _syscall4 #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ @@ -2015,15 +2256,19 @@ struct kernel_stat { "r"(__x4)); \ } #undef _syscall6 - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ + #define _syscall6_long(type,name,svc,type1,arg1,type2,arg2,type3,arg3, \ + type4,arg4,type5,arg5,type6,arg6) \ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ type5 arg5, type6 arg6) { \ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ - LSS_BODY(type, name, "r"(__x0), "r"(__x1), "x"(__x2), "r"(__x3), \ + LSS_BODY(type, svc, "r"(__x0), "r"(__x1), "x"(__x2), "r"(__x3), \ "r"(__x4), "r"(__x5)); \ } + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + _syscall6_long(type,name,name,type1,arg1,type2,arg2,type3,arg3, \ + type4,arg4,type5,arg5,type6,arg6) /* clone function adapted from glibc 2.18 clone.S */ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, @@ -2083,16 +2328,160 @@ struct kernel_stat { } LSS_RETURN(int, __res); } + #elif defined(__s390__) + #undef LSS_REG + #define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a + #undef LSS_BODY + #define LSS_BODY(type, name, args...) \ + register unsigned long __nr __asm__("r1") \ + = (unsigned long)(__NR_##name); \ + register long __res_r2 __asm__("r2"); \ + long __res; \ + __asm__ __volatile__ \ + ("svc 0\n\t" \ + : "=d"(__res_r2) \ + : "d"(__nr), ## args \ + : "memory"); \ + __res = __res_r2; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(2, arg1); \ + LSS_BODY(type, name, "0"(__r2)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4)); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5)); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6)); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5, type6 arg6) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6), "d"(__r7)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret; + { + register int (*__fn)(void *) __asm__ ("r1") = fn; + register void *__cstack __asm__ ("r2") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void *__arg __asm__ ("r0") = arg; + register int *__ptidptr __asm__ ("r4") = parent_tidptr; + register void *__newtls __asm__ ("r6") = newtls; + register int *__ctidptr __asm__ ("r5") = child_tidptr; + __asm__ __volatile__ ( + #ifndef __s390x__ + /* arg already in r0 */ + "ltr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltr %0,%%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lr %%r2, %7\n\t" /* set first parameter to void *arg */ + "ahi %%r15, -96\n\t" /* make room on the stack for the save area */ + "xc 0(4,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #else + /* arg already in r0 */ + "ltgr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltgr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltgr %0, %%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lgr %%r2, %7\n\t" /* set first parameter to void *arg */ + "aghi %%r15, -160\n\t" /* make room on the stack for the save area */ + "xc 0(8,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #endif + : "=r" (__ret) + : "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit), + "d" (__fn), "d" (__cstack), "d" (__flags), "d" (__arg), + "d" (__ptidptr), "d" (__newtls), "d" (__ctidptr) + : "cc", "r14", "memory" + ); + } + LSS_RETURN(int, __ret); + } #endif #define __NR__exit __NR_exit #define __NR__gettid __NR_gettid #define __NR__mremap __NR_mremap LSS_INLINE _syscall1(int, close, int, f) LSS_INLINE _syscall1(int, _exit, int, e) +#if defined(__aarch64__) && defined (__ILP32__) + /* aarch64_ilp32 uses fcntl64 for sys_fcntl() */ + LSS_INLINE _syscall3_long(int, fcntl, fcntl64, int, f, + int, c, long, a) +#else LSS_INLINE _syscall3(int, fcntl, int, f, int, c, long, a) +#endif +#if defined(__aarch64__) && defined (__ILP32__) + /* aarch64_ilp32 uses fstat64 for sys_fstat() */ + LSS_INLINE _syscall2_long(int, fstat, fstat64, int, f, + struct kernel_stat*, b) +#else LSS_INLINE _syscall2(int, fstat, int, f, struct kernel_stat*, b) +#endif LSS_INLINE _syscall6(int, futex, int*, a, int, o, int, v, struct kernel_timespec*, t, @@ -2120,6 +2509,10 @@ struct kernel_stat { _LSS_BODY(3, off_t, lseek, off_t, LSS_SYSCALL_ARG(f), (uint64_t)(o), LSS_SYSCALL_ARG(w)); } + #elif defined(__aarch64__) && defined (__ILP32__) + /* aarch64_ilp32 uses llseek for sys_lseek() */ + LSS_INLINE _syscall3_long(off_t, lseek, llseek, int, f, + off_t, o, int, w) #else LSS_INLINE _syscall3(off_t, lseek, int, f, off_t, o, int, w) @@ -2165,18 +2558,11 @@ struct kernel_stat { LSS_INLINE _syscall3(int, socket, int, d, int, t, int, p) #endif - #if defined(__x86_64__) - /* Need to make sure __off64_t isn't truncated to 32-bits under x32. */ - LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, - __off64_t o) { - LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l), - LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f), - LSS_SYSCALL_ARG(d), (uint64_t)(o)); - } - + #if defined(__x86_64__) || defined(__s390x__) LSS_INLINE int LSS_NAME(sigaction)(int signum, const struct kernel_sigaction *act, struct kernel_sigaction *oldact) { + #if defined(__x86_64__) /* On x86_64, the kernel requires us to always set our own * SA_RESTORER in order to be able to return from a signal handler. * This function must have a "magic" signature that the "gdb" @@ -2188,10 +2574,10 @@ struct kernel_stat { a.sa_restorer = LSS_NAME(restore_rt)(); return LSS_NAME(rt_sigaction)(signum, &a, oldact, (KERNEL_NSIG+7)/8); - } else { + } else + #endif return LSS_NAME(rt_sigaction)(signum, act, oldact, (KERNEL_NSIG+7)/8); - } } LSS_INLINE int LSS_NAME(sigprocmask)(int how, @@ -2201,11 +2587,8 @@ struct kernel_stat { } #endif #if (defined(__aarch64__)) || \ - (defined(__mips__) && (_MIPS_ISA == _MIPS_ISA_MIPS64)) - LSS_INLINE _syscall6(void*, mmap, void*, s, - size_t, l, int, p, - int, f, int, d, - __off64_t, o) + (defined(__mips__) \ + && (_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32)) LSS_INLINE int LSS_NAME(sigaction)(int signum, const struct kernel_sigaction *act, struct kernel_sigaction *oldact) { @@ -2272,26 +2655,30 @@ struct kernel_stat { } } - #if defined(__i386__) || \ - defined(__arm__) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) + #if defined(__i386__) || \ + defined(__arm__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__PPC__) || \ + (defined(__s390__) && !defined(__s390x__)) #define __NR__sigaction __NR_sigaction #define __NR__sigprocmask __NR_sigprocmask LSS_INLINE _syscall2(int, fstat64, int, f, struct kernel_stat64 *, b) LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh) -#ifdef __PPC64__ - LSS_INLINE _syscall6(void*, mmap, void*, s, - size_t, l, int, p, - int, f, int, d, - off_t, o) -#else - #ifndef __ARM_EABI__ - /* Not available on ARM EABI Linux. */ - LSS_INLINE _syscall1(void*, mmap, void*, a) - #endif - LSS_INLINE _syscall6(void*, mmap2, void*, s, +#if defined(__s390__) && !defined(__s390x__) + /* On s390, mmap2() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(_mmap2)(void *s, size_t l, int p, int f, int d, + off_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap2, "0"(__r2)); + } +#elif !defined(__PPC64__) + #define __NR__mmap2 __NR_mmap2 + LSS_INLINE _syscall6(void*, _mmap2, void*, s, size_t, l, int, p, int, f, int, d, off_t, o) @@ -2384,16 +2771,58 @@ struct kernel_stat { return rc; } #endif + #if defined(__i386__) || \ + defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + (defined(__PPC__) && !defined(__PPC64__)) || \ + (defined(__s390__) && !defined(__s390x__)) + /* On these architectures, implement mmap() with mmap2(). */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + if (o % 4096) { + LSS_ERRNO = EINVAL; + return (void *) -1; + } + return LSS_NAME(_mmap2)(s, l, p, f, d, (o / 4096)); + } + #elif defined(__s390x__) + /* On s390x, mmap() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap, "0"(__r2)); + } + #elif defined(__x86_64__) + /* Need to make sure __off64_t isn't truncated to 32-bits under x32. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l), + LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f), + LSS_SYSCALL_ARG(d), (uint64_t)(o)); + } + #elif defined(__aarch64__) && defined (__ILP32__) + /* aarch64_ilp32 uses mmap2 for sys_mmap() */ + LSS_INLINE _syscall6_long(void*, mmap, mmap2, void*, addr, size_t, length, + int, prot, int, flags, int, fd, int64_t, offset) + #else + /* Remaining 64-bit architectures. */ + LSS_INLINE _syscall6(void*, mmap, void*, addr, size_t, length, int, prot, + int, flags, int, fd, int64_t, offset) + #endif #if defined(__i386__) || \ defined(__PPC__) || \ (defined(__arm__) && !defined(__ARM_EABI__)) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__s390__) /* See sys_socketcall in net/socket.c in kernel source. * It de-multiplexes on its first arg and unpacks the arglist * array in its second arg. */ - LSS_INLINE _syscall2(long, socketcall, int, c, unsigned long*, a) + LSS_INLINE _syscall2(int, socketcall, int, c, unsigned long*, a) LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { unsigned long args[3] = { diff --git a/tpl/gperftools/src/base/linuxthreads.h b/tpl/gperftools/src/base/linuxthreads.h index 16bc8c6..09ce45f 100644 --- a/tpl/gperftools/src/base/linuxthreads.h +++ b/tpl/gperftools/src/base/linuxthreads.h @@ -37,11 +37,12 @@ /* Include thread_lister.h to get the interface that we implement for linux. */ -/* We currently only support x86-32 and x86-64 on Linux. Porting to other +/* We currently only support certain platforms on Linux. Porting to other * related platforms should not be difficult. */ -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ - defined(__mips__) || defined(__PPC__) || defined(__aarch64__)) && defined(__linux) +#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__mips__) || defined(__PPC__) || defined(__aarch64__) || \ + defined(__s390__)) && defined(__linux) /* Define the THREADS symbol to make sure that there is exactly one core dumper * built into the library. diff --git a/tpl/gperftools/src/base/low_level_alloc.cc b/tpl/gperftools/src/base/low_level_alloc.cc index 4d2ae8d..6b467cf 100644 --- a/tpl/gperftools/src/base/low_level_alloc.cc +++ b/tpl/gperftools/src/base/low_level_alloc.cc @@ -57,6 +57,9 @@ // A first-fit allocator with amortized logarithmic free() time. +LowLevelAlloc::PagesAllocator::~PagesAllocator() { +} + // --------------------------------------------------------------------------- static const int kMaxLevel = 30; @@ -104,7 +107,7 @@ static int IntLog2(size_t size, size_t base) { // Return a random integer n: p(n)=1/(2**n) if 1 <= n; p(n)=0 if n < 1. static int Random() { - static int32 r = 1; // no locking---it's not critical + static uint32 r = 1; // no locking---it's not critical ANNOTATE_BENIGN_RACE(&r, "benign race, not critical."); int result = 1; while ((((r = r*1103515245 + 12345) >> 30) & 1) == 0) { @@ -196,6 +199,7 @@ struct LowLevelAlloc::Arena { // (init under mu, then ro) size_t min_size; // smallest allocation block size // (init under mu, then ro) + PagesAllocator *allocator; }; // The default arena, which is used when 0 is passed instead of an Arena @@ -208,6 +212,17 @@ static struct LowLevelAlloc::Arena default_arena; static struct LowLevelAlloc::Arena unhooked_arena; static struct LowLevelAlloc::Arena unhooked_async_sig_safe_arena; +namespace { + + class DefaultPagesAllocator : public LowLevelAlloc::PagesAllocator { + public: + virtual ~DefaultPagesAllocator() {}; + virtual void *MapPages(int32 flags, size_t size); + virtual void UnMapPages(int32 flags, void *addr, size_t size); + }; + +} + // magic numbers to identify allocated and unallocated blocks static const intptr_t kMagicAllocated = 0x4c833e95; static const intptr_t kMagicUnallocated = ~kMagicAllocated; @@ -289,12 +304,20 @@ static void ArenaInit(LowLevelAlloc::Arena *arena) { arena->flags = 0; // other arenas' flags may be overridden by client, // but unhooked_arena will have 0 in 'flags'. } + arena->allocator = LowLevelAlloc::GetDefaultPagesAllocator(); } } // L < meta_data_arena->mu LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32 flags, Arena *meta_data_arena) { + return NewArenaWithCustomAlloc(flags, meta_data_arena, NULL); +} + +// L < meta_data_arena->mu +LowLevelAlloc::Arena *LowLevelAlloc::NewArenaWithCustomAlloc(int32 flags, + Arena *meta_data_arena, + PagesAllocator *allocator) { RAW_CHECK(meta_data_arena != 0, "must pass a valid arena"); if (meta_data_arena == &default_arena) { if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { @@ -308,6 +331,9 @@ LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32 flags, new (AllocWithArena(sizeof (*result), meta_data_arena)) Arena(0); ArenaInit(result); result->flags = flags; + if (allocator) { + result->allocator = allocator; + } return result; } @@ -458,15 +484,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { // mmap generous 64K chunks to decrease // the chances/impact of fragmentation: size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16); - void *new_pages; - if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { - new_pages = MallocHook::UnhookedMMap(0, new_pages_size, - PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - } else { - new_pages = mmap(0, new_pages_size, - PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - } - RAW_CHECK(new_pages != MAP_FAILED, "mmap error"); + void *new_pages = arena->allocator->MapPages(arena->flags, new_pages_size); arena->mu.Lock(); s = reinterpret_cast(new_pages); s->header.size = new_pages_size; @@ -521,3 +539,44 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { LowLevelAlloc::Arena *LowLevelAlloc::DefaultArena() { return &default_arena; } + +static DefaultPagesAllocator *default_pages_allocator; +static union { + char chars[sizeof(DefaultPagesAllocator)]; + void *ptr; +} debug_pages_allocator_space; + +LowLevelAlloc::PagesAllocator *LowLevelAlloc::GetDefaultPagesAllocator(void) { + if (default_pages_allocator) { + return default_pages_allocator; + } + default_pages_allocator = new (debug_pages_allocator_space.chars) DefaultPagesAllocator(); + return default_pages_allocator; +} + +void *DefaultPagesAllocator::MapPages(int32 flags, size_t size) { + void *new_pages; + if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { + new_pages = MallocHook::UnhookedMMap(0, size, + PROT_WRITE|PROT_READ, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + } else { + new_pages = mmap(0, size, + PROT_WRITE|PROT_READ, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + } + RAW_CHECK(new_pages != MAP_FAILED, "mmap error"); + + return new_pages; +} + +void DefaultPagesAllocator::UnMapPages(int32 flags, void *region, size_t size) { + int munmap_result; + if ((flags & LowLevelAlloc::kAsyncSignalSafe) == 0) { + munmap_result = munmap(region, size); + } else { + munmap_result = MallocHook::UnhookedMUnmap(region, size); + } + RAW_CHECK(munmap_result == 0, + "LowLevelAlloc::DeleteArena: munmap failed address"); +} diff --git a/tpl/gperftools/src/base/low_level_alloc.h b/tpl/gperftools/src/base/low_level_alloc.h index 4081ff8..d8dfc8f 100644 --- a/tpl/gperftools/src/base/low_level_alloc.h +++ b/tpl/gperftools/src/base/low_level_alloc.h @@ -43,6 +43,15 @@ class LowLevelAlloc { public: + class PagesAllocator { + public: + virtual ~PagesAllocator(); + virtual void *MapPages(int32 flags, size_t size) = 0; + virtual void UnMapPages(int32 flags, void *addr, size_t size) = 0; + }; + + static PagesAllocator *GetDefaultPagesAllocator(void); + struct Arena; // an arena from which memory may be allocated // Returns a pointer to a block of at least "request" bytes @@ -90,6 +99,10 @@ class LowLevelAlloc { }; static Arena *NewArena(int32 flags, Arena *meta_data_arena); + // note: pages allocator will never be destroyed and allocated pages will never be freed + // When allocator is NULL, it's same as NewArena + static Arena *NewArenaWithCustomAlloc(int32 flags, Arena *meta_data_arena, PagesAllocator *allocator); + // Destroys an arena allocated by NewArena and returns true, // provided no allocated blocks remain in the arena. // If allocated blocks remain in the arena, does nothing and diff --git a/tpl/gperftools/src/base/spinlock.cc b/tpl/gperftools/src/base/spinlock.cc index 8b1f11f..85ff21e 100644 --- a/tpl/gperftools/src/base/spinlock.cc +++ b/tpl/gperftools/src/base/spinlock.cc @@ -64,6 +64,12 @@ struct SpinLock_InitHelper { // but nothing lock-intensive should be going on at that time. static SpinLock_InitHelper init_helper; +inline void SpinlockPause(void) { +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + __asm__ __volatile__("rep; nop" : : ); +#endif +} + } // unnamed namespace // Monitor the lock to see if its value changes within some time @@ -72,6 +78,7 @@ static SpinLock_InitHelper init_helper; Atomic32 SpinLock::SpinLoop() { int c = adaptive_spin_count; while (base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree && --c > 0) { + SpinlockPause(); } return base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree, kSpinLockSleeper); diff --git a/tpl/gperftools/src/base/spinlock_linux-inl.h b/tpl/gperftools/src/base/spinlock_linux-inl.h index aadf62a..627a28d 100644 --- a/tpl/gperftools/src/base/spinlock_linux-inl.h +++ b/tpl/gperftools/src/base/spinlock_linux-inl.h @@ -42,6 +42,9 @@ #define FUTEX_WAKE 1 #define FUTEX_PRIVATE_FLAG 128 +// Note: Instead of making direct system calls that are inlined, we rely +// on the syscall() function in glibc to do the right thing. + static bool have_futex; static int futex_private_flag = FUTEX_PRIVATE_FLAG; @@ -51,10 +54,10 @@ static struct InitModule { int x = 0; // futexes are ints, so we can use them only when // that's the same size as the lockword_ in SpinLock. - have_futex = (sizeof (Atomic32) == sizeof (int) && - sys_futex(&x, FUTEX_WAKE, 1, NULL, NULL, 0) >= 0); - if (have_futex && - sys_futex(&x, FUTEX_WAKE | futex_private_flag, 1, NULL, NULL, 0) < 0) { + have_futex = (sizeof(Atomic32) == sizeof(int) && + syscall(__NR_futex, &x, FUTEX_WAKE, 1, NULL, NULL, 0) >= 0); + if (have_futex && syscall(__NR_futex, &x, FUTEX_WAKE | futex_private_flag, + 1, NULL, NULL, 0) < 0) { futex_private_flag = 0; } } @@ -78,10 +81,9 @@ void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) { } if (have_futex) { tm.tv_nsec *= 16; // increase the delay; we expect explicit wakeups - sys_futex(reinterpret_cast(const_cast(w)), - FUTEX_WAIT | futex_private_flag, - value, reinterpret_cast(&tm), - NULL, 0); + syscall(__NR_futex, reinterpret_cast(const_cast(w)), + FUTEX_WAIT | futex_private_flag, value, + reinterpret_cast(&tm), NULL, 0); } else { nanosleep(&tm, NULL); } @@ -91,9 +93,8 @@ void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) { void SpinLockWake(volatile Atomic32 *w, bool all) { if (have_futex) { - sys_futex(reinterpret_cast(const_cast(w)), - FUTEX_WAKE | futex_private_flag, all? INT_MAX : 1, - NULL, NULL, 0); + syscall(__NR_futex, reinterpret_cast(const_cast(w)), + FUTEX_WAKE | futex_private_flag, all ? INT_MAX : 1, NULL, NULL, 0); } } diff --git a/tpl/gperftools/src/base/sysinfo.cc b/tpl/gperftools/src/base/sysinfo.cc index 789a47d..36f7067 100644 --- a/tpl/gperftools/src/base/sysinfo.cc +++ b/tpl/gperftools/src/base/sysinfo.cc @@ -110,6 +110,40 @@ // Some non-trivial getenv-related functions. // ---------------------------------------------------------------------- +// we reimplement memcmp and friends to avoid depending on any glibc +// calls too early in the process lifetime. This allows us to use +// GetenvBeforeMain from inside ifunc handler +static int slow_memcmp(const void *_a, const void *_b, size_t n) { + const uint8_t *a = reinterpret_cast(_a); + const uint8_t *b = reinterpret_cast(_b); + while (n-- != 0) { + uint8_t ac = *a++; + uint8_t bc = *b++; + if (ac != bc) { + if (ac < bc) { + return -1; + } + return 1; + } + } + return 0; +} + +static const char *slow_memchr(const char *s, int c, size_t n) { + uint8_t ch = static_cast(c); + while (n--) { + if (*s++ == ch) { + return s - 1; + } + } + return 0; +} + +static size_t slow_strlen(const char *s) { + const char *s2 = slow_memchr(s, '\0', static_cast(-1)); + return s2 - s; +} + // It's not safe to call getenv() in the malloc hooks, because they // might be called extremely early, before libc is done setting up // correctly. In particular, the thread library may not be done @@ -119,15 +153,12 @@ // /proc/self/environ has a limit of how much data it exports (around // 8K), so it's not an ideal solution. const char* GetenvBeforeMain(const char* name) { + const int namelen = slow_strlen(name); #if defined(HAVE___ENVIRON) // if we have it, it's declared in unistd.h if (__environ) { // can exist but be NULL, if statically linked - const int namelen = strlen(name); for (char** p = __environ; *p; p++) { - if (strlen(*p) < namelen) { - continue; - } - if (!memcmp(*p, name, namelen) && (*p)[namelen] == '=') // it's a match - return *p + namelen+1; // point after = + if (!slow_memcmp(*p, name, namelen) && (*p)[namelen] == '=') + return *p + namelen+1; } return NULL; } @@ -155,14 +186,14 @@ const char* GetenvBeforeMain(const char* name) { } safeclose(fd); } - const int namelen = strlen(name); const char* p = envbuf; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... - const char* endp = (char*)memchr(p, '\0', sizeof(envbuf) - (p - envbuf)); + const char* endp = (char*)slow_memchr(p, '\0', + sizeof(envbuf) - (p - envbuf)); if (endp == NULL) // this entry isn't NUL terminated return NULL; - else if (!memcmp(p, name, namelen) && p[namelen] == '=') // it's a match + else if (!slow_memcmp(p, name, namelen) && p[namelen] == '=') // it's a match return p + namelen+1; // point after = p = endp + 1; } diff --git a/tpl/gperftools/src/base/thread_lister.c b/tpl/gperftools/src/base/thread_lister.c index ca1b2de..9dc8d72 100644 --- a/tpl/gperftools/src/base/thread_lister.c +++ b/tpl/gperftools/src/base/thread_lister.c @@ -32,11 +32,17 @@ */ #include "config.h" + +#include "base/thread_lister.h" + #include /* needed for NULL on some powerpc platforms (?!) */ +#include +#include /* for getpid */ + #ifdef HAVE_SYS_PRCTL # include #endif -#include "base/thread_lister.h" + #include "base/linuxthreads.h" /* Include other thread listers here that define THREADS macro * only when they can provide a good implementation. diff --git a/tpl/gperftools/src/base/vdso_support.cc b/tpl/gperftools/src/base/vdso_support.cc index 730df30..f88aa30 100644 --- a/tpl/gperftools/src/base/vdso_support.cc +++ b/tpl/gperftools/src/base/vdso_support.cc @@ -43,7 +43,6 @@ #include // for ptrdiff_t #include "base/atomicops.h" // for MemoryBarrier -#include "base/linux_syscall_support.h" #include "base/logging.h" #include "base/dynamic_annotations.h" #include "base/basictypes.h" // for COMPILE_ASSERT diff --git a/tpl/gperftools/src/base/vdso_support.h b/tpl/gperftools/src/base/vdso_support.h index c1209a4..c17d224 100644 --- a/tpl/gperftools/src/base/vdso_support.h +++ b/tpl/gperftools/src/base/vdso_support.h @@ -61,7 +61,11 @@ #ifdef HAVE_ELF_MEM_IMAGE +// Enable VDSO support only for the architectures/operating systems that +// support it. +#if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) #define HAVE_VDSO_SUPPORT 1 +#endif #include // for NULL diff --git a/tpl/gperftools/src/central_freelist.cc b/tpl/gperftools/src/central_freelist.cc index 11b190d..01a7310 100644 --- a/tpl/gperftools/src/central_freelist.cc +++ b/tpl/gperftools/src/central_freelist.cc @@ -152,14 +152,14 @@ bool CentralFreeList::EvictRandomSizeClass( int locked_size_class, bool force) { static int race_counter = 0; int t = race_counter++; // Updated without a lock, but who cares. - if (t >= kNumClasses) { - while (t >= kNumClasses) { - t -= kNumClasses; + if (t >= Static::num_size_classes()) { + while (t >= Static::num_size_classes()) { + t -= Static::num_size_classes(); } race_counter = t; } ASSERT(t >= 0); - ASSERT(t < kNumClasses); + ASSERT(t < Static::num_size_classes()); if (t == locked_size_class) return false; return Static::central_cache()[t].ShrinkCache(locked_size_class, force); } @@ -340,7 +340,7 @@ void CentralFreeList::Populate() { // (Instead of being eager, we could just replace any stale info // about this span, but that seems to be no better in practice.) for (int i = 0; i < npages; i++) { - Static::pageheap()->CacheSizeClass(span->start + i, size_class_); + Static::pageheap()->SetCachedSizeClass(span->start + i, size_class_); } // Split the block into pieces and add to the free-list diff --git a/tpl/gperftools/src/common.cc b/tpl/gperftools/src/common.cc index 2cf507e..203afdf 100644 --- a/tpl/gperftools/src/common.cc +++ b/tpl/gperftools/src/common.cc @@ -44,14 +44,14 @@ namespace tcmalloc { // thread and central caches. static int32 FLAGS_tcmalloc_transfer_num_objects; -static const int32 kDefaultTransferNumObjecs = 32768; +static const int32 kDefaultTransferNumObjecs = 32; // The init function is provided to explicit initialize the variable value // from the env. var to avoid C++ global construction that might defer its // initialization after a malloc/new call. static inline void InitTCMallocTransferNumObjects() { - if (UNLIKELY(FLAGS_tcmalloc_transfer_num_objects == 0)) { + if (FLAGS_tcmalloc_transfer_num_objects == 0) { const char *envval = TCMallocGetenvSafe("TCMALLOC_TRANSFER_NUM_OBJ"); FLAGS_tcmalloc_transfer_num_objects = !envval ? kDefaultTransferNumObjecs : strtol(envval, NULL, 10); @@ -173,14 +173,15 @@ void SizeMap::Init() { class_to_size_[sc] = size; sc++; } - if (sc != kNumClasses) { + num_size_classes = sc; + if (sc > kClassSizesMax) { Log(kCrash, __FILE__, __LINE__, - "wrong number of size classes: (found vs. expected )", sc, kNumClasses); + "too many size classes: (found vs. max)", sc, kClassSizesMax); } // Initialize the mapping arrays int next_size = 0; - for (int c = 1; c < kNumClasses; c++) { + for (int c = 1; c < num_size_classes; c++) { const int max_size_in_class = class_to_size_[c]; for (int s = next_size; s <= max_size_in_class; s += kAlignment) { class_array_[ClassIndex(s)] = c; @@ -191,7 +192,7 @@ void SizeMap::Init() { // Double-check sizes just to be safe for (size_t size = 0; size <= kMaxSize;) { const int sc = SizeClass(size); - if (sc <= 0 || sc >= kNumClasses) { + if (sc <= 0 || sc >= num_size_classes) { Log(kCrash, __FILE__, __LINE__, "Bad size class (class, size)", sc, size); } @@ -211,8 +212,23 @@ void SizeMap::Init() { } } + // Our fast-path aligned allocation functions rely on 'naturally + // aligned' sizes to produce aligned addresses. Lets check if that + // holds for size classes that we produced. + // + // I.e. we're checking that + // + // align = (1 << shift), malloc(i * align) % align == 0, + // + // for all align values up to kPageSize. + for (size_t align = kMinAlign; align <= kPageSize; align <<= 1) { + for (size_t size = align; size < kPageSize; size += align) { + CHECK_CONDITION(class_to_size_[SizeClass(size)] % align == 0); + } + } + // Initialize the num_objects_to_move array. - for (size_t cl = 1; cl < kNumClasses; ++cl) { + for (size_t cl = 1; cl < num_size_classes; ++cl) { num_objects_to_move_[cl] = NumMoveSize(ByteSizeForClass(cl)); } } @@ -220,7 +236,6 @@ void SizeMap::Init() { // Metadata allocator -- keeps stats about how many bytes allocated. static uint64_t metadata_system_bytes_ = 0; static const size_t kMetadataAllocChunkSize = 8*1024*1024; -static const size_t kMetadataBigAllocThreshold = kMetadataAllocChunkSize / 8; // As ThreadCache objects are allocated with MetaDataAlloc, and also // CACHELINE_ALIGNED, we must use the same alignment as TCMalloc_SystemAlloc. static const size_t kMetadataAllignment = sizeof(MemoryAligner); diff --git a/tpl/gperftools/src/common.h b/tpl/gperftools/src/common.h index c3484d3..70f8a6a 100644 --- a/tpl/gperftools/src/common.h +++ b/tpl/gperftools/src/common.h @@ -44,14 +44,6 @@ #include "internal_logging.h" // for ASSERT, etc #include "base/basictypes.h" // for LIKELY, etc -#ifdef HAVE_BUILTIN_EXPECT -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif - // Type that can hold a page number typedef uintptr_t PageID; @@ -68,11 +60,8 @@ typedef uintptr_t Length; // Keep in mind when using the 16 bytes alignment you can have a space // waste due alignment of 25%. (eg malloc of 24 bytes will get 32 bytes) static const size_t kMinAlign = 8; -// Number of classes created until reach page size 128. -static const size_t kBaseClasses = 16; #else static const size_t kMinAlign = 16; -static const size_t kBaseClasses = 9; #endif // Using large pages speeds up the execution at a cost of larger memory use. @@ -85,23 +74,21 @@ static const size_t kBaseClasses = 9; // These two factors cause a bounded increase in memory use. #if defined(TCMALLOC_32K_PAGES) static const size_t kPageShift = 15; -static const size_t kNumClasses = kBaseClasses + 69; #elif defined(TCMALLOC_64K_PAGES) static const size_t kPageShift = 16; -static const size_t kNumClasses = kBaseClasses + 73; #else static const size_t kPageShift = 13; -static const size_t kNumClasses = kBaseClasses + 79; #endif +static const size_t kClassSizesMax = 96; + static const size_t kMaxThreadCacheSize = 4 << 20; static const size_t kPageSize = 1 << kPageShift; static const size_t kMaxSize = 256 * 1024; static const size_t kAlignment = 8; -static const size_t kLargeSizeClass = 0; -// For all span-lengths < kMaxPages we keep an exact-size list. -static const size_t kMaxPages = 1 << (20 - kPageShift); +// For all span-lengths <= kMaxPages we keep an exact-size list in PageHeap. +static const size_t kMaxPages = (1 << (21 - kPageShift)); // Default bound on the total amount of thread caches. #ifdef TCMALLOC_SMALL_BUT_SLOW @@ -133,14 +120,27 @@ static const int kMaxDynamicFreeListLength = 8192; static const Length kMaxValidPages = (~static_cast(0)) >> kPageShift; -#if defined __x86_64__ -// All current and planned x86_64 processors only look at the lower 48 bits -// in virtual to physical address translation. The top 16 are thus unused. -// TODO(rus): Under what operating systems can we increase it safely to 17? -// This lets us use smaller page maps. On first allocation, a 36-bit page map -// uses only 96 KB instead of the 4.5 MB used by a 52-bit page map. +#if __aarch64__ || __x86_64__ || _M_AMD64 || _M_ARM64 +// All current x86_64 processors only look at the lower 48 bits in +// virtual to physical address translation. The top 16 are all same as +// bit 47. And bit 47 value 1 reserved for kernel-space addresses in +// practice. So it is actually 47 usable bits from malloc +// perspective. This lets us use faster two level page maps on this +// architecture. +// +// There is very similar story on 64-bit arms except it has full 48 +// bits for user-space. Because of that, and because in principle OSes +// can start giving some of highest-bit-set addresses to user-space, +// we don't bother to limit x86 to 47 bits. +// +// As of now there are published plans to add more bits to x86-64 +// virtual address space, but since 48 bits has been norm for long +// time and lots of software is relying on it, it will be opt-in from +// OS perspective. So we can keep doing "48 bits" at least for now. static const int kAddressBits = (sizeof(void*) < 8 ? (8 * sizeof(void*)) : 48); #else +// mipsen and ppcs have more general hardware so we have to support +// full 64-bits of addresses. static const int kAddressBits = 8 * sizeof(void*); #endif @@ -160,13 +160,6 @@ int AlignmentForSize(size_t size); // Size-class information + mapping class SizeMap { private: - // Number of objects to move between a per-thread list and a central - // list in one shot. We want this to be not too small so we can - // amortize the lock overhead for accessing the central list. Making - // it too big may temporarily cause unnecessary memory wastage in the - // per-thread free list until the scavenger cleans up the list. - int num_objects_to_move_[kNumClasses]; - //------------------------------------------------------------------- // Mapping from size to size_class and vice versa //------------------------------------------------------------------- @@ -194,27 +187,59 @@ class SizeMap { ((kMaxSize + 127 + (120 << 7)) >> 7) + 1; unsigned char class_array_[kClassArraySize]; + static inline size_t SmallSizeClass(size_t s) { + return (static_cast(s) + 7) >> 3; + } + + static inline size_t LargeSizeClass(size_t s) { + return (static_cast(s) + 127 + (120 << 7)) >> 7; + } + + // If size is no more than kMaxSize, compute index of the + // class_array[] entry for it, putting the class index in output + // parameter idx and returning true. Otherwise return false. + static inline bool ATTRIBUTE_ALWAYS_INLINE ClassIndexMaybe(size_t s, + uint32* idx) { + if (PREDICT_TRUE(s <= kMaxSmallSize)) { + *idx = (static_cast(s) + 7) >> 3; + return true; + } else if (s <= kMaxSize) { + *idx = (static_cast(s) + 127 + (120 << 7)) >> 7; + return true; + } + return false; + } + // Compute index of the class_array[] entry for a given size - static inline size_t ClassIndex(int s) { + static inline size_t ClassIndex(size_t s) { // Use unsigned arithmetic to avoid unnecessary sign extensions. ASSERT(0 <= s); ASSERT(s <= kMaxSize); - if (LIKELY(s <= kMaxSmallSize)) { - return (static_cast(s) + 7) >> 3; + if (PREDICT_TRUE(s <= kMaxSmallSize)) { + return SmallSizeClass(s); } else { - return (static_cast(s) + 127 + (120 << 7)) >> 7; + return LargeSizeClass(s); } } + // Number of objects to move between a per-thread list and a central + // list in one shot. We want this to be not too small so we can + // amortize the lock overhead for accessing the central list. Making + // it too big may temporarily cause unnecessary memory wastage in the + // per-thread free list until the scavenger cleans up the list. + int num_objects_to_move_[kClassSizesMax]; + int NumMoveSize(size_t size); // Mapping from size class to max size storable in that class - size_t class_to_size_[kNumClasses]; + int32 class_to_size_[kClassSizesMax]; // Mapping from size class to number of pages to allocate at a time - size_t class_to_pages_[kNumClasses]; + size_t class_to_pages_[kClassSizesMax]; public: + size_t num_size_classes; + // Constructor should do nothing since we rely on explicit Init() // call, which may or may not be called before the constructor runs. SizeMap() { } @@ -222,22 +247,34 @@ class SizeMap { // Initialize the mapping arrays void Init(); - inline int SizeClass(int size) { + inline int SizeClass(size_t size) { return class_array_[ClassIndex(size)]; } + // Check if size is small enough to be representable by a size + // class, and if it is, put matching size class into *cl. Returns + // true iff matching size class was found. + inline bool ATTRIBUTE_ALWAYS_INLINE GetSizeClass(size_t size, uint32* cl) { + uint32 idx; + if (!ClassIndexMaybe(size, &idx)) { + return false; + } + *cl = class_array_[idx]; + return true; + } + // Get the byte-size for a specified class - inline size_t ByteSizeForClass(size_t cl) { + inline int32 ATTRIBUTE_ALWAYS_INLINE ByteSizeForClass(uint32 cl) { return class_to_size_[cl]; } // Mapping from size class to max size storable in that class - inline size_t class_to_size(size_t cl) { + inline int32 class_to_size(uint32 cl) { return class_to_size_[cl]; } // Mapping from size class to number of pages to allocate at a time - inline size_t class_to_pages(size_t cl) { + inline size_t class_to_pages(uint32 cl) { return class_to_pages_[cl]; } @@ -246,7 +283,7 @@ class SizeMap { // amortize the lock overhead for accessing the central list. Making // it too big may temporarily cause unnecessary memory wastage in the // per-thread free list until the scavenger cleans up the list. - inline int num_objects_to_move(size_t cl) { + inline int num_objects_to_move(uint32 cl) { return num_objects_to_move_[cl]; } }; diff --git a/tpl/gperftools/src/config.h b/tpl/gperftools/src/config.h deleted file mode 100644 index 0e9ae4d..0000000 --- a/tpl/gperftools/src/config.h +++ /dev/null @@ -1,356 +0,0 @@ -/* src/config.h.in. Generated from configure.ac by autoheader. */ - - -#ifndef GPERFTOOLS_CONFIG_H_ -#define GPERFTOOLS_CONFIG_H_ - -/* - * When gperftools was incorporated into Faodel, the option and - * feature variables exposed to users were prefixed with - * Faodel_PERFTOOLS_ to prevent namespace polution and make it - * easier for users to find groups of variables. - * - * Here we define a set of boolean variables for those option - * and feature variables. - * - * Later we will use these to define or undef the original - * gperftools variables. - * - */ -#define Faodel_PERFTOOLS_MALLOC_HOOK_MAYBE_VOLATILE 1 -#define Faodel_PERFTOOLS_HAVE_STRUCT_MALLINFO 1 -#define Faodel_PERFTOOLS_DLL_DECL 0 -#define Faodel_PERFTOOLS_STL_NAMESPACE 1 -#define Faodel_PERFTOOLS_TCMALLOC_32K_PAGES 0 -#define Faodel_PERFTOOLS_TCMALLOC_64K_PAGES 0 -#define Faodel_PERFTOOLS_TCMALLOC_ALIGN_8BYTES 0 -#define Faodel_PERFTOOLS_PACKAGE_VERSION 1 -#define Faodel_PERFTOOLS_TC_VERSION_MAJOR 1 -#define Faodel_PERFTOOLS_TC_VERSION_MINOR 1 -#define Faodel_PERFTOOLS_TC_VERSION_PATCH 0 -#define Faodel_PERFTOOLS_PRIdS 1 -#define Faodel_PERFTOOLS_PRIuS 1 -#define Faodel_PERFTOOLS_PRIxS 1 - - -/* Build runtime detection for sized delete */ -/* #undef ENABLE_DYNAMIC_SIZED_DELETE */ - -/* Build sized deletion operators */ -/* #undef ENABLE_SIZED_DELETE */ - -/* Define to 1 if compiler supports __builtin_expect */ -/* #undef HAVE_BUILTIN_EXPECT */ - -/* Define to 1 if compiler supports __builtin_stack_pointer */ -/* #undef HAVE_BUILTIN_STACK_POINTER */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_CONFLICT_SIGNAL_H */ -/* #undef HAVE_CONFLICT_SIGNAL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_CYGWIN_SIGNAL_H */ - -/* Define to 1 if you have the declaration of `backtrace', and to 0 if you - don't. */ -/* #undef HAVE_DECL_BACKTRACE */ - -/* Define to 1 if you have the declaration of `cfree', and to 0 if you don't. - */ -/* #undef HAVE_DECL_CFREE */ - -/* Define to 1 if you have the declaration of `memalign', and to 0 if you - don't. */ -/* #undef HAVE_DECL_MEMALIGN */ - -/* Define to 1 if you have the declaration of `nanosleep', and to 0 if you - don't. */ -/* #undef HAVE_DECL_NANOSLEEP */ - -/* Define to 1 if you have the declaration of `posix_memalign', and to 0 if - you don't. */ -/* #undef HAVE_DECL_POSIX_MEMALIGN */ - -/* Define to 1 if you have the declaration of `pvalloc', and to 0 if you - don't. */ -/* #undef HAVE_DECL_PVALLOC */ - -/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't. - */ -/* #undef HAVE_DECL_SLEEP */ - -/* Define to 1 if you have the declaration of `uname', and to 0 if you don't. - */ -/* #undef HAVE_DECL_UNAME */ - -/* Define to 1 if you have the declaration of `valloc', and to 0 if you don't. - */ -/* #undef HAVE_DECL_VALLOC */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H - -/* Define to 1 if the system has the type `Elf32_Versym'. */ -/* #undef HAVE_ELF32_VERSYM */ - -/* Define to 1 if you have the header file. */ -#define HAVE_EXECINFO_H - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H - -/* Define to 1 if you have the header file. */ -#define HAVE_FEATURES_H - -/* Define to 1 if you have the `fork' function. */ -/* #undef HAVE_FORK */ - -/* Define to 1 if you have the `geteuid' function. */ -/* #undef HAVE_GETEUID */ - -/* Define to 1 if you have the `getpagesize' function. */ -/* #undef HAVE_GETPAGESIZE */ - -/* Define to 1 if you have the header file. */ -#define HAVE_GLOB_H - -/* Define to 1 if you have the header file. */ -#define HAVE_GRP_H - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBUNWIND_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_LINUX_PTRACE_H - -/* Define if this is Linux that has SIGEV_THREAD_ID */ -/* #undef HAVE_LINUX_SIGEV_THREAD_ID */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MALLOC_H - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H - -/* Define to 1 if you have a working `mmap' system call. */ -#define HAVE_MMAP - -/* Define to 1 if you have a working `memalign' system call. */ -#define HAVE_MEMALIGN - -/* define if the compiler implements namespaces */ -/* #undef HAVE_NAMESPACES */ - -/* Define to 1 if you have the header file. */ -#define HAVE_POLL_H - -/* define if libc has program_invocation_name */ -/* #undef HAVE_PROGRAM_INVOCATION_NAME */ - -/* Define if you have POSIX threads libraries and header files. */ -#define HAVE_PTHREAD 1 - -/* defined to 1 if pthread symbols are exposed even without include pthread.h - */ -/* #undef HAVE_PTHREAD_DESPITE_ASKING_FOR */ - -/* Define to 1 if you have the header file. */ -#define HAVE_PWD_H - -/* Define to 1 if you have the `sbrk' function. */ -#define HAVE_SBRK - -/* Define to 1 if you have the header file. */ -#define HAVE_SCHED_H - -/* Define to 1 if you have the header file. */ -/*#define HAVE_STDINT_H */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H - -/* Define to 1 if the system supports `mallinfo'. */ -#if (1 == Faodel_PERFTOOLS_HAVE_STRUCT_MALLINFO) -#define HAVE_STRUCT_MALLINFO 1 -#endif - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_CDEFS_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_PARAM_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_PRCTL_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_RESOURCE_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SYSCALL_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UCONTEXT_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_WAIT_H - -/* Define to 1 if compiler supports __thread */ -/* #undef HAVE_TLS */ - -/* Define to 1 if you have the header file. */ -#define HAVE_UCONTEXT_H - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H - -/* Whether contains _Unwind_Backtrace */ -/* #undef HAVE_UNWIND_BACKTRACE */ - -/* Define to 1 if you have the header file. */ -#define HAVE_UNWIND_H - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VALGRIND_H */ - -/* define if your compiler has __attribute__ */ -/* #undef HAVE___ATTRIBUTE__ */ - -/* Define to 1 if compiler supports __environ */ -/* #undef HAVE___ENVIRON */ - -/* Define to 1 if the system has the type `__int64'. */ -/* #undef HAVE___INT64 */ - -/* prefix where we look for installed files */ -/* #undef INSTALL_PREFIX */ - -/* Define to 1 if int32_t is equivalent to intptr_t */ -/* #undef INT32_EQUALS_INTPTR */ - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -/* #undef LT_OBJDIR */ - -/* Define to 'volatile' if __malloc_hook is declared volatile */ -#define MALLOC_HOOK_MAYBE_VOLATILE volatile - -/* Name of package */ -/* #undef PACKAGE */ - -/* Define to the address where bug reports for this package should be sent. */ -/* #undef PACKAGE_BUGREPORT */ - -/* Define to the full name of this package. */ -/* #undef PACKAGE_NAME */ - -/* Define to the full name and version of this package. */ -/* #undef PACKAGE_STRING */ - -/* Define to the one symbol short name of this package. */ -/* #undef PACKAGE_TARNAME */ - -/* Define to the home page for this package. */ -/* #undef PACKAGE_URL */ - -/* Define to the version of this package. */ -#define PACKAGE_VERSION 2.5 - -/* How to access the PC from a struct ucontext */ -/* #undef PC_FROM_UCONTEXT */ - -/* Always the empty-string on non-windows systems. On windows, should be - "__declspec(dllexport)". This way, when we compile the dll, we export our - functions/classes. It's safe to define this here because config.h is only - used internally, to compile the DLL, and every DLL source file #includes - "config.h" before anything else. */ -#define PERFTOOLS_DLL_DECL - -/* printf format code for printing a size_t and ssize_t */ -#if (1 == Faodel_PERFTOOLS_PRIdS) -#define PRIdS "%d" -#endif - -/* printf format code for printing a size_t and ssize_t */ -#if (1 == Faodel_PERFTOOLS_PRIuS) -#define PRIuS "%u" -#endif - -/* printf format code for printing a size_t and ssize_t */ -#if (1 == Faodel_PERFTOOLS_PRIxS) -#define PRIxS "%x" -#endif - -/* Mark the systems where we know it's bad if pthreads runs too - early before main (before threads are initialized, presumably). */ -#ifdef __FreeBSD__ -#define PTHREADS_CRASHES_IF_RUN_TOO_EARLY 1 -#endif - -/* Define to necessary symbol if this constant uses a non-standard name on - your system. */ -/* #undef PTHREAD_CREATE_JOINABLE */ - -/* Define to 1 if you have the ANSI C header files. */ -/* #undef STDC_HEADERS */ - -/* the namespace where STL code like vector<> is defined */ -#define STL_NAMESPACE std - -/* Define 32K of internal pages size for tcmalloc */ -#if (1 == Faodel_PERFTOOLS_TCMALLOC_32K_PAGES) -#define TCMALLOC_32K_PAGES OFF -#endif - -/* Define 64K of internal pages size for tcmalloc */ -#if (1 == Faodel_PERFTOOLS_TCMALLOC_64K_PAGES) -#define TCMALLOC_64K_PAGES OFF -#endif - -/* Define 8 bytes of allocation alignment for tcmalloc */ -#if (1 == Faodel_PERFTOOLS_TCMALLOC_ALIGN_BYTES) -#define TCMALLOC_ALIGN_BYTES -#endif - -/* Version number of package */ -/* -#define VERSION -*/ - -/* C99 says: define this to get the PRI... macros from stdint.h */ -#ifndef __STDC_FORMAT_MACROS -# define __STDC_FORMAT_MACROS 1 -#endif - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - - -#ifdef __MINGW32__ -#include "windows/mingw.h" -#endif - -#endif /* #ifndef GPERFTOOLS_CONFIG_H_ */ - diff --git a/tpl/gperftools/src/debugallocation.cc b/tpl/gperftools/src/debugallocation.cc index c170bc7..39dea5a 100644 --- a/tpl/gperftools/src/debugallocation.cc +++ b/tpl/gperftools/src/debugallocation.cc @@ -109,7 +109,7 @@ DEFINE_bool(malloc_page_fence, "with a guard page following the allocation (to catch buffer " "overruns right when they happen)."); DEFINE_bool(malloc_page_fence_never_reclaim, - EnvToBool("TCMALLOC_PAGE_FRANCE_NEVER_RECLAIM", false), + EnvToBool("TCMALLOC_PAGE_FENCE_NEVER_RECLAIM", false), "Enables making the virtual address space inaccessible " "upon a deallocation instead of returning it and reusing later."); #else @@ -206,9 +206,10 @@ struct MallocBlockQueueEntry { // Adjust the number of frames to skip (4) if you change the // location of this call. num_deleter_pcs = - GetStackTrace(deleter_pcs, - sizeof(deleter_pcs) / sizeof(deleter_pcs[0]), - 4); + MallocHook::GetCallerStackTrace( + deleter_pcs, + sizeof(deleter_pcs) / sizeof(deleter_pcs[0]), + 4); deleter_threadid = pthread_self(); } else { num_deleter_pcs = 0; @@ -272,8 +273,8 @@ class MallocBlock { // We use either do_malloc or mmap to make the actual allocation. In // order to remember which one of the two was used for any block, we store an // appropriate magic word next to the block. - static const int kMagicMalloc = 0xDEADBEEF; - static const int kMagicMMap = 0xABCDEFAB; + static const size_t kMagicMalloc = 0xDEADBEEF; + static const size_t kMagicMMap = 0xABCDEFAB; // This array will be filled with 0xCD, for use with memcmp. static unsigned char kMagicDeletedBuffer[1024]; @@ -299,7 +300,7 @@ class MallocBlock { // then come the size2_ and magic2_, or a full page of mprotect-ed memory // if the malloc_page_fence feature is enabled. size_t size2_; - int magic2_; + size_t magic2_; private: // static data and helpers @@ -342,7 +343,7 @@ class MallocBlock { bool IsMMapped() const { return kMagicMMap == magic1_; } - bool IsValidMagicValue(int value) const { + bool IsValidMagicValue(size_t value) const { return kMagicMMap == value || kMagicMalloc == value; } @@ -375,8 +376,8 @@ class MallocBlock { return (const size_t*)((char*)&size2_ + size1_); } - int* magic2_addr() { return (int*)(size2_addr() + 1); } - const int* magic2_addr() const { return (const int*)(size2_addr() + 1); } + size_t* magic2_addr() { return (size_t*)(size2_addr() + 1); } + const size_t* magic2_addr() const { return (const size_t*)(size2_addr() + 1); } private: // other helpers @@ -394,28 +395,30 @@ class MallocBlock { offset_ = 0; alloc_type_ = type; if (!IsMMapped()) { - *magic2_addr() = magic1_; - *size2_addr() = size; + bit_store(magic2_addr(), &magic1_); + bit_store(size2_addr(), &size); } alloc_map_lock_.Unlock(); memset(data_addr(), kMagicUninitializedByte, size); if (!IsMMapped()) { - RAW_CHECK(size1_ == *size2_addr(), "should hold"); - RAW_CHECK(magic1_ == *magic2_addr(), "should hold"); + RAW_CHECK(memcmp(&size1_, size2_addr(), sizeof(size1_)) == 0, "should hold"); + RAW_CHECK(memcmp(&magic1_, magic2_addr(), sizeof(magic1_)) == 0, "should hold"); } } - size_t CheckAndClear(int type) { + size_t CheckAndClear(int type, size_t given_size) { alloc_map_lock_.Lock(); CheckLocked(type); if (!IsMMapped()) { - RAW_CHECK(size1_ == *size2_addr(), "should hold"); + RAW_CHECK(memcmp(&size1_, size2_addr(), sizeof(size1_)) == 0, "should hold"); } // record us as deallocated in the map alloc_map_->Insert(data_addr(), type | kDeallocatedTypeBit); alloc_map_lock_.Unlock(); // clear us const size_t size = real_size(); + RAW_CHECK(!given_size || given_size == size1_, + "right size must be passed to sized delete"); memset(this, kMagicDeletedByte, size); return size; } @@ -449,11 +452,13 @@ class MallocBlock { data_addr()); } if (!IsMMapped()) { - if (size1_ != *size2_addr()) { + if (memcmp(&size1_, size2_addr(), sizeof(size1_))) { RAW_LOG(FATAL, "memory stomping bug: a word after object at %p " "has been corrupted", data_addr()); } - if (!IsValidMagicValue(*magic2_addr())) { + size_t addr; + bit_store(&addr, magic2_addr()); + if (!IsValidMagicValue(addr)) { RAW_LOG(FATAL, "memory stomping bug: a word after object at %p " "has been corrupted", data_addr()); } @@ -543,10 +548,10 @@ class MallocBlock { return b; } - void Deallocate(int type) { + void Deallocate(int type, size_t given_size) { if (IsMMapped()) { // have to do this before CheckAndClear #ifdef HAVE_MMAP - int size = CheckAndClear(type); + int size = CheckAndClear(type, given_size); int pagesize = getpagesize(); int num_pages = (size + pagesize - 1) / pagesize + 1; char* p = (char*) this; @@ -559,7 +564,7 @@ class MallocBlock { } #endif } else { - const size_t size = CheckAndClear(type); + const size_t size = CheckAndClear(type, given_size); if (FLAGS_malloc_reclaim_memory) { // Instead of freeing the block immediately, push it onto a queue of // recently freed blocks. Free only enough blocks to keep from @@ -615,7 +620,6 @@ class MallocBlock { free_queue_lock_.Lock(); } } - RAW_CHECK(free_queue_size_ >= 0, "Free queue size went negative!"); free_queue_lock_.Unlock(); for (int i = 0; i < num_entries; i++) { CheckForDanglingWrites(entries[i]); @@ -837,8 +841,8 @@ void DanglingWriteChecker() { // ========================================================================= // -const int MallocBlock::kMagicMalloc; -const int MallocBlock::kMagicMMap; +const size_t MallocBlock::kMagicMalloc; +const size_t MallocBlock::kMagicMMap; MallocBlock::AllocMap* MallocBlock::alloc_map_ = NULL; SpinLock MallocBlock::alloc_map_lock_(SpinLock::LINKER_INITIALIZED); @@ -885,6 +889,7 @@ static void TracePrintf(int fd, const char *fmt, ...) { const char *p = fmt; char numbuf[25]; if (fd < 0) { + va_end(ap); return; } numbuf[sizeof(numbuf)-1] = 0; @@ -1030,11 +1035,11 @@ static inline void* DebugAllocate(size_t size, int type) { return ptr->data_addr(); } -static inline void DebugDeallocate(void* ptr, int type) { +static inline void DebugDeallocate(void* ptr, int type, size_t given_size) { MALLOC_TRACE("free", (ptr != 0 ? MallocBlock::FromRawPointer(ptr)->data_size() : 0), ptr); - if (ptr) MallocBlock::FromRawPointer(ptr)->Deallocate(type); + if (ptr) MallocBlock::FromRawPointer(ptr)->Deallocate(type, given_size); } // ========================================================================= // @@ -1147,8 +1152,8 @@ static union { REGISTER_MODULE_INITIALIZER(debugallocation, { #if (__cplusplus >= 201103L) - COMPILE_ASSERT(alignof(debug_malloc_implementation_space) >= alignof(DebugMallocImplementation), - debug_malloc_implementation_space_is_not_properly_aligned); + static_assert(alignof(decltype(debug_malloc_implementation_space)) >= alignof(DebugMallocImplementation), + "DebugMallocImplementation is expected to need just word alignment"); #endif // Either we or valgrind will control memory management. We // register our extension if we're the winner. Otherwise let @@ -1210,18 +1215,47 @@ inline void* do_debug_malloc_or_debug_cpp_alloc(size_t size) { // Exported routines -extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW { +// frame forcer and force_frame exist only to prevent tail calls to +// DebugDeallocate to be actually implemented as tail calls. This is +// important because stack trace capturing in MallocBlockQueueEntry +// relies on google_malloc section being on stack and tc_XXX functions +// are in that section. So they must not jump to DebugDeallocate but +// have to do call. frame_forcer call at the end of such functions +// prevents tail calls to DebugDeallocate. +static int frame_forcer; +static void force_frame() { + int dummy = *(int volatile *)&frame_forcer; + (void)dummy; +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW { + if (ThreadCache::IsUseEmergencyMalloc()) { + return tcmalloc::EmergencyMalloc(size); + } void* ptr = do_debug_malloc_or_debug_cpp_alloc(size); MallocHook::InvokeNewHook(ptr, size); return ptr; } -extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW { + if (tcmalloc::IsEmergencyPtr(ptr)) { + return tcmalloc::EmergencyFree(ptr); + } + MallocHook::InvokeDeleteHook(ptr); + DebugDeallocate(ptr, MallocBlock::kMallocType, 0); + force_frame(); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW { MallocHook::InvokeDeleteHook(ptr); - DebugDeallocate(ptr, MallocBlock::kMallocType); + DebugDeallocate(ptr, MallocBlock::kMallocType, size); + force_frame(); } -extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) PERFTOOLS_NOTHROW { + if (ThreadCache::IsUseEmergencyMalloc()) { + return tcmalloc::EmergencyCalloc(count, size); + } // Overflow check const size_t total_size = count * size; if (size != 0 && total_size / size != count) return NULL; @@ -1232,12 +1266,19 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) __THROW return block; } -extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW { + if (tcmalloc::IsEmergencyPtr(ptr)) { + return tcmalloc::EmergencyFree(ptr); + } MallocHook::InvokeDeleteHook(ptr); - DebugDeallocate(ptr, MallocBlock::kMallocType); + DebugDeallocate(ptr, MallocBlock::kMallocType, 0); + force_frame(); } -extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW { + if (tcmalloc::IsEmergencyPtr(ptr)) { + return tcmalloc::EmergencyRealloc(ptr, size); + } if (ptr == NULL) { ptr = do_debug_malloc_or_debug_cpp_alloc(size); MallocHook::InvokeNewHook(ptr, size); @@ -1245,7 +1286,7 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW { } if (size == 0) { MallocHook::InvokeDeleteHook(ptr); - DebugDeallocate(ptr, MallocBlock::kMallocType); + DebugDeallocate(ptr, MallocBlock::kMallocType, 0); return NULL; } MallocBlock* old = MallocBlock::FromRawPointer(ptr); @@ -1270,7 +1311,7 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW { memcpy(p->data_addr(), ptr, (old_size < size) ? old_size : size); MallocHook::InvokeDeleteHook(ptr); MallocHook::InvokeNewHook(p->data_addr(), size); - DebugDeallocate(ptr, MallocBlock::kMallocType); + DebugDeallocate(ptr, MallocBlock::kMallocType, 0); MALLOC_TRACE("realloc", p->data_size(), p->data_addr()); return p->data_addr(); } @@ -1284,22 +1325,30 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_new(size_t size) { return ptr; } -extern "C" PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, const std::nothrow_t&) PERFTOOLS_NOTHROW { void* ptr = debug_cpp_alloc(size, MallocBlock::kNewType, true); MallocHook::InvokeNewHook(ptr, size); return ptr; } -extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW { + MallocHook::InvokeDeleteHook(p); + DebugDeallocate(p, MallocBlock::kNewType, 0); + force_frame(); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW { MallocHook::InvokeDeleteHook(p); - DebugDeallocate(p, MallocBlock::kNewType); + DebugDeallocate(p, MallocBlock::kNewType, size); + force_frame(); } // Some STL implementations explicitly invoke this. // It is completely equivalent to a normal delete (delete never throws). -extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, const std::nothrow_t&) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, const std::nothrow_t&) PERFTOOLS_NOTHROW { MallocHook::InvokeDeleteHook(p); - DebugDeallocate(p, MallocBlock::kNewType); + DebugDeallocate(p, MallocBlock::kNewType, 0); + force_frame(); } extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) { @@ -1312,26 +1361,34 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) { } extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) - __THROW { + PERFTOOLS_NOTHROW { void* ptr = debug_cpp_alloc(size, MallocBlock::kArrayNewType, true); MallocHook::InvokeNewHook(ptr, size); return ptr; } -extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW { MallocHook::InvokeDeleteHook(p); - DebugDeallocate(p, MallocBlock::kArrayNewType); + DebugDeallocate(p, MallocBlock::kArrayNewType, 0); + force_frame(); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW { + MallocHook::InvokeDeleteHook(p); + DebugDeallocate(p, MallocBlock::kArrayNewType, size); + force_frame(); } // Some STL implementations explicitly invoke this. // It is completely equivalent to a normal delete (delete never throws). -extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, const std::nothrow_t&) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, const std::nothrow_t&) PERFTOOLS_NOTHROW { MallocHook::InvokeDeleteHook(p); - DebugDeallocate(p, MallocBlock::kArrayNewType); + DebugDeallocate(p, MallocBlock::kArrayNewType, 0); + force_frame(); } // This is mostly the same as do_memalign in tcmalloc.cc. -static void *do_debug_memalign(size_t alignment, size_t size) { +static void *do_debug_memalign(size_t alignment, size_t size, int type) { // Allocate >= size bytes aligned on "alignment" boundary // "alignment" is a power of two. void *p = 0; @@ -1341,7 +1398,7 @@ static void *do_debug_memalign(size_t alignment, size_t size) { // a further data_offset bytes for an additional fake header. size_t extra_bytes = data_offset + alignment - 1; if (size + extra_bytes < size) return NULL; // Overflow - p = DebugAllocate(size + extra_bytes, MallocBlock::kMallocType); + p = DebugAllocate(size + extra_bytes, type); if (p != 0) { intptr_t orig_p = reinterpret_cast(p); // Leave data_offset bytes for fake header, and round up to meet @@ -1366,16 +1423,21 @@ static void *do_debug_memalign(size_t alignment, size_t size) { struct memalign_retry_data { size_t align; size_t size; + int type; }; static void *retry_debug_memalign(void *arg) { memalign_retry_data *data = static_cast(arg); - return do_debug_memalign(data->align, data->size); + return do_debug_memalign(data->align, data->size, data->type); } +ATTRIBUTE_ALWAYS_INLINE inline void* do_debug_memalign_or_debug_cpp_memalign(size_t align, - size_t size) { - void* p = do_debug_memalign(align, size); + size_t size, + int type, + bool from_operator, + bool nothrow) { + void* p = do_debug_memalign(align, size, type); if (p != NULL) { return p; } @@ -1383,26 +1445,27 @@ inline void* do_debug_memalign_or_debug_cpp_memalign(size_t align, struct memalign_retry_data data; data.align = align; data.size = size; + data.type = type; return handle_oom(retry_debug_memalign, &data, - false, true); + from_operator, nothrow); } -extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align, size_t size) __THROW { - void *p = do_debug_memalign_or_debug_cpp_memalign(align, size); +extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align, size_t size) PERFTOOLS_NOTHROW { + void *p = do_debug_memalign_or_debug_cpp_memalign(align, size, MallocBlock::kMallocType, false, true); MallocHook::InvokeNewHook(p, size); return p; } // Implementation taken from tcmalloc/tcmalloc.cc extern "C" PERFTOOLS_DLL_DECL int tc_posix_memalign(void** result_ptr, size_t align, size_t size) - __THROW { + PERFTOOLS_NOTHROW { if (((align % sizeof(void*)) != 0) || ((align & (align - 1)) != 0) || (align == 0)) { return EINVAL; } - void* result = do_debug_memalign_or_debug_cpp_memalign(align, size); + void* result = do_debug_memalign_or_debug_cpp_memalign(align, size, MallocBlock::kMallocType, false, true); MallocHook::InvokeNewHook(result, size); if (result == NULL) { return ENOMEM; @@ -1412,14 +1475,14 @@ extern "C" PERFTOOLS_DLL_DECL int tc_posix_memalign(void** result_ptr, size_t al } } -extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) PERFTOOLS_NOTHROW { // Allocate >= size bytes starting on a page boundary - void *p = do_debug_memalign_or_debug_cpp_memalign(getpagesize(), size); + void *p = do_debug_memalign_or_debug_cpp_memalign(getpagesize(), size, MallocBlock::kMallocType, false, true); MallocHook::InvokeNewHook(p, size); return p; } -extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) PERFTOOLS_NOTHROW { // Round size up to a multiple of pages // then allocate memory on a page boundary int pagesize = getpagesize(); @@ -1427,31 +1490,93 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW { if (size == 0) { // pvalloc(0) should allocate one page, according to size = pagesize; // http://man.free4web.biz/man3/libmpatrol.3.html } - void *p = do_debug_memalign_or_debug_cpp_memalign(pagesize, size); + void *p = do_debug_memalign_or_debug_cpp_memalign(pagesize, size, MallocBlock::kMallocType, false, true); MallocHook::InvokeNewHook(p, size); return p; } +#if defined(ENABLE_ALIGNED_NEW_DELETE) + +extern "C" PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t align) { + void* result = do_debug_memalign_or_debug_cpp_memalign(static_cast(align), size, MallocBlock::kNewType, true, false); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t align, const std::nothrow_t&) PERFTOOLS_NOTHROW { + void* result = do_debug_memalign_or_debug_cpp_memalign(static_cast(align), size, MallocBlock::kNewType, true, true); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t) PERFTOOLS_NOTHROW { + tc_delete(p); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t align) PERFTOOLS_NOTHROW { + // Reproduce actual size calculation done by do_debug_memalign + const size_t alignment = static_cast(align); + const size_t data_offset = MallocBlock::data_offset(); + const size_t extra_bytes = data_offset + alignment - 1; + + tc_delete_sized(p, size + extra_bytes); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t, const std::nothrow_t&) PERFTOOLS_NOTHROW { + tc_delete(p); +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t align) { + void* result = do_debug_memalign_or_debug_cpp_memalign(static_cast(align), size, MallocBlock::kArrayNewType, true, false); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t align, const std::nothrow_t& nt) PERFTOOLS_NOTHROW { + void* result = do_debug_memalign_or_debug_cpp_memalign(static_cast(align), size, MallocBlock::kArrayNewType, true, true); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t) PERFTOOLS_NOTHROW { + tc_deletearray(p); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t align) PERFTOOLS_NOTHROW { + // Reproduce actual size calculation done by do_debug_memalign + const size_t alignment = static_cast(align); + const size_t data_offset = MallocBlock::data_offset(); + const size_t extra_bytes = data_offset + alignment - 1; + + tc_deletearray_sized(p, size + extra_bytes); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t, const std::nothrow_t&) PERFTOOLS_NOTHROW { + tc_deletearray(p); +} + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + // malloc_stats just falls through to the base implementation. -extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW { do_malloc_stats(); } -extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW { +extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW { return do_mallopt(cmd, value); } #ifdef HAVE_STRUCT_MALLINFO -extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW { +extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW { return do_mallinfo(); } #endif -extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW { +extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW { return MallocExtension::instance()->GetAllocatedSize(ptr); } -extern "C" PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW { void* result = DebugAllocate(size, MallocBlock::kMallocType); MallocHook::InvokeNewHook(result, size); return result; diff --git a/tpl/gperftools/src/fake_stacktrace_scope.cc b/tpl/gperftools/src/fake_stacktrace_scope.cc new file mode 100644 index 0000000..ee35a04 --- /dev/null +++ b/tpl/gperftools/src/fake_stacktrace_scope.cc @@ -0,0 +1,39 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Copyright (c) 2014, gperftools Contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/basictypes.h" + +namespace tcmalloc { + ATTRIBUTE_WEAK bool EnterStacktraceScope(void) { + return true; + } + ATTRIBUTE_WEAK void LeaveStacktraceScope(void) { + } +} diff --git a/tpl/gperftools/src/getpc.h b/tpl/gperftools/src/getpc.h index 25fee39..9605363 100644 --- a/tpl/gperftools/src/getpc.h +++ b/tpl/gperftools/src/getpc.h @@ -56,6 +56,9 @@ //#define _XOPEN_SOURCE 500 #include // for memcmp +#ifdef HAVE_ASM_PTRACE_H +#include +#endif #if defined(HAVE_SYS_UCONTEXT_H) #include #elif defined(HAVE_UCONTEXT_H) @@ -179,7 +182,12 @@ inline void* GetPC(const struct ucontext_t& signal_ucontext) { // configure.ac (or set it manually in your config.h). #else inline void* GetPC(const ucontext_t& signal_ucontext) { +#if defined(__s390__) && !defined(__s390x__) + // Mask out the AMODE31 bit from the PC recorded in the context. + return (void*)((unsigned long)signal_ucontext.PC_FROM_UCONTEXT & 0x7fffffffUL); +#else return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h +#endif } #endif diff --git a/tpl/gperftools/src/google/heap-checker.h b/tpl/gperftools/src/google/heap-checker.h index 7cacf1f..6b9ffe5 100644 --- a/tpl/gperftools/src/google/heap-checker.h +++ b/tpl/gperftools/src/google/heap-checker.h @@ -30,7 +30,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/heap-checker.h is deprecated. Use gperftools/heap-checker.h instead" #endif #include diff --git a/tpl/gperftools/src/google/heap-profiler.h b/tpl/gperftools/src/google/heap-profiler.h index 3fc26cf..6155484 100644 --- a/tpl/gperftools/src/google/heap-profiler.h +++ b/tpl/gperftools/src/google/heap-profiler.h @@ -31,7 +31,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/heap-profiler.h is deprecated. Use gperftools/heap-profiler.h instead" #endif #include diff --git a/tpl/gperftools/src/google/malloc_extension.h b/tpl/gperftools/src/google/malloc_extension.h index 7cacc34..fdad25a 100644 --- a/tpl/gperftools/src/google/malloc_extension.h +++ b/tpl/gperftools/src/google/malloc_extension.h @@ -30,7 +30,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/malloc_extension.h is deprecated. Use gperftools/malloc_extension.h instead" #endif #include diff --git a/tpl/gperftools/src/google/malloc_extension_c.h b/tpl/gperftools/src/google/malloc_extension_c.h index f34a835..3c5cd38 100644 --- a/tpl/gperftools/src/google/malloc_extension_c.h +++ b/tpl/gperftools/src/google/malloc_extension_c.h @@ -31,7 +31,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/malloc_extension_c.h is deprecated. Use gperftools/malloc_extension_c.h instead" #endif #include diff --git a/tpl/gperftools/src/google/malloc_hook.h b/tpl/gperftools/src/google/malloc_hook.h index 371aba4..7ec0002 100644 --- a/tpl/gperftools/src/google/malloc_hook.h +++ b/tpl/gperftools/src/google/malloc_hook.h @@ -30,7 +30,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/malloc_hook.h is deprecated. Use gperftools/malloc_hook.h instead" #endif #include diff --git a/tpl/gperftools/src/google/malloc_hook_c.h b/tpl/gperftools/src/google/malloc_hook_c.h index f882c16..eb21aaf 100644 --- a/tpl/gperftools/src/google/malloc_hook_c.h +++ b/tpl/gperftools/src/google/malloc_hook_c.h @@ -31,7 +31,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/malloc_hook_c.h is deprecated. Use gperftools/malloc_hook_c.h instead" #endif #include diff --git a/tpl/gperftools/src/google/profiler.h b/tpl/gperftools/src/google/profiler.h index 3674c9e..293d605 100644 --- a/tpl/gperftools/src/google/profiler.h +++ b/tpl/gperftools/src/google/profiler.h @@ -31,7 +31,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/profiler.h is deprecated. Use gperftools/profiler.h instead" #endif #include diff --git a/tpl/gperftools/src/google/stacktrace.h b/tpl/gperftools/src/google/stacktrace.h index 53d2947..55f12d2 100644 --- a/tpl/gperftools/src/google/stacktrace.h +++ b/tpl/gperftools/src/google/stacktrace.h @@ -30,7 +30,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/stacktrace.h is deprecated. Use gperftools/stacktrace.h instead" #endif #include diff --git a/tpl/gperftools/src/google/tcmalloc.h b/tpl/gperftools/src/google/tcmalloc.h index a2db70e..1addeb6 100644 --- a/tpl/gperftools/src/google/tcmalloc.h +++ b/tpl/gperftools/src/google/tcmalloc.h @@ -31,7 +31,7 @@ /* The code has moved to gperftools/. Use that include-directory for * new code. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING) #warning "google/tcmalloc.h is deprecated. Use gperftools/tcmalloc.h instead" #endif #include diff --git a/tpl/gperftools/src/gperftools/heap-checker.h b/tpl/gperftools/src/gperftools/heap-checker.h index 5a87d8d..edd6cc7 100644 --- a/tpl/gperftools/src/gperftools/heap-checker.h +++ b/tpl/gperftools/src/gperftools/heap-checker.h @@ -34,7 +34,7 @@ // // Module for detecing heap (memory) leaks. // -// For full(er) information, see doc/heap_checker.html +// For full(er) information, see docs/heap_checker.html // // This module can be linked into programs with // no slowdown caused by this unless you activate the leak-checker: diff --git a/tpl/gperftools/src/gperftools/heap-profiler.h b/tpl/gperftools/src/gperftools/heap-profiler.h index 9b67364..38c6afe 100644 --- a/tpl/gperftools/src/gperftools/heap-profiler.h +++ b/tpl/gperftools/src/gperftools/heap-profiler.h @@ -33,7 +33,7 @@ * * Module for heap-profiling. * - * For full(er) information, see doc/heapprofile.html + * For full(er) information, see docs/heapprofile.html * * This module can be linked into your program with * no slowdown caused by this unless you activate the profiler diff --git a/tpl/gperftools/src/gperftools/malloc_extension.h b/tpl/gperftools/src/gperftools/malloc_extension.h index 95b35cb..1be138a 100644 --- a/tpl/gperftools/src/gperftools/malloc_extension.h +++ b/tpl/gperftools/src/gperftools/malloc_extension.h @@ -107,8 +107,12 @@ class PERFTOOLS_DLL_DECL MallocExtension { virtual bool MallocMemoryStats(int* blocks, size_t* total, int histogram[kMallocHistogramSize]); - // Get a human readable description of the current state of the malloc - // data structures. The state is stored as a null-terminated string + // Get a human readable description of the following malloc data structures. + // - Total inuse memory by application. + // - Free memory(thread, central and page heap), + // - Freelist of central cache, each class. + // - Page heap freelist. + // The state is stored as a null-terminated string // in a prefix of "buffer[0,buffer_length-1]". // REQUIRES: buffer_length > 0. virtual void GetStats(char* buffer, int buffer_length); @@ -119,6 +123,10 @@ class PERFTOOLS_DLL_DECL MallocExtension { // therefore be passed to "pprof". This function is equivalent to // ReadStackTraces. The main difference is that this function returns // serialized data appropriately formatted for use by the pprof tool. + // + // Since gperftools 2.8 heap samples are not de-duplicated by the + // library anymore. + // // NOTE: by default, tcmalloc does not do any heap sampling, and this // function will always return an empty sample. To get useful // data from GetHeapSample, you must also set the environment @@ -160,6 +168,14 @@ class PERFTOOLS_DLL_DECL MallocExtension { // freed memory regions // This property is not writable. // + // "generic.total_physical_bytes" + // Estimate of total bytes of the physical memory usage by the + // allocator == + // current_allocated_bytes + + // fragmentation + + // metadata + // This property is not writable. + // // tcmalloc // -------- // "tcmalloc.max_total_thread_cache_bytes" @@ -391,6 +407,15 @@ class PERFTOOLS_DLL_DECL MallocExtension { // Like ReadStackTraces(), but returns stack traces that caused growth // in the address space size. virtual void** ReadHeapGrowthStackTraces(); + + // Returns the size in bytes of the calling threads cache. + virtual size_t GetThreadCacheSize(); + + // Like MarkThreadIdle, but does not destroy the internal data + // structures of the thread cache. When the thread resumes, it wil + // have an empty cache but will not need to pay to reconstruct the + // cache data structures. + virtual void MarkThreadTemporarilyIdle(); }; namespace base { diff --git a/tpl/gperftools/src/gperftools/malloc_extension_c.h b/tpl/gperftools/src/gperftools/malloc_extension_c.h index baa013d..70ff686 100644 --- a/tpl/gperftools/src/gperftools/malloc_extension_c.h +++ b/tpl/gperftools/src/gperftools/malloc_extension_c.h @@ -79,6 +79,8 @@ PERFTOOLS_DLL_DECL void MallocExtension_ReleaseToSystem(size_t num_bytes); PERFTOOLS_DLL_DECL void MallocExtension_ReleaseFreeMemory(void); PERFTOOLS_DLL_DECL size_t MallocExtension_GetEstimatedAllocatedSize(size_t size); PERFTOOLS_DLL_DECL size_t MallocExtension_GetAllocatedSize(const void* p); +PERFTOOLS_DLL_DECL size_t MallocExtension_GetThreadCacheSize(void); +PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadTemporarilyIdle(void); /* * NOTE: These enum values MUST be kept in sync with the version in diff --git a/tpl/gperftools/src/gperftools/malloc_hook.h b/tpl/gperftools/src/gperftools/malloc_hook.h index 9d56fb1..b76411f 100644 --- a/tpl/gperftools/src/gperftools/malloc_hook.h +++ b/tpl/gperftools/src/gperftools/malloc_hook.h @@ -70,7 +70,7 @@ #include #include extern "C" { -#include // a C version of the malloc_hook interface +#include "malloc_hook_c.h" // a C version of the malloc_hook interface } // Annoying stuff for windows -- makes sure clients can import these functions diff --git a/tpl/gperftools/src/gperftools/nallocx.h b/tpl/gperftools/src/gperftools/nallocx.h new file mode 100644 index 0000000..01f874c --- /dev/null +++ b/tpl/gperftools/src/gperftools/nallocx.h @@ -0,0 +1,37 @@ +#ifndef _NALLOCX_H_ +#define _NALLOCX_H_ +#include + +#ifndef PERFTOOLS_DLL_DECL +# ifdef _WIN32 +# define PERFTOOLS_DLL_DECL __declspec(dllimport) +# else +# define PERFTOOLS_DLL_DECL +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MALLOCX_LG_ALIGN(la) ((int)(la)) + +/* + * The nallocx function allocates no memory, but it performs the same size + * computation as the malloc function, and returns the real size of the + * allocation that would result from the equivalent malloc function call. + * nallocx is a malloc extension originally implemented by jemalloc: + * http://www.unix.com/man-page/freebsd/3/nallocx/ + * + * Note, we only support MALLOCX_LG_ALIGN flag and nothing else. + */ +PERFTOOLS_DLL_DECL size_t nallocx(size_t size, int flags); + +/* same as above but never weak */ +PERFTOOLS_DLL_DECL size_t tc_nallocx(size_t size, int flags); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _NALLOCX_H_ */ diff --git a/tpl/gperftools/src/gperftools/profiler.h b/tpl/gperftools/src/gperftools/profiler.h index 2d272d6..1e7eb12 100644 --- a/tpl/gperftools/src/gperftools/profiler.h +++ b/tpl/gperftools/src/gperftools/profiler.h @@ -33,7 +33,7 @@ * * Module for CPU profiling based on periodic pc-sampling. * - * For full(er) information, see doc/cpuprofile.html + * For full(er) information, see docs/cpuprofile.html * * This module is linked into your program with * no slowdown caused by this unless you activate the profiler diff --git a/tpl/gperftools/src/gperftools/tcmalloc.h b/tpl/gperftools/src/gperftools/tcmalloc.h deleted file mode 100644 index 41d58f6..0000000 --- a/tpl/gperftools/src/gperftools/tcmalloc.h +++ /dev/null @@ -1,137 +0,0 @@ -// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- -/* Copyright (c) 2003, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * --- - * Author: Sanjay Ghemawat - * .h file by Craig Silverstein - */ - -#ifndef TCMALLOC_TCMALLOC_H_ -#define TCMALLOC_TCMALLOC_H_ - -#include - -#include // for size_t -#ifdef HAVE_SYS_CDEFS_H -#include // where glibc defines __THROW -#endif - -// __THROW is defined in glibc systems. It means, counter-intuitively, -// "This function will never throw an exception." It's an optional -// optimization tool, but we may need to use it to match glibc prototypes. -#ifndef __THROW /* I guess we're not on a glibc system */ -# define __THROW /* __THROW is just an optimization, so ok to make it "" */ -#endif - -// Define the version number so folks can check against it -#define TC_VERSION_MAJOR 2 -#define TC_VERSION_MINOR 5 -#define TC_VERSION_PATCH "" -#define TC_VERSION_STRING "gperftools 2.5" - -// For struct mallinfo, if it's defined. -#ifdef HAVE_STRUCT_MALLINFO -// Malloc can be in several places on older versions of OS X. -# if defined(HAVE_MALLOC_H) -# include -# elif defined(HAVE_SYS_MALLOC_H) -# include -# elif defined(HAVE_MALLOC_MALLOC_H) -# include -# endif -#endif - -// Annoying stuff for windows -- makes sure clients can import these functions -#ifndef PERFTOOLS_DLL_DECL -# ifdef _WIN32 -# define PERFTOOLS_DLL_DECL __declspec(dllimport) -# else -# define PERFTOOLS_DLL_DECL -# endif -#endif - -#ifdef __cplusplus -namespace std { -struct nothrow_t; -} - -extern "C" { -#endif - // Returns a human-readable version string. If major, minor, - // and/or patch are not NULL, they are set to the major version, - // minor version, and patch-code (a string, usually ""). - PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor, - const char** patch) __THROW; - - PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW; - PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW; - - PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment, - size_t __size) __THROW; - PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr, - size_t align, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) __THROW; - PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) __THROW; - - PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW; - PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW; -#ifdef HAVE_STRUCT_MALLINFO - PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW; -#endif - - // This is an alias for MallocExtension::instance()->GetAllocatedSize(). - // It is equivalent to - // OS X: malloc_size() - // glibc: malloc_usable_size() - // Windows: _msize() - PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW; - -#ifdef __cplusplus - PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW; - PERFTOOLS_DLL_DECL void* tc_new(size_t size); - PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW; - PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void* tc_newarray(size_t size); - PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW; - PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, - const std::nothrow_t&) __THROW; -} -#endif - -#endif // #ifndef TCMALLOC_TCMALLOC_H_ diff --git a/tpl/gperftools/src/gperftools/tcmalloc.h.in b/tpl/gperftools/src/gperftools/tcmalloc.h.in index f130674..2f1ea30 100644 --- a/tpl/gperftools/src/gperftools/tcmalloc.h.in +++ b/tpl/gperftools/src/gperftools/tcmalloc.h.in @@ -1,11 +1,11 @@ -// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- /* Copyright (c) 2003, Google Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -38,37 +38,45 @@ #include -#include // for size_t -#ifdef HAVE_SYS_CDEFS_H -#include // where glibc defines __THROW +#include /* for size_t */ +#ifdef __cplusplus +#include /* for std::nothrow_t, std::align_val_t */ #endif -// __THROW is defined in glibc systems. It means, counter-intuitively, -// "This function will never throw an exception." It's an optional -// optimization tool, but we may need to use it to match glibc prototypes. -#ifndef __THROW /* I guess we're not on a glibc system */ -# define __THROW /* __THROW is just an optimization, so ok to make it "" */ +/* Define the version number so folks can check against it */ +#if 0 +#define TC_VERSION_MAJOR @TC_VERSION_MAJOR@ +#define TC_VERSION_MINOR @TC_VERSION_MINOR@ +#define TC_VERSION_PATCH "@TC_VERSION_PATCH@" +#define TC_VERSION_STRING "gperftools @TC_VERSION_MAJOR@.@TC_VERSION_MINOR@@TC_VERSION_PATCH@" #endif - -// Define the version number so folks can check against it #define TC_VERSION_MAJOR @Faodel_PERFTOOLS_TC_VERSION_MAJOR@ #define TC_VERSION_MINOR @Faodel_PERFTOOLS_TC_VERSION_MINOR@ #define TC_VERSION_PATCH "@Faodel_PERFTOOLS_TC_VERSION_PATCH@" #define TC_VERSION_STRING "gperftools @Faodel_PERFTOOLS_TC_VERSION_MAJOR@.@Faodel_PERFTOOLS_TC_VERSION_MINOR@@Faodel_PERFTOOLS_TC_VERSION_PATCH@" -// For struct mallinfo, if it's defined. +/* For struct mallinfo, if it's defined. */ +//#if @ac_cv_have_struct_mallinfo@ #ifdef HAVE_STRUCT_MALLINFO -// Malloc can be in several places on older versions of OS X. -# if defined(HAVE_MALLOC_H) # include -# elif defined(HAVE_SYS_MALLOC_H) -# include -# elif defined(HAVE_MALLOC_MALLOC_H) -# include +#endif + +#ifndef PERFTOOLS_NOTHROW + +#if __cplusplus >= 201103L +#define PERFTOOLS_NOTHROW noexcept +#elif defined(__cplusplus) +#define PERFTOOLS_NOTHROW throw() +#else +# ifdef __GNUC__ +# define PERFTOOLS_NOTHROW __attribute__((__nothrow__)) +# else +# define PERFTOOLS_NOTHROW # endif #endif -// Annoying stuff for windows -- makes sure clients can import these functions +#endif + #ifndef PERFTOOLS_DLL_DECL # ifdef _WIN32 # define PERFTOOLS_DLL_DECL __declspec(dllimport) @@ -78,60 +86,89 @@ #endif #ifdef __cplusplus -namespace std { -struct nothrow_t; -} - extern "C" { #endif - // Returns a human-readable version string. If major, minor, - // and/or patch are not NULL, they are set to the major version, - // minor version, and patch-code (a string, usually ""). + /* + * Returns a human-readable version string. If major, minor, + * and/or patch are not NULL, they are set to the major version, + * minor version, and patch-code (a string, usually ""). + */ PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor, - const char** patch) __THROW; + const char** patch) PERFTOOLS_NOTHROW; - PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW; - PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW; + PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment, - size_t __size) __THROW; + size_t __size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr, - size_t align, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) __THROW; - PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) __THROW; + size_t align, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW; - PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW; - PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW; + PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW; +//#if @ac_cv_have_struct_mallinfo@ #ifdef HAVE_STRUCT_MALLINFO - PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW; + PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW; #endif - // This is an alias for MallocExtension::instance()->GetAllocatedSize(). - // It is equivalent to - // OS X: malloc_size() - // glibc: malloc_usable_size() - // Windows: _msize() - PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW; + /* + * This is an alias for MallocExtension::instance()->GetAllocatedSize(). + * It is equivalent to + * OS X: malloc_size() + * glibc: malloc_usable_size() + * Windows: _msize() + */ + PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW; #ifdef __cplusplus - PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW; + PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_new(size_t size); PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, - const std::nothrow_t&) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_newarray(size_t size); PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, - const std::nothrow_t&) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + +//#if @ac_cv_have_std_align_val_t@ && __cplusplus >= 201703L +#if 1 && __cplusplus >= 201703L + PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t al); + PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t al); + PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; +#endif } #endif -#endif // #ifndef TCMALLOC_TCMALLOC_H_ +/* We're only un-defining for public */ +#if !defined(GPERFTOOLS_CONFIG_H_) + +#undef PERFTOOLS_NOTHROW + +#endif /* GPERFTOOLS_CONFIG_H_ */ + +#endif /* #ifndef TCMALLOC_TCMALLOC_H_ */ diff --git a/tpl/gperftools/src/heap-checker.cc b/tpl/gperftools/src/heap-checker.cc index 9c82dea..8e71f58 100755 --- a/tpl/gperftools/src/heap-checker.cc +++ b/tpl/gperftools/src/heap-checker.cc @@ -131,8 +131,8 @@ static bool IsDebuggerAttached(void) { // only works under linux, probably // This is the default if you don't link in -lprofiler extern "C" { -ATTRIBUTE_WEAK PERFTOOLS_DLL_DECL bool ProfilingIsEnabledForAllThreads(); -bool ProfilingIsEnabledForAllThreads() { return false; } +ATTRIBUTE_WEAK PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads(); +int ProfilingIsEnabledForAllThreads() { return false; } } //---------------------------------------------------------------------- @@ -495,7 +495,7 @@ class InitThreadDisableCounter { InitThreadDisableCounter() { perftools_pthread_key_create(&thread_disable_counter_key, NULL); // Set up the main thread's value, which we have a special variable for. - void* p = (void*)main_thread_counter; // store the counter directly + void* p = (void*)(intptr_t)main_thread_counter; // store the counter directly perftools_pthread_setspecific(thread_disable_counter_key, p); use_main_thread_counter = false; } diff --git a/tpl/gperftools/src/heap-profile-table.cc b/tpl/gperftools/src/heap-profile-table.cc index 7486468..c3ce41c 100644 --- a/tpl/gperftools/src/heap-profile-table.cc +++ b/tpl/gperftools/src/heap-profile-table.cc @@ -440,22 +440,20 @@ bool HeapProfileTable::WriteProfile(const char* file_name, AllocationMap* allocations) { RAW_VLOG(1, "Dumping non-live heap profile to %s", file_name); RawFD fd = RawOpenForWriting(file_name); - if (fd != kIllegalRawFD) { - RawWrite(fd, kProfileHeader, strlen(kProfileHeader)); - char buf[512]; - int len = UnparseBucket(total, buf, 0, sizeof(buf), " heapprofile", - NULL); - RawWrite(fd, buf, len); - const DumpArgs args(fd, NULL); - allocations->Iterate(DumpNonLiveIterator, args); - RawWrite(fd, kProcSelfMapsHeader, strlen(kProcSelfMapsHeader)); - DumpProcSelfMaps(fd); - RawClose(fd); - return true; - } else { + if (fd == kIllegalRawFD) { RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name); return false; } + RawWrite(fd, kProfileHeader, strlen(kProfileHeader)); + char buf[512]; + int len = UnparseBucket(total, buf, 0, sizeof(buf), " heapprofile", NULL); + RawWrite(fd, buf, len); + const DumpArgs args(fd, NULL); + allocations->Iterate(DumpNonLiveIterator, args); + RawWrite(fd, kProcSelfMapsHeader, strlen(kProcSelfMapsHeader)); + DumpProcSelfMaps(fd); + RawClose(fd); + return true; } void HeapProfileTable::CleanupOldProfiles(const char* prefix) { diff --git a/tpl/gperftools/src/heap-profiler.cc b/tpl/gperftools/src/heap-profiler.cc index 17d8697..33a25ac 100755 --- a/tpl/gperftools/src/heap-profiler.cc +++ b/tpl/gperftools/src/heap-profiler.cc @@ -272,7 +272,7 @@ static void MaybeDumpProfileLocked() { const int64 inuse_bytes = total.alloc_size - total.free_size; bool need_to_dump = false; char buf[128]; - int64 current_time = time(NULL); + if (FLAGS_heap_profile_allocation_interval > 0 && total.alloc_size >= last_dump_alloc + FLAGS_heap_profile_allocation_interval) { @@ -293,13 +293,15 @@ static void MaybeDumpProfileLocked() { snprintf(buf, sizeof(buf), "%" PRId64 " MB currently in use", inuse_bytes >> 20); need_to_dump = true; - } else if (FLAGS_heap_profile_time_interval > 0 && - current_time - last_dump_time >= - FLAGS_heap_profile_time_interval) { - snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump", - current_time - last_dump_time); - need_to_dump = true; - last_dump_time = current_time; + } else if (FLAGS_heap_profile_time_interval > 0 ) { + int64 current_time = time(NULL); + if (current_time - last_dump_time >= + FLAGS_heap_profile_time_interval) { + snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump", + current_time - last_dump_time); + need_to_dump = true; + last_dump_time = current_time; + } } if (need_to_dump) { DumpProfileLocked(buf); diff --git a/tpl/gperftools/src/internal_logging.cc b/tpl/gperftools/src/internal_logging.cc index 4e7fc87..708fa65 100644 --- a/tpl/gperftools/src/internal_logging.cc +++ b/tpl/gperftools/src/internal_logging.cc @@ -45,8 +45,6 @@ #include "base/logging.h" // for perftools_vsnprintf #include "base/spinlock.h" // for SpinLockHolder, SpinLock -static const int kLogBufSize = 800; - // Variables for storing crash output. Allocated statically since we // may not be able to heap-allocate while crashing. static SpinLock crash_lock(base::LINKER_INITIALIZED); diff --git a/tpl/gperftools/src/libc_override.h b/tpl/gperftools/src/libc_override.h index c01a97c..c981c3d 100644 --- a/tpl/gperftools/src/libc_override.h +++ b/tpl/gperftools/src/libc_override.h @@ -58,6 +58,14 @@ #endif #include +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define CPP_NOTHROW noexcept +#define CPP_BADALLOC +#else +#define CPP_NOTHROW throw() +#define CPP_BADALLOC throw(std::bad_alloc) +#endif + static void ReplaceSystemAlloc(); // defined in the .h files below // For windows, there are two ways to get tcmalloc. If we're diff --git a/tpl/gperftools/src/libc_override_gcc_and_weak.h b/tpl/gperftools/src/libc_override_gcc_and_weak.h index 818e43d..bb99b69 100644 --- a/tpl/gperftools/src/libc_override_gcc_and_weak.h +++ b/tpl/gperftools/src/libc_override_gcc_and_weak.h @@ -44,6 +44,9 @@ #endif #include +#include "getenv_safe.h" // TCMallocGetenvSafe +#include "base/commandlineflags.h" + #ifndef __THROW // I guess we're not on a glibc-like system # define __THROW // __THROW is just an optimization, so ok to make it "" #endif @@ -52,24 +55,157 @@ # error libc_override_gcc_and_weak.h is for gcc distributions only. #endif -#define ALIAS(tc_fn) __attribute__ ((alias (#tc_fn))) - -void* operator new(size_t size) throw (std::bad_alloc) - ALIAS(tc_new); -void operator delete(void* p) __THROW - ALIAS(tc_delete); -void* operator new[](size_t size) throw (std::bad_alloc) - ALIAS(tc_newarray); -void operator delete[](void* p) __THROW - ALIAS(tc_deletearray); -void* operator new(size_t size, const std::nothrow_t& nt) __THROW - ALIAS(tc_new_nothrow); -void* operator new[](size_t size, const std::nothrow_t& nt) __THROW - ALIAS(tc_newarray_nothrow); -void operator delete(void* p, const std::nothrow_t& nt) __THROW - ALIAS(tc_delete_nothrow); -void operator delete[](void* p, const std::nothrow_t& nt) __THROW - ALIAS(tc_deletearray_nothrow); +#define ALIAS(tc_fn) __attribute__ ((alias (#tc_fn), used)) + +void* operator new(size_t size) CPP_BADALLOC ALIAS(tc_new); +void operator delete(void* p) CPP_NOTHROW ALIAS(tc_delete); +void* operator new[](size_t size) CPP_BADALLOC ALIAS(tc_newarray); +void operator delete[](void* p) CPP_NOTHROW ALIAS(tc_deletearray); +void* operator new(size_t size, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_new_nothrow); +void* operator new[](size_t size, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_newarray_nothrow); +void operator delete(void* p, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_delete_nothrow); +void operator delete[](void* p, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_deletearray_nothrow); + +#if defined(ENABLE_SIZED_DELETE) + +void operator delete(void *p, size_t size) CPP_NOTHROW + ALIAS(tc_delete_sized); +void operator delete[](void *p, size_t size) CPP_NOTHROW + ALIAS(tc_deletearray_sized); + +#elif defined(ENABLE_DYNAMIC_SIZED_DELETE) && \ + (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 + +static void delegate_sized_delete(void *p, size_t s) { + (operator delete)(p); +} + +static void delegate_sized_deletearray(void *p, size_t s) { + (operator delete[])(p); +} + +extern "C" __attribute__((weak)) +int tcmalloc_sized_delete_enabled(void); + +static bool sized_delete_enabled(void) { + if (tcmalloc_sized_delete_enabled != 0) { + return !!tcmalloc_sized_delete_enabled(); + } + + const char *flag = TCMallocGetenvSafe("TCMALLOC_ENABLE_SIZED_DELETE"); + return tcmalloc::commandlineflags::StringToBool(flag, false); +} + +extern "C" { + +static void *resolve_delete_sized(void) { + if (sized_delete_enabled()) { + return reinterpret_cast(tc_delete_sized); + } + return reinterpret_cast(delegate_sized_delete); +} + +static void *resolve_deletearray_sized(void) { + if (sized_delete_enabled()) { + return reinterpret_cast(tc_deletearray_sized); + } + return reinterpret_cast(delegate_sized_deletearray); +} + +} + +void operator delete(void *p, size_t size) CPP_NOTHROW + __attribute__((ifunc("resolve_delete_sized"))); +void operator delete[](void *p, size_t size) CPP_NOTHROW + __attribute__((ifunc("resolve_deletearray_sized"))); + +#else /* !ENABLE_SIZED_DELETE && !ENABLE_DYN_SIZED_DELETE */ + +void operator delete(void *p, size_t size) CPP_NOTHROW + ALIAS(tc_delete_sized); +void operator delete[](void *p, size_t size) CPP_NOTHROW + ALIAS(tc_deletearray_sized); + +#endif /* !ENABLE_SIZED_DELETE && !ENABLE_DYN_SIZED_DELETE */ + +#if defined(ENABLE_ALIGNED_NEW_DELETE) + +void* operator new(size_t size, std::align_val_t al) + ALIAS(tc_new_aligned); +void operator delete(void* p, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_delete_aligned); +void* operator new[](size_t size, std::align_val_t al) + ALIAS(tc_newarray_aligned); +void operator delete[](void* p, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_deletearray_aligned); +void* operator new(size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_new_aligned_nothrow); +void* operator new[](size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_newarray_aligned_nothrow); +void operator delete(void* p, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_delete_aligned_nothrow); +void operator delete[](void* p, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW + ALIAS(tc_deletearray_aligned_nothrow); + +#if defined(ENABLE_SIZED_DELETE) + +void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_delete_sized_aligned); +void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_deletearray_sized_aligned); + +#else /* defined(ENABLE_SIZED_DELETE) */ + +#if defined(ENABLE_DYNAMIC_SIZED_DELETE) && \ + (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 + +static void delegate_sized_aligned_delete(void *p, size_t s, std::align_val_t al) { + (operator delete)(p, al); +} + +static void delegate_sized_aligned_deletearray(void *p, size_t s, std::align_val_t al) { + (operator delete[])(p, al); +} + +extern "C" { + +static void *resolve_delete_sized_aligned(void) { + if (sized_delete_enabled()) { + return reinterpret_cast(tc_delete_sized_aligned); + } + return reinterpret_cast(delegate_sized_aligned_delete); +} + +static void *resolve_deletearray_sized_aligned(void) { + if (sized_delete_enabled()) { + return reinterpret_cast(tc_deletearray_sized_aligned); + } + return reinterpret_cast(delegate_sized_aligned_deletearray); +} + +} + +void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW + __attribute__((ifunc("resolve_delete_sized_aligned"))); +void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW + __attribute__((ifunc("resolve_deletearray_sized_aligned"))); + +#else /* defined(ENABLE_DYN_SIZED_DELETE) */ + +void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_delete_sized_aligned); +void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW + ALIAS(tc_deletearray_sized_aligned); + +#endif /* defined(ENABLE_DYN_SIZED_DELETE) */ + +#endif /* defined(ENABLE_SIZED_DELETE) */ + +#endif /* defined(ENABLE_ALIGNED_NEW_DELETE) */ extern "C" { void* malloc(size_t size) __THROW ALIAS(tc_malloc); @@ -78,6 +214,7 @@ extern "C" { void* calloc(size_t n, size_t size) __THROW ALIAS(tc_calloc); void cfree(void* ptr) __THROW ALIAS(tc_cfree); void* memalign(size_t align, size_t s) __THROW ALIAS(tc_memalign); + void* aligned_alloc(size_t align, size_t s) __THROW ALIAS(tc_memalign); void* valloc(size_t size) __THROW ALIAS(tc_valloc); void* pvalloc(size_t size) __THROW ALIAS(tc_pvalloc); int posix_memalign(void** r, size_t a, size_t s) __THROW diff --git a/tpl/gperftools/src/libc_override_glibc.h b/tpl/gperftools/src/libc_override_glibc.h index b6843e1..3269213 100644 --- a/tpl/gperftools/src/libc_override_glibc.h +++ b/tpl/gperftools/src/libc_override_glibc.h @@ -38,9 +38,6 @@ #include #include // for __GLIBC__ -#ifdef HAVE_SYS_CDEFS_H -#include // for __THROW -#endif #include #ifndef __GLIBC__ @@ -89,60 +86,6 @@ extern "C" { #endif // #if defined(__GNUC__) && !defined(__MACH__) - -// We also have to hook libc malloc. While our work with weak symbols -// should make sure libc malloc is never called in most situations, it -// can be worked around by shared libraries with the DEEPBIND -// environment variable set. The below hooks libc to call our malloc -// routines even in that situation. In other situations, this hook -// should never be called. -extern "C" { -static void* glibc_override_malloc(size_t size, const void *caller) { - return tc_malloc(size); -} -static void* glibc_override_realloc(void *ptr, size_t size, - const void *caller) { - return tc_realloc(ptr, size); -} -static void glibc_override_free(void *ptr, const void *caller) { - tc_free(ptr); -} -static void* glibc_override_memalign(size_t align, size_t size, - const void *caller) { - return tc_memalign(align, size); -} - -// We should be using __malloc_initialize_hook here, like the #if 0 -// code below. (See http://swoolley.org/man.cgi/3/malloc_hook.) -// However, this causes weird linker errors with programs that link -// with -static, so instead we just assign the vars directly at -// static-constructor time. That should serve the same effect of -// making sure the hooks are set before the first malloc call the -// program makes. -#if 0 -#include // for __malloc_hook, etc. -void glibc_override_malloc_init_hook(void) { - __malloc_hook = glibc_override_malloc; - __realloc_hook = glibc_override_realloc; - __free_hook = glibc_override_free; - __memalign_hook = glibc_override_memalign; -} - -void (* MALLOC_HOOK_MAYBE_VOLATILE __malloc_initialize_hook)(void) - = &glibc_override_malloc_init_hook; -#endif - -void* (* MALLOC_HOOK_MAYBE_VOLATILE __malloc_hook)(size_t, const void*) - = &glibc_override_malloc; -void* (* MALLOC_HOOK_MAYBE_VOLATILE __realloc_hook)(void*, size_t, const void*) - = &glibc_override_realloc; -void (* MALLOC_HOOK_MAYBE_VOLATILE __free_hook)(void*, const void*) - = &glibc_override_free; -void* (* MALLOC_HOOK_MAYBE_VOLATILE __memalign_hook)(size_t,size_t, const void*) - = &glibc_override_memalign; - -} // extern "C" - // No need to write ReplaceSystemAlloc(); one of the #includes above // did it for us. diff --git a/tpl/gperftools/src/libc_override_osx.h b/tpl/gperftools/src/libc_override_osx.h index b801f22..9d5d611 100644 --- a/tpl/gperftools/src/libc_override_osx.h +++ b/tpl/gperftools/src/libc_override_osx.h @@ -211,6 +211,33 @@ extern "C" { size_t malloc_usable_size(void* p) { return tc_malloc_size(p); } } // extern "C" +static malloc_zone_t *get_default_zone() { + malloc_zone_t **zones = NULL; + unsigned int num_zones = 0; + + /* + * On OSX 10.12, malloc_default_zone returns a special zone that is not + * present in the list of registered zones. That zone uses a "lite zone" + * if one is present (apparently enabled when malloc stack logging is + * enabled), or the first registered zone otherwise. In practice this + * means unless malloc stack logging is enabled, the first registered + * zone is the default. + * So get the list of zones to get the first one, instead of relying on + * malloc_default_zone. + */ + if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, (vm_address_t**) &zones, + &num_zones)) { + /* Reset the value in case the failure happened after it was set. */ + num_zones = 0; + } + + if (num_zones) + return zones[0]; + + return malloc_default_zone(); +} + + static void ReplaceSystemAlloc() { static malloc_introspection_t tcmalloc_introspection; memset(&tcmalloc_introspection, 0, sizeof(tcmalloc_introspection)); @@ -273,7 +300,7 @@ static void ReplaceSystemAlloc() { // zone. The default zone is then re-registered to ensure that // allocations made from it earlier will be handled correctly. // Things are not guaranteed to work that way, but it's how they work now. - malloc_zone_t *default_zone = malloc_default_zone(); + malloc_zone_t *default_zone = get_default_zone(); malloc_zone_unregister(default_zone); malloc_zone_register(default_zone); } diff --git a/tpl/gperftools/src/libc_override_redefine.h b/tpl/gperftools/src/libc_override_redefine.h index a1e50f8..4d61b25 100644 --- a/tpl/gperftools/src/libc_override_redefine.h +++ b/tpl/gperftools/src/libc_override_redefine.h @@ -42,49 +42,86 @@ #ifndef TCMALLOC_LIBC_OVERRIDE_REDEFINE_H_ #define TCMALLOC_LIBC_OVERRIDE_REDEFINE_H_ -#ifdef HAVE_SYS_CDEFS_H -#include // for __THROW -#endif - -#ifndef __THROW // I guess we're not on a glibc-like system -# define __THROW // __THROW is just an optimization, so ok to make it "" -#endif - void* operator new(size_t size) { return tc_new(size); } -void operator delete(void* p) __THROW { tc_delete(p); } +void operator delete(void* p) CPP_NOTHROW { tc_delete(p); } void* operator new[](size_t size) { return tc_newarray(size); } -void operator delete[](void* p) __THROW { tc_deletearray(p); } -void* operator new(size_t size, const std::nothrow_t& nt) __THROW { +void operator delete[](void* p) CPP_NOTHROW { tc_deletearray(p); } +void* operator new(size_t size, const std::nothrow_t& nt) CPP_NOTHROW { return tc_new_nothrow(size, nt); } -void* operator new[](size_t size, const std::nothrow_t& nt) __THROW { +void* operator new[](size_t size, const std::nothrow_t& nt) CPP_NOTHROW { return tc_newarray_nothrow(size, nt); } -void operator delete(void* ptr, const std::nothrow_t& nt) __THROW { +void operator delete(void* ptr, const std::nothrow_t& nt) CPP_NOTHROW { return tc_delete_nothrow(ptr, nt); } -void operator delete[](void* ptr, const std::nothrow_t& nt) __THROW { +void operator delete[](void* ptr, const std::nothrow_t& nt) CPP_NOTHROW { return tc_deletearray_nothrow(ptr, nt); } + +#ifdef ENABLE_SIZED_DELETE +void operator delete(void* p, size_t s) CPP_NOTHROW { tc_delete_sized(p, s); } +void operator delete[](void* p, size_t s) CPP_NOTHROW{ tc_deletearray_sized(p, s);} +#endif + +#if defined(ENABLE_ALIGNED_NEW_DELETE) + +void* operator new(size_t size, std::align_val_t al) { + return tc_new_aligned(size, al); +} +void operator delete(void* p, std::align_val_t al) CPP_NOTHROW { + tc_delete_aligned(p, al); +} +void* operator new[](size_t size, std::align_val_t al) { + return tc_newarray_aligned(size, al); +} +void operator delete[](void* p, std::align_val_t al) CPP_NOTHROW { + tc_deletearray_aligned(p, al); +} +void* operator new(size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW { + return tc_new_aligned_nothrow(size, al, nt); +} +void* operator new[](size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW { + return tc_newarray_aligned_nothrow(size, al, nt); +} +void operator delete(void* ptr, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW { + return tc_delete_aligned_nothrow(ptr, al, nt); +} +void operator delete[](void* ptr, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW { + return tc_deletearray_aligned_nothrow(ptr, al, nt); +} + +#ifdef ENABLE_SIZED_DELETE +void operator delete(void* p, size_t s, std::align_val_t al) CPP_NOTHROW { + tc_delete_sized_aligned(p, s, al); +} +void operator delete[](void* p, size_t s, std::align_val_t al) CPP_NOTHROW { + tc_deletearray_sized_aligned(p, s, al); +} +#endif + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + extern "C" { - void* malloc(size_t s) __THROW { return tc_malloc(s); } - void free(void* p) __THROW { tc_free(p); } - void* realloc(void* p, size_t s) __THROW { return tc_realloc(p, s); } - void* calloc(size_t n, size_t s) __THROW { return tc_calloc(n, s); } - void cfree(void* p) __THROW { tc_cfree(p); } - void* memalign(size_t a, size_t s) __THROW { return tc_memalign(a, s); } - void* valloc(size_t s) __THROW { return tc_valloc(s); } - void* pvalloc(size_t s) __THROW { return tc_pvalloc(s); } - int posix_memalign(void** r, size_t a, size_t s) __THROW { + void* malloc(size_t s) { return tc_malloc(s); } + void free(void* p) { tc_free(p); } + void* realloc(void* p, size_t s) { return tc_realloc(p, s); } + void* calloc(size_t n, size_t s) { return tc_calloc(n, s); } + void cfree(void* p) { tc_cfree(p); } + void* memalign(size_t a, size_t s) { return tc_memalign(a, s); } + void* aligned_alloc(size_t a, size_t s) { return tc_memalign(a, s); } + void* valloc(size_t s) { return tc_valloc(s); } + void* pvalloc(size_t s) { return tc_pvalloc(s); } + int posix_memalign(void** r, size_t a, size_t s) { return tc_posix_memalign(r, a, s); } - void malloc_stats(void) __THROW { tc_malloc_stats(); } - int mallopt(int cmd, int v) __THROW { return tc_mallopt(cmd, v); } + void malloc_stats(void) { tc_malloc_stats(); } + int mallopt(int cmd, int v) { return tc_mallopt(cmd, v); } #ifdef HAVE_STRUCT_MALLINFO - struct mallinfo mallinfo(void) __THROW { return tc_mallinfo(); } + struct mallinfo mallinfo(void) { return tc_mallinfo(); } #endif - size_t malloc_size(void* p) __THROW { return tc_malloc_size(p); } - size_t malloc_usable_size(void* p) __THROW { return tc_malloc_size(p); } + size_t malloc_size(void* p) { return tc_malloc_size(p); } + size_t malloc_usable_size(void* p) { return tc_malloc_size(p); } } // extern "C" // No need to do anything at tcmalloc-registration time: we do it all diff --git a/tpl/gperftools/src/linked_list.h b/tpl/gperftools/src/linked_list.h index 66a0741..f25b6f8 100644 --- a/tpl/gperftools/src/linked_list.h +++ b/tpl/gperftools/src/linked_list.h @@ -50,8 +50,9 @@ inline void SLL_SetNext(void *t, void *n) { } inline void SLL_Push(void **list, void *element) { - SLL_SetNext(element, *list); + void *next = *list; *list = element; + SLL_SetNext(element, next); } inline void *SLL_Pop(void **list) { @@ -60,6 +61,17 @@ inline void *SLL_Pop(void **list) { return result; } +inline bool SLL_TryPop(void **list, void **rv) { + void *result = *list; + if (!result) { + return false; + } + void *next = SLL_Next(*list); + *list = next; + *rv = result; + return true; +} + // Remove N elements from a linked list to which head points. head will be // modified to point to the new head. start and end will point to the first // and last nodes of the range. Note that end will point to NULL after this diff --git a/tpl/gperftools/src/malloc_extension.cc b/tpl/gperftools/src/malloc_extension.cc index abfcde5..6e69552 100644 --- a/tpl/gperftools/src/malloc_extension.cc +++ b/tpl/gperftools/src/malloc_extension.cc @@ -45,9 +45,9 @@ #include #include "base/dynamic_annotations.h" #include "base/sysinfo.h" // for FillProcSelfMaps -// #ifndef NO_HEAP_CHECK -// #include "gperftools/heap-checker.h" -// #endif +#ifndef NO_HEAP_CHECK +#include "gperftools/heap-checker.h" +#endif #include "gperftools/malloc_extension.h" #include "gperftools/malloc_extension_c.h" #include "maybe_threads.h" @@ -193,6 +193,14 @@ void MallocExtension::GetFreeListSizes( v->clear(); } +size_t MallocExtension::GetThreadCacheSize() { + return 0; +} + +void MallocExtension::MarkThreadTemporarilyIdle() { + // Default implementation does nothing +} + // The current malloc extension object. static MallocExtension* current_instance; @@ -202,9 +210,9 @@ static void InitModule() { return; } current_instance = new MallocExtension; -// #ifndef NO_HEAP_CHECK -// HeapLeakChecker::IgnoreObject(current_instance); -// #endif +#ifndef NO_HEAP_CHECK + HeapLeakChecker::IgnoreObject(current_instance); +#endif } REGISTER_MODULE_INITIALIZER(malloc_extension_init, InitModule()) @@ -369,6 +377,8 @@ C_SHIM(ReleaseFreeMemory, void, (void), ()); C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes)); C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size)); C_SHIM(GetAllocatedSize, size_t, (const void* p), (p)); +C_SHIM(GetThreadCacheSize, size_t, (void), ()); +C_SHIM(MarkThreadTemporarilyIdle, void, (void), ()); // Can't use the shim here because of the need to translate the enums. extern "C" diff --git a/tpl/gperftools/src/malloc_hook-inl.h b/tpl/gperftools/src/malloc_hook-inl.h index 9e74ec8..30375d6 100644 --- a/tpl/gperftools/src/malloc_hook-inl.h +++ b/tpl/gperftools/src/malloc_hook-inl.h @@ -44,6 +44,8 @@ #include "base/basictypes.h" #include +#include "common.h" // for UNLIKELY + namespace base { namespace internal { // Capacity of 8 means that HookList is 9 words. @@ -121,7 +123,7 @@ inline MallocHook::NewHook MallocHook::GetNewHook() { } inline void MallocHook::InvokeNewHook(const void* p, size_t s) { - if (!base::internal::new_hooks_.empty()) { + if (PREDICT_FALSE(!base::internal::new_hooks_.empty())) { InvokeNewHookSlow(p, s); } } @@ -132,7 +134,7 @@ inline MallocHook::DeleteHook MallocHook::GetDeleteHook() { } inline void MallocHook::InvokeDeleteHook(const void* p) { - if (!base::internal::delete_hooks_.empty()) { + if (PREDICT_FALSE(!base::internal::delete_hooks_.empty())) { InvokeDeleteHookSlow(p); } } diff --git a/tpl/gperftools/src/malloc_hook.cc b/tpl/gperftools/src/malloc_hook.cc index 681d8a2..64c2165 100644 --- a/tpl/gperftools/src/malloc_hook.cc +++ b/tpl/gperftools/src/malloc_hook.cc @@ -49,6 +49,7 @@ #include #include "base/logging.h" #include "base/spinlock.h" +#include "maybe_emergency_malloc.h" #include "maybe_threads.h" #include "malloc_hook-inl.h" #include @@ -491,10 +492,16 @@ MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook) { void MallocHook::InvokeNewHookSlow(const void* p, size_t s) { + if (tcmalloc::IsEmergencyPtr(p)) { + return; + } INVOKE_HOOKS(NewHook, new_hooks_, (p, s)); } void MallocHook::InvokeDeleteHookSlow(const void* p) { + if (tcmalloc::IsEmergencyPtr(p)) { + return; + } INVOKE_HOOKS(DeleteHook, delete_hooks_, (p)); } @@ -560,6 +567,8 @@ void MallocHook::InvokeSbrkHookSlow(const void* result, ptrdiff_t increment) { #undef INVOKE_HOOKS +#ifndef NO_TCMALLOC_SAMPLES + DEFINE_ATTRIBUTE_SECTION_VARS(google_malloc); DECLARE_ATTRIBUTE_SECTION_VARS(google_malloc); // actual functions are in debugallocation.cc or tcmalloc.cc @@ -605,6 +614,8 @@ static inline void CheckInHookCaller() { } } +#endif // !NO_TCMALLOC_SAMPLES + // We can improve behavior/compactness of this function // if we pass a generic test function (with a generic arg) // into the implementations for GetStackTrace instead of the skip_count. @@ -636,6 +647,14 @@ extern "C" int MallocHook_GetCallerStackTrace(void** result, int max_depth, return 0; for (int i = 0; i < depth; ++i) { // stack[0] is our immediate caller if (InHookCaller(stack[i])) { + // fast-path to slow-path calls may be implemented by compiler + // as non-tail calls. Causing two functions on stack trace to be + // inside google_malloc. In such case we're skipping to + // outermost such frame since this is where malloc stack frames + // really start. + while (i + 1 < depth && InHookCaller(stack[i+1])) { + i++; + } RAW_VLOG(10, "Found hooked allocator at %d: %p <- %p", i, stack[i], stack[i+1]); i += 1; // skip hook caller frame diff --git a/tpl/gperftools/src/malloc_hook_mmap_linux.h b/tpl/gperftools/src/malloc_hook_mmap_linux.h old mode 100755 new mode 100644 index 0f531db..34de715 --- a/tpl/gperftools/src/malloc_hook_mmap_linux.h +++ b/tpl/gperftools/src/malloc_hook_mmap_linux.h @@ -52,12 +52,16 @@ // I test for 64-bit first so I don't have to do things like // '#if (defined(__mips__) && !defined(__MIPS64__))' as a mips32 check. -#if defined(__x86_64__) || defined(__PPC64__) || defined(__aarch64__) || (defined(_MIPS_SIM) && _MIPS_SIM == _ABI64) +#if defined(__x86_64__) \ + || defined(__PPC64__) \ + || defined(__aarch64__) \ + || (defined(_MIPS_SIM) && (_MIPS_SIM == _ABI64 || _MIPS_SIM == _ABIN32)) \ + || defined(__s390__) static inline void* do_mmap64(void *start, size_t length, int prot, int flags, - int fd, __off64_t offset) __THROW { - return sys_mmap(start, length, prot, flags, fd, offset); + int fd, off64_t offset) __THROW { + return (void*)syscall(SYS_mmap, start, length, prot, flags, fd, offset); } #define MALLOC_HOOK_HAVE_DO_MMAP64 1 @@ -67,7 +71,7 @@ static inline void* do_mmap64(void *start, size_t length, static inline void* do_mmap64(void *start, size_t length, int prot, int flags, - int fd, __off64_t offset) __THROW { + int fd, off64_t offset) __THROW { void *result; // Try mmap2() unless it's not supported @@ -133,12 +137,13 @@ static inline void* do_mmap64(void *start, size_t length, // malloc_hook section, // so that MallocHook::GetCallerStackTrace can function accurately: -// Make sure mmap doesn't get #define'd away by +// Make sure mmap64 and mmap doesn't get #define'd away by +# undef mmap64 # undef mmap extern "C" { void* mmap64(void *start, size_t length, int prot, int flags, - int fd, __off64_t offset ) __THROW + int fd, off64_t offset ) __THROW ATTRIBUTE_SECTION(malloc_hook); void* mmap(void *start, size_t length,int prot, int flags, int fd, off_t offset) __THROW @@ -148,12 +153,12 @@ extern "C" { void* mremap(void* old_addr, size_t old_size, size_t new_size, int flags, ...) __THROW ATTRIBUTE_SECTION(malloc_hook); - void* sbrk(ptrdiff_t increment) __THROW + void* sbrk(intptr_t increment) __THROW ATTRIBUTE_SECTION(malloc_hook); } extern "C" void* mmap64(void *start, size_t length, int prot, int flags, - int fd, __off64_t offset) __THROW { + int fd, off64_t offset) __THROW { MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset); void *result; if (!MallocHook::InvokeMmapReplacement( @@ -185,7 +190,7 @@ extern "C" int munmap(void* start, size_t length) __THROW { MallocHook::InvokeMunmapHook(start, length); int result; if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) { - result = sys_munmap(start, length); + result = syscall(SYS_munmap, start, length); } return result; } @@ -196,17 +201,18 @@ extern "C" void* mremap(void* old_addr, size_t old_size, size_t new_size, va_start(ap, flags); void *new_address = va_arg(ap, void *); va_end(ap); - void* result = sys_mremap(old_addr, old_size, new_size, flags, new_address); + void* result = (void*)syscall(SYS_mremap, old_addr, old_size, new_size, flags, + new_address); MallocHook::InvokeMremapHook(result, old_addr, old_size, new_size, flags, new_address); return result; } -#ifndef __UCLIBC__ +#ifdef HAVE___SBRK // libc's version: -extern "C" void* __sbrk(ptrdiff_t increment); +extern "C" void* __sbrk(intptr_t increment); -extern "C" void* sbrk(ptrdiff_t increment) __THROW { +extern "C" void* sbrk(intptr_t increment) __THROW { MallocHook::InvokePreSbrkHook(increment); void *result = __sbrk(increment); MallocHook::InvokeSbrkHook(result, increment); diff --git a/tpl/gperftools/src/maybe_emergency_malloc.h b/tpl/gperftools/src/maybe_emergency_malloc.h new file mode 100644 index 0000000..250ecf0 --- /dev/null +++ b/tpl/gperftools/src/maybe_emergency_malloc.h @@ -0,0 +1,55 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Copyright (c) 2014, gperftools Contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MAYBE_EMERGENCY_MALLOC_H +#define MAYBE_EMERGENCY_MALLOC_H + +#include "config.h" + +#ifdef ENABLE_EMERGENCY_MALLOC + +#include "emergency_malloc.h" + +#else + +namespace tcmalloc { + static inline void *EmergencyMalloc(size_t size) {return NULL;} + static inline void EmergencyFree(void *p) {} + static inline void *EmergencyCalloc(size_t n, size_t elem_size) {return NULL;} + static inline void *EmergencyRealloc(void *old_ptr, size_t new_size) {return NULL;} + + static inline bool IsEmergencyPtr(const void *_ptr) { + return false; + } +} + +#endif // ENABLE_EMERGENCY_MALLOC + +#endif diff --git a/tpl/gperftools/src/maybe_threads.cc b/tpl/gperftools/src/maybe_threads.cc index acfc99a..ef7e582 100644 --- a/tpl/gperftools/src/maybe_threads.cc +++ b/tpl/gperftools/src/maybe_threads.cc @@ -69,10 +69,12 @@ extern "C" { __THROW ATTRIBUTE_WEAK; int pthread_once(pthread_once_t *, void (*)(void)) ATTRIBUTE_WEAK; +#ifdef HAVE_FORK int pthread_atfork(void (*__prepare) (void), void (*__parent) (void), void (*__child) (void)) __THROW ATTRIBUTE_WEAK; +#endif } #define MAX_PERTHREAD_VALS 16 @@ -161,6 +163,8 @@ int perftools_pthread_once(pthread_once_t *ctl, } } +#ifdef HAVE_FORK + void perftools_pthread_atfork(void (*before)(), void (*parent_after)(), void (*child_after)()) { @@ -169,3 +173,5 @@ void perftools_pthread_atfork(void (*before)(), CHECK(rv == 0); } } + +#endif diff --git a/tpl/gperftools/src/memfs_malloc.cc b/tpl/gperftools/src/memfs_malloc.cc index 52a7f13..fd26daf 100644 --- a/tpl/gperftools/src/memfs_malloc.cc +++ b/tpl/gperftools/src/memfs_malloc.cc @@ -97,7 +97,7 @@ class HugetlbSysAllocator: public SysAllocator { fallback_(fallback) { } - void* Alloc(size_t size, size_t *actual_size, size_t alignment) override; + void* Alloc(size_t size, size_t *actual_size, size_t alignment); bool Initialize(); bool failed_; // Whether failed to allocate memory. @@ -111,7 +111,10 @@ class HugetlbSysAllocator: public SysAllocator { SysAllocator* fallback_; // Default system allocator to fall back to. }; -static char hugetlb_space[sizeof(HugetlbSysAllocator)]; +static union { + char buf[sizeof(HugetlbSysAllocator)]; + void *ptr; +} hugetlb_space; // No locking needed here since we assume that tcmalloc calls // us with an internal lock held (see tcmalloc/system-alloc.cc). @@ -258,7 +261,8 @@ bool HugetlbSysAllocator::Initialize() { REGISTER_MODULE_INITIALIZER(memfs_malloc, { if (FLAGS_memfs_malloc_path.length()) { SysAllocator* alloc = MallocExtension::instance()->GetSystemAllocator(); - HugetlbSysAllocator* hp = new (hugetlb_space) HugetlbSysAllocator(alloc); + HugetlbSysAllocator* hp = + new (hugetlb_space.buf) HugetlbSysAllocator(alloc); if (hp->Initialize()) { MallocExtension::instance()->SetSystemAllocator(hp); } diff --git a/tpl/gperftools/src/memory_region_map.cc b/tpl/gperftools/src/memory_region_map.cc old mode 100755 new mode 100644 index 841d6f3..06b6fb0 --- a/tpl/gperftools/src/memory_region_map.cc +++ b/tpl/gperftools/src/memory_region_map.cc @@ -234,6 +234,9 @@ void MemoryRegionMap::Init(int max_stack_depth, bool use_buckets) { memset(bucket_table_, 0, table_bytes); num_buckets_ = 0; } + if (regions_ == NULL) { // init regions_ + InitRegionSetLocked(); + } Unlock(); RAW_VLOG(10, "MemoryRegionMap Init done"); } @@ -536,6 +539,15 @@ void MemoryRegionMap::RestoreSavedBucketsLocked() { } } +inline void MemoryRegionMap::InitRegionSetLocked() { + RAW_VLOG(12, "Initializing region set"); + regions_ = regions_rep.region_set(); + recursive_insert = true; + new (regions_) RegionSet(); + HandleSavedRegionsLocked(&DoInsertRegionLocked); + recursive_insert = false; +} + inline void MemoryRegionMap::InsertRegionLocked(const Region& region) { RAW_CHECK(LockIsHeld(), "should be held (by this thread)"); // We can be called recursively, because RegionSet constructor @@ -556,12 +568,7 @@ inline void MemoryRegionMap::InsertRegionLocked(const Region& region) { saved_regions[saved_regions_count++] = region; } else { // not a recusrive call if (regions_ == NULL) { // init regions_ - RAW_VLOG(12, "Initializing region set"); - regions_ = regions_rep.region_set(); - recursive_insert = true; - new(regions_) RegionSet(); - HandleSavedRegionsLocked(&DoInsertRegionLocked); - recursive_insert = false; + InitRegionSetLocked(); } recursive_insert = true; // Do the actual insertion work to put new regions into regions_: diff --git a/tpl/gperftools/src/memory_region_map.h b/tpl/gperftools/src/memory_region_map.h index ec388e1..f774994 100644 --- a/tpl/gperftools/src/memory_region_map.h +++ b/tpl/gperftools/src/memory_region_map.h @@ -362,6 +362,9 @@ class MemoryRegionMap { // table where all buckets eventually should be. static void RestoreSavedBucketsLocked(); + // Initialize RegionSet regions_. + inline static void InitRegionSetLocked(); + // Wrapper around DoInsertRegionLocked // that handles the case of recursive allocator calls. inline static void InsertRegionLocked(const Region& region); diff --git a/tpl/gperftools/src/packed-cache-inl.h b/tpl/gperftools/src/packed-cache-inl.h index 0946260..7c216e5 100644 --- a/tpl/gperftools/src/packed-cache-inl.h +++ b/tpl/gperftools/src/packed-cache-inl.h @@ -118,6 +118,7 @@ #include // for uintptr_t #endif #include "base/basictypes.h" +#include "common.h" #include "internal_logging.h" // A safe way of doing "(1 << n) - 1" -- without worrying about overflow @@ -128,14 +129,16 @@ // The types K and V provide upper bounds on the number of valid keys // and values, but we explicitly require the keys to be less than -// 2^kKeybits and the values to be less than 2^kValuebits. The size of -// the table is controlled by kHashbits, and the type of each entry in -// the cache is T. See also the big comment at the top of the file. -template +// 2^kKeybits and the values to be less than 2^kValuebits. The size +// of the table is controlled by kHashbits, and the type of each entry +// in the cache is uintptr_t (native machine word). See also the big +// comment at the top of the file. +template class PackedCache { public: + typedef uintptr_t T; typedef uintptr_t K; - typedef size_t V; + typedef uint32 V; #ifdef TCMALLOC_SMALL_BUT_SLOW // Decrease the size map cache if running in the small memory mode. static const int kHashbits = 12; @@ -143,89 +146,63 @@ class PackedCache { static const int kHashbits = 16; #endif static const int kValuebits = 7; - static const bool kUseWholeKeys = kKeybits + kValuebits <= 8 * sizeof(T); + // one bit after value bits + static const int kInvalidMask = 0x80; - explicit PackedCache(V initial_value) { - COMPILE_ASSERT(kKeybits <= sizeof(K) * 8, key_size); - COMPILE_ASSERT(kValuebits <= sizeof(V) * 8, value_size); + explicit PackedCache() { + COMPILE_ASSERT(kKeybits + kValuebits + 1 <= 8 * sizeof(T), use_whole_keys); COMPILE_ASSERT(kHashbits <= kKeybits, hash_function); - COMPILE_ASSERT(kKeybits - kHashbits + kValuebits <= kTbits, - entry_size_must_be_big_enough); - Clear(initial_value); + COMPILE_ASSERT(kHashbits >= kValuebits + 1, small_values_space); + Clear(); } - void Put(K key, V value) { + bool TryGet(K key, V* out) const { + // As with other code in this class, we touch array_ as few times + // as we can. Assuming entries are read atomically then certain + // races are harmless. ASSERT(key == (key & kKeyMask)); - ASSERT(value == (value & kValueMask)); - array_[Hash(key)] = KeyToUpper(key) | value; + T hash = Hash(key); + T expected_entry = key; + expected_entry &= ~N_ONES_(T, kHashbits); + T entry = array_[hash]; + entry ^= expected_entry; + if (PREDICT_FALSE(entry >= (1 << kValuebits))) { + return false; + } + *out = static_cast(entry); + return true; } - bool Has(K key) const { - ASSERT(key == (key & kKeyMask)); - return KeyMatch(array_[Hash(key)], key); + void Clear() { + // sets 'invalid' bit in every byte, include value byte + memset(const_cast(array_), kInvalidMask, sizeof(array_)); } - V GetOrDefault(K key, V default_value) const { - // As with other code in this class, we touch array_ as few times - // as we can. Assuming entries are read atomically (e.g., their - // type is uintptr_t on most hardware) then certain races are - // harmless. + void Put(K key, V value) { ASSERT(key == (key & kKeyMask)); - T entry = array_[Hash(key)]; - return KeyMatch(entry, key) ? EntryToValue(entry) : default_value; - } - - void Clear(V value) { ASSERT(value == (value & kValueMask)); - for (int i = 0; i < 1 << kHashbits; i++) { - ASSERT(kUseWholeKeys || KeyToUpper(i) == 0); - array_[i] = kUseWholeKeys ? (value | KeyToUpper(i)) : value; - } + array_[Hash(key)] = KeyToUpper(key) | value; } - private: - // We are going to pack a value and the upper part of a key (or a - // whole key) into an entry of type T. The UPPER type is for the - // upper part of a key, after the key has been masked and shifted - // for inclusion in an entry. - typedef T UPPER; - - static V EntryToValue(T t) { return t & kValueMask; } - - // If we have space for a whole key, we just shift it left. - // Otherwise kHashbits determines where in a K to find the upper - // part of the key, and kValuebits determines where in the entry to - // put it. - static UPPER KeyToUpper(K k) { - if (kUseWholeKeys) { - return static_cast(k) << kValuebits; - } else { - const int shift = kHashbits - kValuebits; - // Assume kHashbits >= kValuebits. It'd be easy to lift this assumption. - return static_cast(k >> shift) & kUpperMask; - } + void Invalidate(K key) { + ASSERT(key == (key & kKeyMask)); + array_[Hash(key)] = KeyToUpper(key) | kInvalidMask; } - static size_t Hash(K key) { - return static_cast(key) & N_ONES_(size_t, kHashbits); + private: + // we just wipe all hash bits out of key. I.e. clear lower + // kHashbits. We rely on compiler knowing value of Hash(k). + static T KeyToUpper(K k) { + return static_cast(k) ^ Hash(k); } - // Does the entry match the relevant part of the given key? - static bool KeyMatch(T entry, K key) { - return kUseWholeKeys ? - (entry >> kValuebits == key) : - ((KeyToUpper(key) ^ entry) & kUpperMask) == 0; + static T Hash(K key) { + return static_cast(key) & N_ONES_(size_t, kHashbits); } - static const int kTbits = 8 * sizeof(T); - static const int kUpperbits = kUseWholeKeys ? kKeybits : kKeybits - kHashbits; - // For masking a K. static const K kKeyMask = N_ONES_(K, kKeybits); - // For masking a T. - static const T kUpperMask = N_ONES_(T, kUpperbits) << kValuebits; - // For masking a V or a T. static const V kValueMask = N_ONES_(V, kValuebits); diff --git a/tpl/gperftools/src/page_heap.cc b/tpl/gperftools/src/page_heap.cc index f52ae2a..a3a202f 100644 --- a/tpl/gperftools/src/page_heap.cc +++ b/tpl/gperftools/src/page_heap.cc @@ -64,14 +64,12 @@ namespace tcmalloc { PageHeap::PageHeap() : pagemap_(MetaDataAlloc), - pagemap_cache_(0), scavenge_counter_(0), // Start scavenging at kMaxPages list release_index_(kMaxPages), - aggressive_decommit_(false) { - COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits); - DLL_Init(&large_.normal); - DLL_Init(&large_.returned); + aggressive_decommit_(false), + min_system_alloc_(kMinSystemAlloc) { + COMPILE_ASSERT(kClassSizesMax <= (1 << PageMapCache::kValuebits), valuebits); for (int i = 0; i < kMaxPages; i++) { DLL_Init(&free_[i].normal); DLL_Init(&free_[i].returned); @@ -83,15 +81,15 @@ Span* PageHeap::SearchFreeAndLargeLists(Length n) { ASSERT(n > 0); // Find first size >= n that has a non-empty list - for (Length s = n; s < kMaxPages; s++) { - Span* ll = &free_[s].normal; + for (Length s = n; s <= kMaxPages; s++) { + Span* ll = &free_[s - 1].normal; // If we're lucky, ll is non-empty, meaning it has a suitable span. if (!DLL_IsEmpty(ll)) { ASSERT(ll->next->location == Span::ON_NORMAL_FREELIST); return Carve(ll->next, n); } // Alternatively, maybe there's a usable returned span. - ll = &free_[s].returned; + ll = &free_[s - 1].returned; if (!DLL_IsEmpty(ll)) { // We did not call EnsureLimit before, to avoid releasing the span // that will be taken immediately back. @@ -169,45 +167,38 @@ Span* PageHeap::New(Length n) { } Span* PageHeap::AllocLarge(Length n) { - // find the best span (closest to n in size). - // The following loops implements address-ordered best-fit. Span *best = NULL; + Span *best_normal = NULL; - // Search through normal list - for (Span* span = large_.normal.next; - span != &large_.normal; - span = span->next) { - if (span->length >= n) { - if ((best == NULL) - || (span->length < best->length) - || ((span->length == best->length) && (span->start < best->start))) { - best = span; - ASSERT(best->location == Span::ON_NORMAL_FREELIST); - } - } - } + // Create a Span to use as an upper bound. + Span bound; + bound.start = 0; + bound.length = n; - Span *bestNormal = best; + // First search the NORMAL spans.. + SpanSet::iterator place = large_normal_.upper_bound(SpanPtrWithLength(&bound)); + if (place != large_normal_.end()) { + best = place->span; + best_normal = best; + ASSERT(best->location == Span::ON_NORMAL_FREELIST); + } - // Search through released list in case it has a better fit - for (Span* span = large_.returned.next; - span != &large_.returned; - span = span->next) { - if (span->length >= n) { - if ((best == NULL) - || (span->length < best->length) - || ((span->length == best->length) && (span->start < best->start))) { - best = span; - ASSERT(best->location == Span::ON_RETURNED_FREELIST); - } - } + // Try to find better fit from RETURNED spans. + place = large_returned_.upper_bound(SpanPtrWithLength(&bound)); + if (place != large_returned_.end()) { + Span *c = place->span; + ASSERT(c->location == Span::ON_RETURNED_FREELIST); + if (best_normal == NULL + || c->length < best->length + || (c->length == best->length && c->start < best->start)) + best = place->span; } - if (best == bestNormal) { + if (best == best_normal) { return best == NULL ? NULL : Carve(best, n); } - // best comes from returned list. + // best comes from RETURNED set. if (EnsureLimit(n, false)) { return Carve(best, n); @@ -215,13 +206,13 @@ Span* PageHeap::AllocLarge(Length n) { if (EnsureLimit(n, true)) { // best could have been destroyed by coalescing. - // bestNormal is not a best-fit, and it could be destroyed as well. + // best_normal is not a best-fit, and it could be destroyed as well. // We retry, the limit is already ensured: return AllocLarge(n); } - // If bestNormal existed, EnsureLimit would succeeded: - ASSERT(bestNormal == NULL); + // If best_normal existed, EnsureLimit would succeeded: + ASSERT(best_normal == NULL); // We are not allowed to take best from returned list. return NULL; } @@ -245,16 +236,22 @@ Span* PageHeap::Split(Span* span, Length n) { } void PageHeap::CommitSpan(Span* span) { + ++stats_.commit_count; + TCMalloc_SystemCommit(reinterpret_cast(span->start << kPageShift), static_cast(span->length << kPageShift)); stats_.committed_bytes += span->length << kPageShift; + stats_.total_commit_bytes += (span->length << kPageShift); } bool PageHeap::DecommitSpan(Span* span) { + ++stats_.decommit_count; + bool rv = TCMalloc_SystemRelease(reinterpret_cast(span->start << kPageShift), static_cast(span->length << kPageShift)); if (rv) { stats_.committed_bytes -= span->length << kPageShift; + stats_.total_decommit_bytes += (span->length << kPageShift); } return rv; @@ -320,11 +317,29 @@ void PageHeap::Delete(Span* span) { ASSERT(Check()); } -bool PageHeap::MayMergeSpans(Span *span, Span *other) { - if (aggressive_decommit_) { - return other->location != Span::IN_USE; +// Given span we're about to free and other span (still on free list), +// checks if 'other' span is mergable with 'span'. If it is, removes +// other span from free list, performs aggressive decommit if +// necessary and returns 'other' span. Otherwise 'other' span cannot +// be merged and is left untouched. In that case NULL is returned. +Span* PageHeap::CheckAndHandlePreMerge(Span* span, Span* other) { + if (other == NULL) { + return other; + } + // if we're in aggressive decommit mode and span is decommitted, + // then we try to decommit adjacent span. + if (aggressive_decommit_ && other->location == Span::ON_NORMAL_FREELIST + && span->location == Span::ON_RETURNED_FREELIST) { + bool worked = DecommitSpan(other); + if (!worked) { + return NULL; + } + } else if (other->location != span->location) { + return NULL; } - return span->location == other->location; + + RemoveFromFreeList(other); + return other; } void PageHeap::MergeIntoFreeList(Span* span) { @@ -340,15 +355,6 @@ void PageHeap::MergeIntoFreeList(Span* span) { // // The following applies if aggressive_decommit_ is enabled: // - // Note that the adjacent spans we merge into "span" may come out of a - // "normal" (committed) list, and cleanly merge with our IN_USE span, which - // is implicitly committed. If the adjacents spans are on the "returned" - // (decommitted) list, then we must get both spans into the same state before - // or after we coalesce them. The current code always decomits. This is - // achieved by blindly decommitting the entire coalesced region, which may - // include any combination of committed and decommitted spans, at the end of - // the method. - // TODO(jar): "Always decommit" causes some extra calls to commit when we are // called in GrowHeap() during an allocation :-/. We need to eval the cost of // that oscillation, and possibly do something to reduce it. @@ -356,65 +362,62 @@ void PageHeap::MergeIntoFreeList(Span* span) { // TODO(jar): We need a better strategy for deciding to commit, or decommit, // based on memory usage and free heap sizes. - uint64_t temp_committed = 0; - const PageID p = span->start; const Length n = span->length; - Span* prev = GetDescriptor(p-1); - if (prev != NULL && MayMergeSpans(span, prev)) { + + if (aggressive_decommit_ && span->location == Span::ON_NORMAL_FREELIST) { + if (DecommitSpan(span)) { + span->location = Span::ON_RETURNED_FREELIST; + } + } + + Span* prev = CheckAndHandlePreMerge(span, GetDescriptor(p-1)); + if (prev != NULL) { // Merge preceding span into this span ASSERT(prev->start + prev->length == p); const Length len = prev->length; - if (aggressive_decommit_ && prev->location == Span::ON_RETURNED_FREELIST) { - // We're about to put the merge span into the returned freelist and call - // DecommitSpan() on it, which will mark the entire span including this - // one as released and decrease stats_.committed_bytes by the size of the - // merged span. To make the math work out we temporarily increase the - // stats_.committed_bytes amount. - temp_committed = prev->length << kPageShift; - } - RemoveFromFreeList(prev); DeleteSpan(prev); span->start -= len; span->length += len; pagemap_.set(span->start, span); Event(span, 'L', len); } - Span* next = GetDescriptor(p+n); - if (next != NULL && MayMergeSpans(span, next)) { + Span* next = CheckAndHandlePreMerge(span, GetDescriptor(p+n)); + if (next != NULL) { // Merge next span into this span ASSERT(next->start == p+n); const Length len = next->length; - if (aggressive_decommit_ && next->location == Span::ON_RETURNED_FREELIST) { - // See the comment below 'if (prev->location ...' for explanation. - temp_committed += next->length << kPageShift; - } - RemoveFromFreeList(next); DeleteSpan(next); span->length += len; pagemap_.set(span->start + span->length - 1, span); Event(span, 'R', len); } - if (aggressive_decommit_) { - if (DecommitSpan(span)) { - span->location = Span::ON_RETURNED_FREELIST; - stats_.committed_bytes += temp_committed; - } else { - ASSERT(temp_committed == 0); - } - } PrependToFreeList(span); } void PageHeap::PrependToFreeList(Span* span) { ASSERT(span->location != Span::IN_USE); - SpanList* list = (span->length < kMaxPages) ? &free_[span->length] : &large_; - if (span->location == Span::ON_NORMAL_FREELIST) { + if (span->location == Span::ON_NORMAL_FREELIST) stats_.free_bytes += (span->length << kPageShift); + else + stats_.unmapped_bytes += (span->length << kPageShift); + + if (span->length > kMaxPages) { + SpanSet *set = &large_normal_; + if (span->location == Span::ON_RETURNED_FREELIST) + set = &large_returned_; + std::pair p = + set->insert(SpanPtrWithLength(span)); + ASSERT(p.second); // We never have duplicates since span->start is unique. + span->SetSpanSetIterator(p.first); + return; + } + + SpanList* list = &free_[span->length - 1]; + if (span->location == Span::ON_NORMAL_FREELIST) { DLL_Prepend(&list->normal, span); } else { - stats_.unmapped_bytes += (span->length << kPageShift); DLL_Prepend(&list->returned, span); } } @@ -426,7 +429,17 @@ void PageHeap::RemoveFromFreeList(Span* span) { } else { stats_.unmapped_bytes -= (span->length << kPageShift); } - DLL_Remove(span); + if (span->length > kMaxPages) { + SpanSet *set = &large_normal_; + if (span->location == Span::ON_RETURNED_FREELIST) + set = &large_returned_; + SpanSet::iterator iter = span->ExtractSpanSetIterator(); + ASSERT(iter->span == span); + ASSERT(set->find(SpanPtrWithLength(span)) == iter); + set->erase(iter); + } else { + DLL_Remove(span); + } } void PageHeap::IncrementalScavenge(Length n) { @@ -441,6 +454,8 @@ void PageHeap::IncrementalScavenge(Length n) { return; } + ++stats_.scavenge_count; + Length released_pages = ReleaseAtLeastNPages(1); if (released_pages == 0) { @@ -460,8 +475,7 @@ void PageHeap::IncrementalScavenge(Length n) { } } -Length PageHeap::ReleaseLastNormalSpan(SpanList* slist) { - Span* s = slist->normal.prev; +Length PageHeap::ReleaseSpan(Span* s) { ASSERT(s->location == Span::ON_NORMAL_FREELIST); if (DecommitSpan(s)) { @@ -478,21 +492,35 @@ Length PageHeap::ReleaseLastNormalSpan(SpanList* slist) { Length PageHeap::ReleaseAtLeastNPages(Length num_pages) { Length released_pages = 0; - // Round robin through the lists of free spans, releasing the last - // span in each list. Stop after releasing at least num_pages + // Round robin through the lists of free spans, releasing a + // span from each list. Stop after releasing at least num_pages // or when there is nothing more to release. while (released_pages < num_pages && stats_.free_bytes > 0) { for (int i = 0; i < kMaxPages+1 && released_pages < num_pages; i++, release_index_++) { + Span *s; if (release_index_ > kMaxPages) release_index_ = 0; - SpanList* slist = (release_index_ == kMaxPages) ? - &large_ : &free_[release_index_]; - if (!DLL_IsEmpty(&slist->normal)) { - Length released_len = ReleaseLastNormalSpan(slist); - // Some systems do not support release - if (released_len == 0) return released_pages; - released_pages += released_len; + + if (release_index_ == kMaxPages) { + if (large_normal_.empty()) { + continue; + } + s = (large_normal_.begin())->span; + } else { + SpanList* slist = &free_[release_index_]; + if (DLL_IsEmpty(&slist->normal)) { + continue; + } + s = slist->normal.prev; } + // TODO(todd) if the remaining number of pages to release + // is significantly smaller than s->length, and s is on the + // large freelist, should we carve s instead of releasing? + // the whole thing? + Length released_len = ReleaseSpan(s); + // Some systems do not support release + if (released_len == 0) return released_pages; + released_pages += released_len; } } return released_pages; @@ -522,7 +550,7 @@ bool PageHeap::EnsureLimit(Length n, bool withRelease) return takenPages + n <= limit; } -void PageHeap::RegisterSizeClass(Span* span, size_t sc) { +void PageHeap::RegisterSizeClass(Span* span, uint32 sc) { // Associate span object with all interior pages as well ASSERT(span->location == Span::IN_USE); ASSERT(GetDescriptor(span->start) == span); @@ -535,9 +563,9 @@ void PageHeap::RegisterSizeClass(Span* span, size_t sc) { } void PageHeap::GetSmallSpanStats(SmallSpanStats* result) { - for (int s = 0; s < kMaxPages; s++) { - result->normal_length[s] = DLL_Length(&free_[s].normal); - result->returned_length[s] = DLL_Length(&free_[s].returned); + for (int i = 0; i < kMaxPages; i++) { + result->normal_length[i] = DLL_Length(&free_[i].normal); + result->returned_length[i] = DLL_Length(&free_[i].returned); } } @@ -545,12 +573,12 @@ void PageHeap::GetLargeSpanStats(LargeSpanStats* result) { result->spans = 0; result->normal_pages = 0; result->returned_pages = 0; - for (Span* s = large_.normal.next; s != &large_.normal; s = s->next) { - result->normal_pages += s->length;; + for (SpanSet::iterator it = large_normal_.begin(); it != large_normal_.end(); ++it) { + result->normal_pages += it->length; result->spans++; } - for (Span* s = large_.returned.next; s != &large_.returned; s = s->next) { - result->returned_pages += s->length; + for (SpanSet::iterator it = large_returned_.begin(); it != large_returned_.end(); ++it) { + result->returned_pages += it->length; result->spans++; } } @@ -595,9 +623,9 @@ static void RecordGrowth(size_t growth) { } bool PageHeap::GrowHeap(Length n) { - ASSERT(kMaxPages >= kMinSystemAlloc); + ASSERT(kMaxPages >= min_system_alloc_); if (n > kMaxValidPages) return false; - Length ask = (n>kMinSystemAlloc) ? n : static_cast(kMinSystemAlloc); + Length ask = (n>min_system_alloc_) ? n : static_cast(min_system_alloc_); size_t actual_size; void* ptr = NULL; if (EnsureLimit(ask)) { @@ -616,9 +644,16 @@ bool PageHeap::GrowHeap(Length n) { ask = actual_size >> kPageShift; RecordGrowth(ask << kPageShift); + ++stats_.reserve_count; + ++stats_.commit_count; + uint64_t old_system_bytes = stats_.system_bytes; stats_.system_bytes += (ask << kPageShift); stats_.committed_bytes += (ask << kPageShift); + + stats_.total_commit_bytes += (ask << kPageShift); + stats_.total_reserve_bytes += (ask << kPageShift); + const PageID p = reinterpret_cast(ptr) >> kPageShift; ASSERT(p > 0); @@ -651,18 +686,16 @@ bool PageHeap::GrowHeap(Length n) { } bool PageHeap::Check() { - ASSERT(free_[0].normal.next == &free_[0].normal); - ASSERT(free_[0].returned.next == &free_[0].returned); return true; } bool PageHeap::CheckExpensive() { bool result = Check(); - CheckList(&large_.normal, kMaxPages, 1000000000, Span::ON_NORMAL_FREELIST); - CheckList(&large_.returned, kMaxPages, 1000000000, Span::ON_RETURNED_FREELIST); - for (Length s = 1; s < kMaxPages; s++) { - CheckList(&free_[s].normal, s, s, Span::ON_NORMAL_FREELIST); - CheckList(&free_[s].returned, s, s, Span::ON_RETURNED_FREELIST); + CheckSet(&large_normal_, kMaxPages + 1, Span::ON_NORMAL_FREELIST); + CheckSet(&large_returned_, kMaxPages + 1, Span::ON_RETURNED_FREELIST); + for (int s = 1; s <= kMaxPages; s++) { + CheckList(&free_[s - 1].normal, s, s, Span::ON_NORMAL_FREELIST); + CheckList(&free_[s - 1].returned, s, s, Span::ON_RETURNED_FREELIST); } return result; } @@ -679,4 +712,16 @@ bool PageHeap::CheckList(Span* list, Length min_pages, Length max_pages, return true; } +bool PageHeap::CheckSet(SpanSet* spanset, Length min_pages,int freelist) { + for (SpanSet::iterator it = spanset->begin(); it != spanset->end(); ++it) { + Span* s = it->span; + CHECK_CONDITION(s->length == it->length); + CHECK_CONDITION(s->location == freelist); // NORMAL or RETURNED + CHECK_CONDITION(s->length >= min_pages); + CHECK_CONDITION(GetDescriptor(s->start) == s); + CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s); + } + return true; +} + } // namespace tcmalloc diff --git a/tpl/gperftools/src/page_heap.h b/tpl/gperftools/src/page_heap.h index 18abed1..27b515a 100644 --- a/tpl/gperftools/src/page_heap.h +++ b/tpl/gperftools/src/page_heap.h @@ -83,14 +83,24 @@ namespace tcmalloc { template class MapSelector { public: typedef TCMalloc_PageMap3 Type; - typedef PackedCache CacheType; }; +#ifndef TCMALLOC_SMALL_BUT_SLOW +// x86-64 and arm64 are using 48 bits of address space. So we can use +// just two level map, but since initial ram consumption of this mode +// is a bit on the higher side, we opt-out of it in +// TCMALLOC_SMALL_BUT_SLOW mode. +template <> class MapSelector<48> { + public: + typedef TCMalloc_PageMap2<48-kPageShift> Type; +}; + +#endif // TCMALLOC_SMALL_BUT_SLOW + // A two-level map for 32-bit machines template <> class MapSelector<32> { public: typedef TCMalloc_PageMap2<32-kPageShift> Type; - typedef PackedCache<32-kPageShift, uint16_t> CacheType; }; // ------------------------------------------------------------------------- @@ -119,7 +129,7 @@ class PERFTOOLS_DLL_DECL PageHeap { // specified size-class. // REQUIRES: span was returned by an earlier call to New() // and has not yet been deleted. - void RegisterSizeClass(Span* span, size_t sc); + void RegisterSizeClass(Span* span, uint32 sc); // Split an allocated span into two spans: one of length "n" pages // followed by another span of length "span->length - n" pages. @@ -133,7 +143,8 @@ class PERFTOOLS_DLL_DECL PageHeap { // Return the descriptor for the specified page. Returns NULL if // this PageID was not allocated previously. - inline Span* GetDescriptor(PageID p) const { + inline ATTRIBUTE_ALWAYS_INLINE + Span* GetDescriptor(PageID p) const { return reinterpret_cast(pagemap_.get(p)); } @@ -143,18 +154,32 @@ class PERFTOOLS_DLL_DECL PageHeap { // Page heap statistics struct Stats { - Stats() : system_bytes(0), free_bytes(0), unmapped_bytes(0), committed_bytes(0) {} + Stats() : system_bytes(0), free_bytes(0), unmapped_bytes(0), committed_bytes(0), + scavenge_count(0), commit_count(0), total_commit_bytes(0), + decommit_count(0), total_decommit_bytes(0), + reserve_count(0), total_reserve_bytes(0) {} uint64_t system_bytes; // Total bytes allocated from system uint64_t free_bytes; // Total bytes on normal freelists uint64_t unmapped_bytes; // Total bytes on returned freelists uint64_t committed_bytes; // Bytes committed, always <= system_bytes_. + uint64_t scavenge_count; // Number of times scavagened flush pages + + uint64_t commit_count; // Number of virtual memory commits + uint64_t total_commit_bytes; // Bytes committed in lifetime of process + uint64_t decommit_count; // Number of virtual memory decommits + uint64_t total_decommit_bytes; // Bytes decommitted in lifetime of process + + uint64_t reserve_count; // Number of virtual memory reserves + uint64_t total_reserve_bytes; // Bytes reserved in lifetime of process }; inline Stats stats() const { return stats_; } struct SmallSpanStats { // For each free list of small spans, the length (in spans) of the // normal and returned free lists for that size. + // + // NOTE: index 'i' accounts the number of spans of length 'i + 1'. int64 normal_length[kMaxPages]; int64 returned_length[kMaxPages]; }; @@ -173,6 +198,7 @@ class PERFTOOLS_DLL_DECL PageHeap { bool CheckExpensive(); bool CheckList(Span* list, Length min_pages, Length max_pages, int freelist); // ON_NORMAL_FREELIST or ON_RETURNED_FREELIST + bool CheckSet(SpanSet *s, Length min_pages, int freelist); // Try to release at least num_pages for reuse by the OS. Returns // the actual number of pages released, which may be less than @@ -182,21 +208,37 @@ class PERFTOOLS_DLL_DECL PageHeap { // smaller released and unreleased ranges. Length ReleaseAtLeastNPages(Length num_pages); - // Return 0 if we have no information, or else the correct sizeclass for p. // Reads and writes to pagemap_cache_ do not require locking. - // The entries are 64 bits on 64-bit hardware and 16 bits on - // 32-bit hardware, and we don't mind raciness as long as each read of - // an entry yields a valid entry, not a partially updated entry. - size_t GetSizeClassIfCached(PageID p) const { - return pagemap_cache_.GetOrDefault(p, 0); + bool TryGetSizeClass(PageID p, uint32* out) const { + return pagemap_cache_.TryGet(p, out); + } + void SetCachedSizeClass(PageID p, uint32 cl) { + ASSERT(cl != 0); + pagemap_cache_.Put(p, cl); + } + void InvalidateCachedSizeClass(PageID p) { pagemap_cache_.Invalidate(p); } + uint32 GetSizeClassOrZero(PageID p) const { + uint32 cached_value; + if (!TryGetSizeClass(p, &cached_value)) { + cached_value = 0; + } + return cached_value; } - void CacheSizeClass(PageID p, size_t cl) const { pagemap_cache_.Put(p, cl); } bool GetAggressiveDecommit(void) {return aggressive_decommit_;} void SetAggressiveDecommit(bool aggressive_decommit) { aggressive_decommit_ = aggressive_decommit; } + size_t GetMinSystemAlloc(void) {return kMinSystemAlloc;} + void SetMinSystemAlloc(size_t min_system_alloc) { + if( min_system_alloc > kMaxPages ) { + printf("ERROR: Minimum system allocation must be less than or equal to %d bytes\n", (kMaxPages << kPageShift)); + } else { + min_system_alloc_ = min_system_alloc; + } + } + private: // Allocates a big block of memory for the pagemap once we reach more than // 128MB @@ -222,9 +264,9 @@ class PERFTOOLS_DLL_DECL PageHeap { // Pick the appropriate map and cache types based on pointer size typedef MapSelector::Type PageMap; - typedef MapSelector::CacheType PageMapCache; - PageMap pagemap_; + typedef PackedCache PageMapCache; mutable PageMapCache pagemap_cache_; + PageMap pagemap_; // We segregate spans of a given size into two circular linked // lists: one for normal spans, and one for spans whose memory @@ -234,10 +276,16 @@ class PERFTOOLS_DLL_DECL PageHeap { Span returned; }; - // List of free spans of length >= kMaxPages - SpanList large_; + // Sets of spans with length > kMaxPages. + // + // Rather than using a linked list, we use sets here for efficient + // best-fit search. + SpanSet large_normal_; + SpanSet large_returned_; // Array mapping from span length to a doubly linked list of free spans + // + // NOTE: index 'i' stores spans of length 'i + 1'. SpanList free_[kMaxPages]; // Statistics on system, free, and unmapped bytes @@ -287,16 +335,19 @@ class PERFTOOLS_DLL_DECL PageHeap { // IncrementalScavenge(n) is called whenever n pages are freed. void IncrementalScavenge(Length n); - // Release the last span on the normal portion of this list. - // Return the length of that span or zero if release failed. - Length ReleaseLastNormalSpan(SpanList* slist); + // Attempts to decommit 's' and move it to the returned freelist. + // + // Returns the length of the Span or zero if release failed. + // + // REQUIRES: 's' must be on the NORMAL freelist. + Length ReleaseSpan(Span *s); // Checks if we are allowed to take more memory from the system. // If limit is reached and allowRelease is true, tries to release // some unused spans. bool EnsureLimit(Length n, bool allowRelease = true); - bool MayMergeSpans(Span *span, Span *other); + Span* CheckAndHandlePreMerge(Span *span, Span *other); // Number of pages to deallocate before doing more scavenging int64_t scavenge_counter_; @@ -305,6 +356,7 @@ class PERFTOOLS_DLL_DECL PageHeap { int release_index_; bool aggressive_decommit_; + int min_system_alloc_; }; } // namespace tcmalloc diff --git a/tpl/gperftools/src/page_heap_allocator.h b/tpl/gperftools/src/page_heap_allocator.h index 892d1c1..3fecabd 100644 --- a/tpl/gperftools/src/page_heap_allocator.h +++ b/tpl/gperftools/src/page_heap_allocator.h @@ -109,6 +109,71 @@ class PageHeapAllocator { int inuse_; }; +// STL-compatible allocator which forwards allocations to a PageHeapAllocator. +// +// Like PageHeapAllocator, this requires external synchronization. To avoid multiple +// separate STLPageHeapAllocator from sharing the same underlying PageHeapAllocator, +// the |LockingTag| template argument should be used. Template instantiations with +// different locking tags can safely be used concurrently. +template +class STLPageHeapAllocator { + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + + template struct rebind { + typedef STLPageHeapAllocator other; + }; + + STLPageHeapAllocator() { } + STLPageHeapAllocator(const STLPageHeapAllocator&) { } + template STLPageHeapAllocator(const STLPageHeapAllocator&) { } + ~STLPageHeapAllocator() { } + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + void construct(pointer p, const T& val) { ::new(p) T(val); } + void construct(pointer p) { ::new(p) T(); } + void destroy(pointer p) { p->~T(); } + + // There's no state, so these allocators are always equal + bool operator==(const STLPageHeapAllocator&) const { return true; } + bool operator!=(const STLPageHeapAllocator&) const { return false; } + + pointer allocate(size_type n, const void* = 0) { + if (!underlying_.initialized) { + underlying_.allocator.Init(); + underlying_.initialized = true; + } + + CHECK_CONDITION(n == 1); + return underlying_.allocator.New(); + } + void deallocate(pointer p, size_type n) { + CHECK_CONDITION(n == 1); + underlying_.allocator.Delete(p); + } + + private: + struct Storage { + explicit Storage(base::LinkerInitialized x) {} + PageHeapAllocator allocator; + bool initialized; + }; + static Storage underlying_; +}; + +template +typename STLPageHeapAllocator::Storage STLPageHeapAllocator::underlying_(base::LINKER_INITIALIZED); + } // namespace tcmalloc #endif // TCMALLOC_PAGE_HEAP_ALLOCATOR_H_ diff --git a/tpl/gperftools/src/pagemap.h b/tpl/gperftools/src/pagemap.h index dd94423..68b2d24 100644 --- a/tpl/gperftools/src/pagemap.h +++ b/tpl/gperftools/src/pagemap.h @@ -89,6 +89,7 @@ class TCMalloc_PageMap1 { // Return the current value for KEY. Returns NULL if not yet set, // or if k is out of range. + ATTRIBUTE_ALWAYS_INLINE void* get(Number k) const { if ((k >> BITS) > 0) { return NULL; @@ -119,19 +120,18 @@ class TCMalloc_PageMap1 { template class TCMalloc_PageMap2 { private: - // Put 32 entries in the root and (2^BITS)/32 entries in each leaf. - static const int ROOT_BITS = 5; - static const int ROOT_LENGTH = 1 << ROOT_BITS; - - static const int LEAF_BITS = BITS - ROOT_BITS; + static const int LEAF_BITS = (BITS + 1) / 2; static const int LEAF_LENGTH = 1 << LEAF_BITS; + static const int ROOT_BITS = BITS - LEAF_BITS; + static const int ROOT_LENGTH = 1 << ROOT_BITS; + // Leaf node struct Leaf { void* values[LEAF_LENGTH]; }; - Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes + Leaf* root_[ROOT_LENGTH]; // Pointers to child nodes void* (*allocator_)(size_t); // Memory allocator public: @@ -142,6 +142,7 @@ class TCMalloc_PageMap2 { memset(root_, 0, sizeof(root_)); } + ATTRIBUTE_ALWAYS_INLINE void* get(Number k) const { const Number i1 = k >> LEAF_BITS; const Number i2 = k & (LEAF_LENGTH-1); @@ -182,11 +183,13 @@ class TCMalloc_PageMap2 { void PreallocateMoreMemory() { // Allocate enough to keep track of all possible pages - Ensure(0, 1 << BITS); + if (BITS < 20) { + Ensure(0, Number(1) << BITS); + } } void* Next(Number k) const { - while (k < (1 << BITS)) { + while (k < (Number(1) << BITS)) { const Number i1 = k >> LEAF_BITS; Leaf* leaf = root_[i1]; if (leaf != NULL) { @@ -226,7 +229,7 @@ class TCMalloc_PageMap3 { void* values[LEAF_LENGTH]; }; - Node* root_; // Root of radix tree + Node root_; // Root of radix tree void* (*allocator_)(size_t); // Memory allocator Node* NewNode() { @@ -242,18 +245,19 @@ class TCMalloc_PageMap3 { explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) { allocator_ = allocator; - root_ = NewNode(); + memset(&root_, 0, sizeof(root_)); } + ATTRIBUTE_ALWAYS_INLINE void* get(Number k) const { const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); const Number i3 = k & (LEAF_LENGTH-1); if ((k >> BITS) > 0 || - root_->ptrs[i1] == NULL || root_->ptrs[i1]->ptrs[i2] == NULL) { + root_.ptrs[i1] == NULL || root_.ptrs[i1]->ptrs[i2] == NULL) { return NULL; } - return reinterpret_cast(root_->ptrs[i1]->ptrs[i2])->values[i3]; + return reinterpret_cast(root_.ptrs[i1]->ptrs[i2])->values[i3]; } void set(Number k, void* v) { @@ -261,7 +265,7 @@ class TCMalloc_PageMap3 { const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); const Number i3 = k & (LEAF_LENGTH-1); - reinterpret_cast(root_->ptrs[i1]->ptrs[i2])->values[i3] = v; + reinterpret_cast(root_.ptrs[i1]->ptrs[i2])->values[i3] = v; } bool Ensure(Number start, size_t n) { @@ -274,18 +278,18 @@ class TCMalloc_PageMap3 { return false; // Make 2nd level node if necessary - if (root_->ptrs[i1] == NULL) { + if (root_.ptrs[i1] == NULL) { Node* n = NewNode(); if (n == NULL) return false; - root_->ptrs[i1] = n; + root_.ptrs[i1] = n; } // Make leaf node if necessary - if (root_->ptrs[i1]->ptrs[i2] == NULL) { + if (root_.ptrs[i1]->ptrs[i2] == NULL) { Leaf* leaf = reinterpret_cast((*allocator_)(sizeof(Leaf))); if (leaf == NULL) return false; memset(leaf, 0, sizeof(*leaf)); - root_->ptrs[i1]->ptrs[i2] = reinterpret_cast(leaf); + root_.ptrs[i1]->ptrs[i2] = reinterpret_cast(leaf); } // Advance key past whatever is covered by this leaf node @@ -301,11 +305,11 @@ class TCMalloc_PageMap3 { while (k < (Number(1) << BITS)) { const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); - if (root_->ptrs[i1] == NULL) { + if (root_.ptrs[i1] == NULL) { // Advance to next top-level entry k = (i1 + 1) << (LEAF_BITS + INTERIOR_BITS); } else { - Leaf* leaf = reinterpret_cast(root_->ptrs[i1]->ptrs[i2]); + Leaf* leaf = reinterpret_cast(root_.ptrs[i1]->ptrs[i2]); if (leaf != NULL) { for (Number i3 = (k & (LEAF_LENGTH-1)); i3 < LEAF_LENGTH; i3++) { if (leaf->values[i3] != NULL) { diff --git a/tpl/gperftools/src/pprof b/tpl/gperftools/src/pprof index 6abf528..9e18fc6 100755 --- a/tpl/gperftools/src/pprof +++ b/tpl/gperftools/src/pprof @@ -148,13 +148,13 @@ my @stackTraces; sub usage_string { return < +$0 [options] is a space separated list of profile names. -pprof [options] +$0 [options] is a list of profile files where each file contains the necessary symbol mappings as well as profile data (likely generated with --raw). -pprof [options] +$0 [options] is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE Each name can be: @@ -165,9 +165,9 @@ pprof [options] $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall, $CENSUSPROFILE_PAGE, or /pprof/filteredprofile. For instance: - pprof http://myserver.com:80$HEAP_PAGE + $0 http://myserver.com:80$HEAP_PAGE If / is omitted, the service defaults to $PROFILE_PAGE (cpu profiling). -pprof --symbols +$0 --symbols Maps addresses to symbol names. In this mode, stdin should be a list of library mappings, in the same format as is found in the heap- and cpu-profile files (this loosely matches that of /proc/self/maps @@ -204,7 +204,7 @@ Output type: --disasm= Generate disassembly of matching routines --symbols Print demangled symbol names found at given addresses --dot Generate DOT file to stdout - --ps Generate Postcript to stdout + --ps Generate Postscript to stdout --pdf Generate PDF to stdout --svg Generate SVG to stdout --gif Generate GIF to stdout @@ -252,29 +252,29 @@ Environment Variables: Examples: -pprof /bin/ls ls.prof +$0 /bin/ls ls.prof Enters "interactive" mode -pprof --text /bin/ls ls.prof +$0 --text /bin/ls ls.prof Outputs one line per procedure -pprof --web /bin/ls ls.prof +$0 --web /bin/ls ls.prof Displays annotated call-graph in web browser -pprof --gv /bin/ls ls.prof +$0 --gv /bin/ls ls.prof Displays annotated call-graph via 'gv' -pprof --gv --focus=Mutex /bin/ls ls.prof +$0 --gv --focus=Mutex /bin/ls ls.prof Restricts to code paths including a .*Mutex.* entry -pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof +$0 --gv --focus=Mutex --ignore=string /bin/ls ls.prof Code paths including Mutex but not string -pprof --list=getdir /bin/ls ls.prof +$0 --list=getdir /bin/ls ls.prof (Per-line) annotated source listing for getdir() -pprof --disasm=getdir /bin/ls ls.prof +$0 --disasm=getdir /bin/ls ls.prof (Per-PC) annotated disassembly for getdir() -pprof http://localhost:1234/ +$0 http://localhost:1234/ Enters "interactive" mode -pprof --text localhost:1234 +$0 --text localhost:1234 Outputs one line per procedure for localhost:1234 -pprof --raw localhost:1234 > ./local.raw -pprof --text ./local.raw +$0 --raw localhost:1234 > ./local.raw +$0 --text ./local.raw Fetches a remote profile for later analysis and then analyzes it in text mode. EOF @@ -1302,7 +1302,7 @@ sub PrintCallgrind { $filename = "&STDOUT"; } open(CG, ">$filename"); - printf CG ("events: Hits\n\n"); + print CG ("events: Hits\n\n"); foreach my $call ( map { $_->[0] } sort { $a->[1] cmp $b ->[1] || $a->[2] <=> $b->[2] } @@ -1318,14 +1318,14 @@ sub PrintCallgrind { # TODO(csilvers): for better compression, collect all the # caller/callee_files and functions first, before printing # anything, and only compress those referenced more than once. - printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map); - printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map); + print CG CompressedCGName("fl", $caller_file, \%filename_to_index_map); + print CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map); if (defined $6) { - printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map); - printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map); - printf CG ("calls=$count $callee_line\n"); + print CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map); + print CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map); + print CG ("calls=$count $callee_line\n"); } - printf CG ("$caller_line $count\n\n"); + print CG ("$caller_line $count\n\n"); } } @@ -4511,7 +4511,6 @@ sub ParseLibraries { my $zero_offset = HexExtend("0"); my $buildvar = ""; - my $priorlib = ""; foreach my $l (split("\n", $map)) { if ($l =~ m/^\s*build=(.*)$/) { $buildvar = $1; @@ -4521,7 +4520,7 @@ sub ParseLibraries { my $finish; my $offset; my $lib; - if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(.+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { + if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(.+\.(so|dll|dylib|bundle|node)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { # Full line from /proc/self/maps. Example: # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so $start = HexExtend($1); @@ -4568,16 +4567,7 @@ sub ParseLibraries { } } - # If we find multiple executable segments for a single library, merge them - # into a single entry that spans the complete address range. - if ($lib eq $priorlib) { - my $prior = pop(@{$result}); - $start = @$prior[1]; - # TODO $offset may be wrong if .text is not in the final segment. - } - push(@{$result}, [$lib, $start, $finish, $offset]); - $priorlib = $lib; } # Append special entry for additional library (not relocated) @@ -5161,7 +5151,7 @@ sub cleanup { } print STDERR "If you want to investigate this profile further, you can do:\n"; print STDERR "\n"; - print STDERR " pprof \\\n"; + print STDERR " $0 \\\n"; print STDERR " $main::prog \\\n"; print STDERR " $main::collected_profile\n"; print STDERR "\n"; diff --git a/tpl/gperftools/src/profile-handler.cc b/tpl/gperftools/src/profile-handler.cc index 66c9d74..7fdcb69 100644 --- a/tpl/gperftools/src/profile-handler.cc +++ b/tpl/gperftools/src/profile-handler.cc @@ -79,39 +79,44 @@ struct ProfileHandlerToken { void* callback_arg; }; +// Blocks a signal from being delivered to the current thread while the object +// is alive. Unblocks it upon destruction. +class ScopedSignalBlocker { + public: + ScopedSignalBlocker(int signo) { + sigemptyset(&sig_set_); + sigaddset(&sig_set_, signo); + RAW_CHECK(sigprocmask(SIG_BLOCK, &sig_set_, NULL) == 0, + "sigprocmask (block)"); + } + ~ScopedSignalBlocker() { + RAW_CHECK(sigprocmask(SIG_UNBLOCK, &sig_set_, NULL) == 0, + "sigprocmask (unblock)"); + } + + private: + sigset_t sig_set_; +}; + // This class manages profile timers and associated signal handler. This is a // a singleton. class ProfileHandler { public: - // Registers the current thread with the profile handler. On systems which - // have a separate interval timer for each thread, this function starts the - // timer for the current thread. - // - // The function also attempts to determine whether or not timers are shared by - // all threads in the process. (With LinuxThreads, and with NPTL on some - // Linux kernel versions, each thread has separate timers.) - // - // Prior to determining whether timers are shared, this function will - // unconditionally start the timer. However, if this function determines - // that timers are shared, then it will stop the timer if no callbacks are - // currently registered. + // Registers the current thread with the profile handler. void RegisterThread(); // Registers a callback routine to receive profile timer ticks. The returned // token is to be used when unregistering this callback and must not be - // deleted by the caller. Registration of the first callback enables the - // SIGPROF handler (or SIGALRM if using ITIMER_REAL). + // deleted by the caller. ProfileHandlerToken* RegisterCallback(ProfileHandlerCallback callback, void* callback_arg); // Unregisters a previously registered callback. Expects the token returned - // by the corresponding RegisterCallback routine. Unregistering the last - // callback disables the SIGPROF handler (or SIGALRM if using ITIMER_REAL). + // by the corresponding RegisterCallback routine. void UnregisterCallback(ProfileHandlerToken* token) NO_THREAD_SAFETY_ANALYSIS; - // Unregisters all the callbacks, stops the timer if shared, disables the - // SIGPROF (or SIGALRM) handler and clears the timer_sharing_ state. + // Unregisters all the callbacks and stops the timer(s). void Reset(); // Gets the current state of profile handler. @@ -138,13 +143,18 @@ class ProfileHandler { // Initializes the ProfileHandler singleton via GoogleOnceInit. static void Init(); - // The number of SIGPROF (or SIGALRM for ITIMER_REAL) interrupts received. + // Timer state as configured previously. + bool timer_running_; + + // The number of profiling signal interrupts received. int64 interrupts_ GUARDED_BY(signal_lock_); - // SIGPROF/SIGALRM interrupt frequency, read-only after construction. + // Profiling signal interrupt frequency, read-only after construction. int32 frequency_; - // ITIMER_PROF (which uses SIGPROF), or ITIMER_REAL (which uses SIGALRM) + // ITIMER_PROF (which uses SIGPROF), or ITIMER_REAL (which uses SIGALRM). + // Translated into an equivalent choice of clock if per_thread_timer_enabled_ + // is true. int timer_type_; // Signal number for timer signal. @@ -156,6 +166,7 @@ class ProfileHandler { // Is profiling allowed at all? bool allowed_; + // Must be false if HAVE_LINUX_SIGEV_THREAD_ID is not defined. bool per_thread_timer_enabled_; #ifdef HAVE_LINUX_SIGEV_THREAD_ID @@ -164,19 +175,6 @@ class ProfileHandler { pthread_key_t thread_timer_key; #endif - // Whether or not the threading system provides interval timers that are - // shared by all threads in a process. - enum { - // No timer initialization attempted yet. - TIMERS_UNTOUCHED, - // First thread has registered and set timer. - TIMERS_ONE_SET, - // Timers are shared by all threads. - TIMERS_SHARED, - // Timers are separate in each thread. - TIMERS_SEPARATE - } timer_sharing_ GUARDED_BY(control_lock_); - // This lock serializes the registration of threads and protects the // callbacks_ list below. // Locking order: @@ -203,32 +201,16 @@ class ProfileHandler { typedef CallbackList::iterator CallbackIterator; CallbackList callbacks_ GUARDED_BY(signal_lock_); - // Starts the interval timer. If the thread library shares timers between - // threads, this function starts the shared timer. Otherwise, this will start - // the timer in the current thread. - void StartTimer() EXCLUSIVE_LOCKS_REQUIRED(control_lock_); - - // Stops the interval timer. If the thread library shares timers between - // threads, this fucntion stops the shared timer. Otherwise, this will stop - // the timer in the current thread. - void StopTimer() EXCLUSIVE_LOCKS_REQUIRED(control_lock_); - - // Returns true if the profile interval timer is enabled in the current - // thread. This actually checks the kernel's interval timer setting. (It is - // used to detect whether timers are shared or separate.) - bool IsTimerRunning() EXCLUSIVE_LOCKS_REQUIRED(control_lock_); - - // Sets the timer interrupt signal handler. - void EnableHandler() EXCLUSIVE_LOCKS_REQUIRED(control_lock_); - - // Disables (ignores) the timer interrupt signal. - void DisableHandler() EXCLUSIVE_LOCKS_REQUIRED(control_lock_); + // Starts or stops the interval timer. + // Will ignore any requests to enable or disable when + // per_thread_timer_enabled_ is true. + void UpdateTimer(bool enable) EXCLUSIVE_LOCKS_REQUIRED(signal_lock_); // Returns true if the handler is not being used by something else. // This checks the kernel's signal handler table. bool IsSignalHandlerAvailable(); - // SIGPROF/SIGALRM handler. Iterate over and call all the registered callbacks. + // Signal handler. Iterates over and calls all the registered callbacks. static void SignalHandler(int sig, siginfo_t* sinfo, void* ucontext); DISALLOW_COPY_AND_ASSIGN(ProfileHandler); @@ -240,32 +222,25 @@ pthread_once_t ProfileHandler::once_ = PTHREAD_ONCE_INIT; const int32 ProfileHandler::kMaxFrequency; const int32 ProfileHandler::kDefaultFrequency; -// If we are LD_PRELOAD-ed against a non-pthreads app, then -// pthread_once won't be defined. We declare it here, for that -// case (with weak linkage) which will cause the non-definition to -// resolve to NULL. We can then check for NULL or not in Instance. -extern "C" int pthread_once(pthread_once_t *, void (*)(void)) - ATTRIBUTE_WEAK; +// If we are LD_PRELOAD-ed against a non-pthreads app, then these functions +// won't be defined. We declare them here, for that case (with weak linkage) +// which will cause the non-definition to resolve to NULL. We can then check +// for NULL or not in Instance. +extern "C" { +int pthread_once(pthread_once_t *, void (*)(void)) ATTRIBUTE_WEAK; +int pthread_kill(pthread_t thread_id, int signo) ATTRIBUTE_WEAK; #if HAVE_LINUX_SIGEV_THREAD_ID - -// We use weak alias to timer_create to avoid runtime dependency on -// -lrt and in turn -lpthread. -// -// At runtime we detect if timer_create is available and if so we -// can enable linux-sigev-thread mode of profiling -extern "C" { - int timer_create(clockid_t clockid, struct sigevent *evp, - timer_t *timerid) - ATTRIBUTE_WEAK; - int timer_delete(timer_t timerid) - ATTRIBUTE_WEAK; - int timer_settime(timer_t timerid, int flags, - const struct itimerspec *value, - struct itimerspec *ovalue) - ATTRIBUTE_WEAK; +int timer_create(clockid_t clockid, struct sigevent* evp, + timer_t* timerid) ATTRIBUTE_WEAK; +int timer_delete(timer_t timerid) ATTRIBUTE_WEAK; +int timer_settime(timer_t timerid, int flags, const struct itimerspec* value, + struct itimerspec* ovalue) ATTRIBUTE_WEAK; +#endif } +#if HAVE_LINUX_SIGEV_THREAD_ID + struct timer_id_holder { timer_t timerid; timer_id_holder(timer_t _timerid) : timerid(_timerid) {} @@ -343,11 +318,11 @@ ProfileHandler* ProfileHandler::Instance() { } ProfileHandler::ProfileHandler() - : interrupts_(0), + : timer_running_(false), + interrupts_(0), callback_count_(0), allowed_(true), - per_thread_timer_enabled_(false), - timer_sharing_(TIMERS_UNTOUCHED) { + per_thread_timer_enabled_(false) { SpinLockHolder cl(&control_lock_); timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF); @@ -376,7 +351,6 @@ ProfileHandler::ProfileHandler() if (per_thread || signal_number) { if (timer_create && pthread_once) { - timer_sharing_ = TIMERS_SEPARATE; CreateThreadTimerKey(&thread_timer_key); per_thread_timer_enabled_ = true; // Override signal number if requested. @@ -401,10 +375,12 @@ ProfileHandler::ProfileHandler() return; } - // Ignore signals until we decide to turn profiling on. (Paranoia; - // should already be ignored.) - DisableHandler(); - + // Install the signal handler. + struct sigaction sa; + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + RAW_CHECK(sigaction(signal_number_, &sa, NULL) == 0, "sigprof (enable)"); } ProfileHandler::~ProfileHandler() { @@ -423,47 +399,17 @@ void ProfileHandler::RegisterThread() { return; } - // We try to detect whether timers are being shared by setting a - // timer in the first call to this function, then checking whether - // it's set in the second call. - // - // Note that this detection method requires that the first two calls - // to RegisterThread must be made from different threads. (Subsequent - // calls will see timer_sharing_ set to either TIMERS_SEPARATE or - // TIMERS_SHARED, and won't try to detect the timer sharing type.) - // - // Also note that if timer settings were inherited across new thread - // creation but *not* shared, this approach wouldn't work. That's - // not an issue for any Linux threading implementation, and should - // not be a problem for a POSIX-compliant threads implementation. - switch (timer_sharing_) { - case TIMERS_UNTOUCHED: - StartTimer(); - timer_sharing_ = TIMERS_ONE_SET; - break; - case TIMERS_ONE_SET: - // If the timer is running, that means that the main thread's - // timer setup is seen in this (second) thread -- and therefore - // that timers are shared. - if (IsTimerRunning()) { - timer_sharing_ = TIMERS_SHARED; - // If callback is already registered, we have to keep the timer - // running. If not, we disable the timer here. - if (callback_count_ == 0) { - StopTimer(); - } - } else { - timer_sharing_ = TIMERS_SEPARATE; - StartTimer(); - } - break; - case TIMERS_SHARED: - // Nothing needed. - break; - case TIMERS_SEPARATE: - StartTimer(); - break; + // Record the thread identifier and start the timer if profiling is on. + ScopedSignalBlocker block(signal_number_); + SpinLockHolder sl(&signal_lock_); +#if HAVE_LINUX_SIGEV_THREAD_ID + if (per_thread_timer_enabled_) { + StartLinuxThreadTimer(timer_type_, signal_number_, frequency_, + thread_timer_key); + return; } +#endif + UpdateTimer(callback_count_ > 0); } ProfileHandlerToken* ProfileHandler::RegisterCallback( @@ -472,17 +418,13 @@ ProfileHandlerToken* ProfileHandler::RegisterCallback( ProfileHandlerToken* token = new ProfileHandlerToken(callback, callback_arg); SpinLockHolder cl(&control_lock_); - DisableHandler(); { + ScopedSignalBlocker block(signal_number_); SpinLockHolder sl(&signal_lock_); callbacks_.push_back(token); + ++callback_count_; + UpdateTimer(true); } - // Start the timer if timer is shared and this is a first callback. - if ((callback_count_ == 0) && (timer_sharing_ == TIMERS_SHARED)) { - StartTimer(); - } - ++callback_count_; - EnableHandler(); return token; } @@ -492,17 +434,14 @@ void ProfileHandler::UnregisterCallback(ProfileHandlerToken* token) { ++it) { if ((*it) == token) { RAW_CHECK(callback_count_ > 0, "Invalid callback count"); - DisableHandler(); { + ScopedSignalBlocker block(signal_number_); SpinLockHolder sl(&signal_lock_); delete *it; callbacks_.erase(it); - } - --callback_count_; - if (callback_count_ > 0) { - EnableHandler(); - } else if (timer_sharing_ == TIMERS_SHARED) { - StopTimer(); + --callback_count_; + if (callback_count_ == 0) + UpdateTimer(false); } return; } @@ -513,8 +452,8 @@ void ProfileHandler::UnregisterCallback(ProfileHandlerToken* token) { void ProfileHandler::Reset() { SpinLockHolder cl(&control_lock_); - DisableHandler(); { + ScopedSignalBlocker block(signal_number_); SpinLockHolder sl(&signal_lock_); CallbackIterator it = callbacks_.begin(); while (it != callbacks_.end()) { @@ -523,96 +462,44 @@ void ProfileHandler::Reset() { delete *tmp; callbacks_.erase(tmp); } + callback_count_ = 0; + UpdateTimer(false); } - callback_count_ = 0; - if (timer_sharing_ == TIMERS_SHARED) { - StopTimer(); - } - timer_sharing_ = TIMERS_UNTOUCHED; } void ProfileHandler::GetState(ProfileHandlerState* state) { SpinLockHolder cl(&control_lock_); - DisableHandler(); { + ScopedSignalBlocker block(signal_number_); SpinLockHolder sl(&signal_lock_); // Protects interrupts_. state->interrupts = interrupts_; } - if (callback_count_ > 0) { - EnableHandler(); - } state->frequency = frequency_; state->callback_count = callback_count_; state->allowed = allowed_; } -void ProfileHandler::StartTimer() { - if (!allowed_) { - return; - } - -#if HAVE_LINUX_SIGEV_THREAD_ID +void ProfileHandler::UpdateTimer(bool enable) { if (per_thread_timer_enabled_) { - StartLinuxThreadTimer(timer_type_, signal_number_, frequency_, thread_timer_key); + // Ignore any attempts to disable it because that's not supported, and it's + // always enabled so enabling is always a NOP. return; } -#endif - - struct itimerval timer; - timer.it_interval.tv_sec = 0; - timer.it_interval.tv_usec = 1000000 / frequency_; - timer.it_value = timer.it_interval; - setitimer(timer_type_, &timer, 0); -} -void ProfileHandler::StopTimer() { - if (!allowed_) { + if (enable == timer_running_) { return; } - if (per_thread_timer_enabled_) { - RAW_LOG(FATAL, "StopTimer cannot be called in linux-per-thread-timers mode"); - } + timer_running_ = enable; struct itimerval timer; - memset(&timer, 0, sizeof timer); + static const int kMillion = 1000000; + int interval_usec = enable ? kMillion / frequency_ : 0; + timer.it_interval.tv_sec = interval_usec / kMillion; + timer.it_interval.tv_usec = interval_usec % kMillion; + timer.it_value = timer.it_interval; setitimer(timer_type_, &timer, 0); } -bool ProfileHandler::IsTimerRunning() { - if (!allowed_) { - return false; - } - if (per_thread_timer_enabled_) { - return false; - } - struct itimerval current_timer; - RAW_CHECK(0 == getitimer(timer_type_, ¤t_timer), "getitimer"); - return (current_timer.it_value.tv_sec != 0 || - current_timer.it_value.tv_usec != 0); -} - -void ProfileHandler::EnableHandler() { - if (!allowed_) { - return; - } - struct sigaction sa; - sa.sa_sigaction = SignalHandler; - sa.sa_flags = SA_RESTART | SA_SIGINFO; - sigemptyset(&sa.sa_mask); - RAW_CHECK(sigaction(signal_number_, &sa, NULL) == 0, "sigprof (enable)"); -} - -void ProfileHandler::DisableHandler() { - if (!allowed_) { - return; - } - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_RESTART; - sigemptyset(&sa.sa_mask); - RAW_CHECK(sigaction(signal_number_, &sa, NULL) == 0, "sigprof (disable)"); -} - bool ProfileHandler::IsSignalHandlerAvailable() { struct sigaction sa; RAW_CHECK(sigaction(signal_number_, NULL, &sa) == 0, "is-signal-handler avail"); @@ -650,24 +537,24 @@ void ProfileHandler::SignalHandler(int sig, siginfo_t* sinfo, void* ucontext) { // executed in the context of the main thread. REGISTER_MODULE_INITIALIZER(profile_main, ProfileHandlerRegisterThread()); -extern "C" void ProfileHandlerRegisterThread() { +void ProfileHandlerRegisterThread() { ProfileHandler::Instance()->RegisterThread(); } -extern "C" ProfileHandlerToken* ProfileHandlerRegisterCallback( +ProfileHandlerToken* ProfileHandlerRegisterCallback( ProfileHandlerCallback callback, void* callback_arg) { return ProfileHandler::Instance()->RegisterCallback(callback, callback_arg); } -extern "C" void ProfileHandlerUnregisterCallback(ProfileHandlerToken* token) { +void ProfileHandlerUnregisterCallback(ProfileHandlerToken* token) { ProfileHandler::Instance()->UnregisterCallback(token); } -extern "C" void ProfileHandlerReset() { +void ProfileHandlerReset() { return ProfileHandler::Instance()->Reset(); } -extern "C" void ProfileHandlerGetState(ProfileHandlerState* state) { +void ProfileHandlerGetState(ProfileHandlerState* state) { ProfileHandler::Instance()->GetState(state); } @@ -677,21 +564,21 @@ extern "C" void ProfileHandlerGetState(ProfileHandlerState* state) { // work as well for profiling, and also interferes with alarm(). Because of // these issues, unless a specific need is identified, profiler support is // disabled under Cygwin. -extern "C" void ProfileHandlerRegisterThread() { +void ProfileHandlerRegisterThread() { } -extern "C" ProfileHandlerToken* ProfileHandlerRegisterCallback( +ProfileHandlerToken* ProfileHandlerRegisterCallback( ProfileHandlerCallback callback, void* callback_arg) { return NULL; } -extern "C" void ProfileHandlerUnregisterCallback(ProfileHandlerToken* token) { +void ProfileHandlerUnregisterCallback(ProfileHandlerToken* token) { } -extern "C" void ProfileHandlerReset() { +void ProfileHandlerReset() { } -extern "C" void ProfileHandlerGetState(ProfileHandlerState* state) { +void ProfileHandlerGetState(ProfileHandlerState* state) { } #endif // OS_CYGWIN diff --git a/tpl/gperftools/src/profile-handler.h b/tpl/gperftools/src/profile-handler.h index 4f96a18..3eae169 100644 --- a/tpl/gperftools/src/profile-handler.h +++ b/tpl/gperftools/src/profile-handler.h @@ -32,15 +32,17 @@ * Author: Nabeel Mian * * This module manages the cpu profile timers and the associated interrupt - * handler. When enabled, all registered threads in the program are profiled. - * (Note: if using linux 2.4 or earlier, you must use the Thread class, in - * google3/thread, to ensure all threads are profiled.) + * handler. When enabled, all threads in the program are profiled. * * Any component interested in receiving a profile timer interrupt can do so by * registering a callback. All registered callbacks must be async-signal-safe. * - * Note: This module requires the sole ownership of ITIMER_PROF timer and the - * SIGPROF signal. + * Note: This module requires the sole ownership of the configured timer and + * signal. The timer defaults to ITIMER_PROF, can be changed to ITIMER_REAL by + * the environment variable CPUPROFILE_REALTIME, or is changed to a POSIX timer + * with CPUPROFILE_PER_THREAD_TIMERS. The signal defaults to SIGPROF/SIGALRM to + * match the choice of timer and can be set to an arbitrary value using + * CPUPROFILE_TIMER_SIGNAL with CPUPROFILE_PER_THREAD_TIMERS. */ #ifndef BASE_PROFILE_HANDLER_H_ @@ -53,11 +55,6 @@ #endif #include "base/basictypes.h" -/* All this code should be usable from within C apps. */ -#ifdef __cplusplus -extern "C" { -#endif - /* Forward declaration. */ struct ProfileHandlerToken; @@ -142,8 +139,4 @@ struct ProfileHandlerState { }; void ProfileHandlerGetState(struct ProfileHandlerState* state); -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif /* BASE_PROFILE_HANDLER_H_ */ diff --git a/tpl/gperftools/src/profiledata.h b/tpl/gperftools/src/profiledata.h index 44033f0..b94b28c 100644 --- a/tpl/gperftools/src/profiledata.h +++ b/tpl/gperftools/src/profiledata.h @@ -35,7 +35,7 @@ // Collect profiling data. // // The profile data file format is documented in -// doc/cpuprofile-fileformat.html +// docs/cpuprofile-fileformat.html #ifndef BASE_PROFILEDATA_H_ diff --git a/tpl/gperftools/src/profiler.cc b/tpl/gperftools/src/profiler.cc index f4f5990..3bd0ed9 100644 --- a/tpl/gperftools/src/profiler.cc +++ b/tpl/gperftools/src/profiler.cc @@ -144,34 +144,31 @@ class CpuProfiler { // number is defined in the environment variable CPUPROFILESIGNAL. static void CpuProfilerSwitch(int signal_number) { - bool static started = false; - static unsigned profile_count = 0; - static char base_profile_name[1024] = "\0"; - - if (base_profile_name[0] == '\0') { - if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) { - RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined"); - return; - } - } - if (!started) - { - char full_profile_name[1024]; - - snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u", - base_profile_name, profile_count++); - - if(!ProfilerStart(full_profile_name)) - { - RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n", - full_profile_name, strerror(errno)); - } + static unsigned profile_count; + static char base_profile_name[PATH_MAX]; + static bool started = false; + + if (base_profile_name[0] == '\0') { + if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) { + RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined"); + return; } - else - { - ProfilerStop(); + } + + if (!started) { + char full_profile_name[PATH_MAX + 16]; + + snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u", + base_profile_name, profile_count++); + + if(!ProfilerStart(full_profile_name)) { + RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n", + full_profile_name, strerror(errno)); } - started = !started; + } else { + ProfilerStop(); + } + started = !started; } // Profile data structure singleton: Constructor will check to see if diff --git a/tpl/gperftools/src/sampler.cc b/tpl/gperftools/src/sampler.cc old mode 100755 new mode 100644 index cc71112..e63d4cb --- a/tpl/gperftools/src/sampler.cc +++ b/tpl/gperftools/src/sampler.cc @@ -57,34 +57,16 @@ DEFINE_int64(tcmalloc_sample_parameter, namespace tcmalloc { -// Statics for Sampler -double Sampler::log_table_[1<(i+0.5)/(1<(reinterpret_cast(this)); - if (rnd_ == 0) { - rnd_ = 1; - } - } + rnd_ = seed; // Step it forward 20 times for good measure for (int i = 0; i < 20; i++) { rnd_ = NextRandom(rnd_); @@ -93,10 +75,7 @@ void Sampler::Init(uint32_t seed) { bytes_until_sample_ = PickNextSamplingPoint(); } -// Initialize the Statics for the Sampler class -void Sampler::InitStatics() { - PopulateFastLog2Table(); -} +#define MAX_SSIZE (static_cast(static_cast(static_cast(-1)) >> 1)) // Generates a geometric variable with the specified mean (512K by default). // This is done by generating a random number between 0 and 1 and applying @@ -109,7 +88,17 @@ void Sampler::InitStatics() { // -log_e(q)/m = x // log_2(q) * (-log_e(2) * 1/m) = x // In the code, q is actually in the range 1 to 2**26, hence the -26 below -size_t Sampler::PickNextSamplingPoint() { +ssize_t Sampler::PickNextSamplingPoint() { + if (FLAGS_tcmalloc_sample_parameter <= 0) { + // In this case, we don't want to sample ever, and the larger a + // value we put here, the longer until we hit the slow path + // again. However, we have to support the flag changing at + // runtime, so pick something reasonably large (to keep overhead + // low) but small enough that we'll eventually start to sample + // again. + return 16 << 20; + } + rnd_ = NextRandom(rnd_); // Take the top 26 bits as the random number // (This plus the 1<<58 sampling bound give a max possible step of @@ -119,13 +108,26 @@ size_t Sampler::PickNextSamplingPoint() { // under piii debug for some binaries. double q = static_cast(rnd_ >> (prng_mod_power - 26)) + 1.0; // Put the computed p-value through the CDF of a geometric. - // For faster performance (save ~1/20th exec time), replace - // min(0.0, FastLog2(q) - 26) by (Fastlog2(q) - 26.000705) - // The value 26.000705 is used rather than 26 to compensate - // for inaccuracies in FastLog2 which otherwise result in a - // negative answer. - return static_cast(min(0.0, (FastLog2(q) - 26)) * (-log(2.0) - * FLAGS_tcmalloc_sample_parameter) + 1); + double interval = + (log2(q) - 26) * (-log(2.0) * FLAGS_tcmalloc_sample_parameter); + + // Very large values of interval overflow ssize_t. If we happen to + // hit such improbable condition, we simply cheat and clamp interval + // to largest supported value. + return static_cast(std::min(interval, MAX_SSIZE)); +} + +bool Sampler::RecordAllocationSlow(size_t k) { + if (!initialized_) { + initialized_ = true; + Init(reinterpret_cast(this)); + if (static_cast(bytes_until_sample_) >= k) { + bytes_until_sample_ -= k; + return true; + } + } + bytes_until_sample_ = PickNextSamplingPoint(); + return FLAGS_tcmalloc_sample_parameter <= 0; } } // namespace tcmalloc diff --git a/tpl/gperftools/src/sampler.h b/tpl/gperftools/src/sampler.h old mode 100755 new mode 100644 index eb316d7..2d6c504 --- a/tpl/gperftools/src/sampler.h +++ b/tpl/gperftools/src/sampler.h @@ -44,6 +44,7 @@ #include // for memcpy #include "base/basictypes.h" // for ASSERT #include "internal_logging.h" // for ASSERT +#include "static_vars.h" namespace tcmalloc { @@ -80,7 +81,7 @@ namespace tcmalloc { // a geometric with mean tcmalloc_sample_parameter. (ie. the // probability that at least one byte in the range is marked). This // is accurately given by the CDF of the corresponding exponential -// distribution : 1 - e^(X/tcmalloc_sample_parameter_) +// distribution : 1 - e^(-X/tcmalloc_sample_parameter_) // Independence of the byte marking ensures independence of // the sampling of each allocation. // @@ -100,52 +101,116 @@ namespace tcmalloc { // only result in a single call to PickNextSamplingPoint. //------------------------------------------------------------------- +class SamplerTest; + class PERFTOOLS_DLL_DECL Sampler { public: - // Initialize this sampler. - // Passing a seed of 0 gives a non-deterministic - // seed value given by casting the object ("this") - void Init(uint32_t seed); - void Cleanup(); + constexpr Sampler() {} - // Record allocation of "k" bytes. Return true iff allocation - // should be sampled - bool SampleAllocation(size_t k); + // Initialize this sampler. + void Init(uint64_t seed); + + // Record allocation of "k" bytes. Return true if no further work + // is need, and false if allocation needed to be sampled. + bool RecordAllocation(size_t k); + + // Same as above (but faster), except: + // a) REQUIRES(k < std::numeric_limits::max()) + // b) if this returns false, you must call RecordAllocation + // to confirm if sampling truly needed. + // + // The point of this function is to only deal with common case of no + // sampling and let caller (which is in malloc fast-path) to + // "escalate" to fuller and slower logic only if necessary. + bool TryRecordAllocationFast(size_t k); // Generate a geometric with mean 512K (or FLAG_tcmalloc_sample_parameter) - size_t PickNextSamplingPoint(); - - // Initialize the statics for the Sampler class - static void InitStatics(); + ssize_t PickNextSamplingPoint(); // Returns the current sample period - int GetSamplePeriod(); + static int GetSamplePeriod(); // The following are public for the purposes of testing static uint64_t NextRandom(uint64_t rnd_); // Returns the next prng value - static double FastLog2(const double & d); // Computes Log2(x) quickly - static void PopulateFastLog2Table(); // Populate the lookup table + // C++03 requires that types stored in TLS be POD. As a result, you must + // initialize these members to {0, 0, false} before using this class! + // + // TODO(ahh): C++11 support will let us make these private. + + // Bytes until we sample next. + // + // More specifically when bytes_until_sample_ is X, we can allocate + // X bytes without triggering sampling; on the (X+1)th allocated + // byte, the containing allocation will be sampled. + // + // Always non-negative with only very brief exceptions (see + // DecrementFast{,Finish}, so casting to size_t is ok. private: - size_t bytes_until_sample_; // Bytes until we sample next - uint64_t rnd_; // Cheap random number generator - - // Statics for the fast log - // Note that this code may not depend on anything in //util - // hence the duplication of functionality here - static const int kFastlogNumBits = 10; - static const int kFastlogMask = (1 << kFastlogNumBits) - 1; - static double log_table_[1<(bytes_until_sample_) < k) { + bool result = RecordAllocationSlow(k); + ASSERT(Static::IsInited()); + return result; } else { bytes_until_sample_ -= k; + ASSERT(Static::IsInited()); + return true; + } +} + +inline bool Sampler::TryRecordAllocationFast(size_t k) { + // For efficiency reason, we're testing bytes_until_sample_ after + // decrementing it by k. This allows compiler to do sub , + // followed by conditional jump on sign. But it is correct only if k + // is actually smaller than largest ssize_t value. Otherwise + // converting k to signed value overflows. + // + // It would be great for generated code to be sub , + // followed by conditional jump on 'carry', which would work for + // arbitrary values of k, but there seem to be no way to express + // that in C++. + // + // Our API contract explicitly states that only small values of k + // are permitted. And thus it makes sense to assert on that. + ASSERT(static_cast(k) >= 0); + + bytes_until_sample_ -= static_cast(k); + if (PREDICT_FALSE(bytes_until_sample_ < 0)) { + // Note, we undo sampling counter update, since we're not actually + // handling slow path in the "needs sampling" case (calling + // RecordAllocationSlow to reset counter). And we do that in order + // to avoid non-tail calls in malloc fast-path. See also comments + // on declaration inside Sampler class. + // + // volatile is used here to improve compiler's choice of + // instuctions. We know that this path is very rare and that there + // is no need to keep previous value of bytes_until_sample_ in + // register. This helps compiler generate slightly more efficient + // sub , instruction for subtraction above. + volatile ssize_t *ptr = + const_cast(&bytes_until_sample_); + *ptr += k; return false; } + return true; } // Inline functions which are public for testing purposes @@ -154,27 +219,14 @@ inline bool Sampler::SampleAllocation(size_t k) { // pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 // This is the lrand64 generator. inline uint64_t Sampler::NextRandom(uint64_t rnd) { - const uint64_t prng_mult = 0x5DEECE66DLL; + const uint64_t prng_mult = 0x5DEECE66DULL; const uint64_t prng_add = 0xB; const uint64_t prng_mod_power = 48; const uint64_t prng_mod_mask = - ~((~static_cast(0)) << prng_mod_power); + ~((~static_cast(0)) << prng_mod_power); return (prng_mult * rnd + prng_add) & prng_mod_mask; } -// Adapted from //util/math/fastmath.[h|cc] by Noam Shazeer -// This mimics the VeryFastLog2 code in those files -inline double Sampler::FastLog2(const double & d) { - ASSERT(d>0); - COMPILE_ASSERT(sizeof(d) == sizeof(uint64_t), DoubleMustBe64Bits); - uint64_t x; - memcpy(&x, &d, sizeof(x)); // we depend on the compiler inlining this - const uint32_t x_high = x >> 32; - const uint32_t y = x_high >> (20 - kFastlogNumBits) & kFastlogMask; - const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023; - return exponent + log_table_[y]; -} - } // namespace tcmalloc #endif // TCMALLOC_SAMPLER_H_ diff --git a/tpl/gperftools/src/span.h b/tpl/gperftools/src/span.h index 83feda1..ca3f710 100644 --- a/tpl/gperftools/src/span.h +++ b/tpl/gperftools/src/span.h @@ -37,21 +37,61 @@ #define TCMALLOC_SPAN_H_ #include +#include #include "common.h" +#include "base/logging.h" +#include "page_heap_allocator.h" namespace tcmalloc { +struct SpanBestFitLess; +struct Span; + +// Store a pointer to a span along with a cached copy of its length. +// These are used as set elements to improve the performance of +// comparisons during tree traversal: the lengths are inline with the +// tree nodes and thus avoid expensive cache misses to dereference +// the actual Span objects in most cases. +struct SpanPtrWithLength { + explicit SpanPtrWithLength(Span* s); + + Span* span; + Length length; +}; +typedef std::set > SpanSet; + +// Comparator for best-fit search, with address order as a tie-breaker. +struct SpanBestFitLess { + bool operator()(SpanPtrWithLength a, SpanPtrWithLength b) const; +}; + // Information kept for a span (a contiguous run of pages). struct Span { PageID start; // Starting page number Length length; // Number of pages in span Span* next; // Used when in link list Span* prev; // Used when in link list - void* objects; // Linked list of free objects + union { + void* objects; // Linked list of free objects + + // Span may contain iterator pointing back at SpanSet entry of + // this span into set of large spans. It is used to quickly delete + // spans from those sets. span_iter_space is space for such + // iterator which lifetime is controlled explicitly. + char span_iter_space[sizeof(SpanSet::iterator)]; + }; unsigned int refcount : 16; // Number of non-free objects unsigned int sizeclass : 8; // Size-class for small objects (or 0) unsigned int location : 2; // Is the span on a freelist, and if so, which? unsigned int sample : 1; // Sampled object? + bool has_span_iter : 1; // Iff span_iter_space has valid + // iterator. Only for debug builds. + + // Sets iterator stored in span_iter_space. + // Requires has_span_iter == 0. + void SetSpanSetIterator(const SpanSet::iterator& iter); + // Copies out and destroys iterator stored in span_iter_space. + SpanSet::iterator ExtractSpanSetIterator(); #undef SPAN_HISTORY #ifdef SPAN_HISTORY @@ -71,6 +111,39 @@ void Event(Span* span, char op, int v = 0); #define Event(s,o,v) ((void) 0) #endif +inline SpanPtrWithLength::SpanPtrWithLength(Span* s) + : span(s), + length(s->length) { +} + +inline bool SpanBestFitLess::operator()(SpanPtrWithLength a, SpanPtrWithLength b) const { + if (a.length < b.length) + return true; + if (a.length > b.length) + return false; + return a.span->start < b.span->start; +} + +inline void Span::SetSpanSetIterator(const SpanSet::iterator& iter) { + ASSERT(!has_span_iter); + has_span_iter = 1; + + new (span_iter_space) SpanSet::iterator(iter); +} + +inline SpanSet::iterator Span::ExtractSpanSetIterator() { + typedef SpanSet::iterator iterator_type; + + ASSERT(has_span_iter); + has_span_iter = 0; + + iterator_type* this_iter = + reinterpret_cast(span_iter_space); + iterator_type retval = *this_iter; + this_iter->~iterator_type(); + return retval; +} + // Allocator/deallocator for spans Span* NewSpan(PageID p, Length len); void DeleteSpan(Span* span); diff --git a/tpl/gperftools/src/stack_trace_table.cc b/tpl/gperftools/src/stack_trace_table.cc index 1862124..5888dc0 100644 --- a/tpl/gperftools/src/stack_trace_table.cc +++ b/tpl/gperftools/src/stack_trace_table.cc @@ -42,27 +42,15 @@ namespace tcmalloc { -bool StackTraceTable::Bucket::KeyEqual(uintptr_t h, - const StackTrace& t) const { - const bool eq = (this->hash == h && this->trace.depth == t.depth); - for (int i = 0; eq && i < t.depth; ++i) { - if (this->trace.stack[i] != t.stack[i]) { - return false; - } - } - return eq; -} - StackTraceTable::StackTraceTable() : error_(false), depth_total_(0), bucket_total_(0), - table_(new Bucket*[kHashTableSize]()) { - memset(table_, 0, kHashTableSize * sizeof(Bucket*)); + head_(nullptr) { } StackTraceTable::~StackTraceTable() { - delete[] table_; + ASSERT(head_ == nullptr); } void StackTraceTable::AddTrace(const StackTrace& t) { @@ -70,89 +58,64 @@ void StackTraceTable::AddTrace(const StackTrace& t) { return; } - // Hash function borrowed from base/heap-profile-table.cc - uintptr_t h = 0; - for (int i = 0; i < t.depth; ++i) { - h += reinterpret_cast(t.stack[i]); - h += h << 10; - h ^= h >> 6; - } - h += h << 3; - h ^= h >> 11; - - const int idx = h % kHashTableSize; - - Bucket* b = table_[idx]; - while (b != NULL && !b->KeyEqual(h, t)) { - b = b->next; - } - if (b != NULL) { - b->count++; - b->trace.size += t.size; // keep cumulative size + depth_total_ += t.depth; + bucket_total_++; + Entry* entry = allocator_.allocate(1); + if (entry == nullptr) { + Log(kLog, __FILE__, __LINE__, + "tcmalloc: could not allocate bucket", sizeof(*entry)); + error_ = true; } else { - depth_total_ += t.depth; - bucket_total_++; - b = Static::bucket_allocator()->New(); - if (b == NULL) { - Log(kLog, __FILE__, __LINE__, - "tcmalloc: could not allocate bucket", sizeof(*b)); - error_ = true; - } else { - b->hash = h; - b->trace = t; - b->count = 1; - b->next = table_[idx]; - table_[idx] = b; - } + entry->trace = t; + entry->next = head_; + head_ = entry; } } void** StackTraceTable::ReadStackTracesAndClear() { - if (error_) { - return NULL; - } + void** out = nullptr; - // Allocate output array const int out_len = bucket_total_ * 3 + depth_total_ + 1; - void** out = new void*[out_len]; - if (out == NULL) { - Log(kLog, __FILE__, __LINE__, - "tcmalloc: allocation failed for stack traces", - out_len * sizeof(*out)); - return NULL; + if (!error_) { + // Allocate output array + out = new (std::nothrow_t{}) void*[out_len]; + if (out == nullptr) { + Log(kLog, __FILE__, __LINE__, + "tcmalloc: allocation failed for stack traces", + out_len * sizeof(*out)); + } } - // Fill output array - int idx = 0; - for (int i = 0; i < kHashTableSize; ++i) { - Bucket* b = table_[i]; - while (b != NULL) { - out[idx++] = reinterpret_cast(static_cast(b->count)); - out[idx++] = reinterpret_cast(b->trace.size); // cumulative size - out[idx++] = reinterpret_cast(b->trace.depth); - for (int d = 0; d < b->trace.depth; ++d) { - out[idx++] = b->trace.stack[d]; + if (out) { + // Fill output array + int idx = 0; + Entry* entry = head_; + while (entry != NULL) { + out[idx++] = reinterpret_cast(uintptr_t{1}); // count + out[idx++] = reinterpret_cast(entry->trace.size); // cumulative size + out[idx++] = reinterpret_cast(entry->trace.depth); + for (int d = 0; d < entry->trace.depth; ++d) { + out[idx++] = entry->trace.stack[d]; } - b = b->next; + entry = entry->next; } + out[idx++] = NULL; + ASSERT(idx == out_len); } - out[idx++] = NULL; - ASSERT(idx == out_len); // Clear state error_ = false; depth_total_ = 0; bucket_total_ = 0; + SpinLockHolder h(Static::pageheap_lock()); - for (int i = 0; i < kHashTableSize; ++i) { - Bucket* b = table_[i]; - while (b != NULL) { - Bucket* next = b->next; - Static::bucket_allocator()->Delete(b); - b = next; - } - table_[i] = NULL; + Entry* entry = head_; + while (entry != nullptr) { + Entry* next = entry->next; + allocator_.deallocate(entry, 1); + entry = next; } + head_ = nullptr; return out; } diff --git a/tpl/gperftools/src/stack_trace_table.h b/tpl/gperftools/src/stack_trace_table.h index e289771..46b86ba 100644 --- a/tpl/gperftools/src/stack_trace_table.h +++ b/tpl/gperftools/src/stack_trace_table.h @@ -41,6 +41,7 @@ #include // for uintptr_t #endif #include "common.h" +#include "page_heap_allocator.h" namespace tcmalloc { @@ -62,29 +63,21 @@ class PERFTOOLS_DLL_DECL StackTraceTable { void** ReadStackTracesAndClear(); // Exposed for PageHeapAllocator - struct Bucket { - // Key - uintptr_t hash; - StackTrace trace; - - // Payload - int count; - Bucket* next; - - bool KeyEqual(uintptr_t h, const StackTrace& t) const; - }; - // For testing int depth_total() const { return depth_total_; } int bucket_total() const { return bucket_total_; } private: - static const int kHashTableSize = 1 << 14; // => table_ is 128k + struct Entry { + Entry* next; + StackTrace trace; + }; bool error_; int depth_total_; int bucket_total_; - Bucket** table_; + Entry* head_; + STLPageHeapAllocator allocator_; }; } // namespace tcmalloc diff --git a/tpl/gperftools/src/stacktrace.cc b/tpl/gperftools/src/stacktrace.cc index 999863c..7e853d8 100644 --- a/tpl/gperftools/src/stacktrace.cc +++ b/tpl/gperftools/src/stacktrace.cc @@ -60,6 +60,7 @@ #include "gperftools/stacktrace.h" #include "base/commandlineflags.h" #include "base/googleinit.h" +#include "getenv_safe.h" // we're using plain struct and not class to avoid any possible issues @@ -90,6 +91,15 @@ struct GetStackImplementation { #define HAVE_GST_generic #endif +#ifdef HAVE_UNWIND_BACKTRACE +#define STACKTRACE_INL_HEADER "stacktrace_libgcc-inl.h" +#define GST_SUFFIX libgcc +#include "stacktrace_impl_setup-inl.h" +#undef GST_SUFFIX +#undef STACKTRACE_INL_HEADER +#define HAVE_GST_libgcc +#endif + // libunwind uses __thread so we check for both libunwind.h and // __thread support #if defined(HAVE_LIBUNWIND_H) && defined(HAVE_TLS) @@ -153,6 +163,9 @@ struct GetStackImplementation { #endif static GetStackImplementation *all_impls[] = { +#ifdef HAVE_GST_libgcc + &impl__libgcc, +#endif #ifdef HAVE_GST_generic &impl__generic, #endif @@ -185,6 +198,8 @@ static GetStackImplementation *all_impls[] = { #endif #endif +static bool get_stack_impl_inited; + #if defined(HAVE_GST_instrument) static GetStackImplementation *get_stack_impl = &impl__instrument; #elif defined(HAVE_GST_win32) @@ -195,10 +210,12 @@ static GetStackImplementation *get_stack_impl = &impl__x86; static GetStackImplementation *get_stack_impl = &impl__ppc; #elif defined(HAVE_GST_libunwind) static GetStackImplementation *get_stack_impl = &impl__libunwind; -#elif defined(HAVE_GST_arm) -static GetStackImplementation *get_stack_impl = &impl__arm; +#elif defined(HAVE_GST_libgcc) +static GetStackImplementation *get_stack_impl = &impl__libgcc; #elif defined(HAVE_GST_generic) static GetStackImplementation *get_stack_impl = &impl__generic; +#elif defined(HAVE_GST_arm) +static GetStackImplementation *get_stack_impl = &impl__arm; #elif 0 // This is for the benefit of code analysis tools that may have // trouble with the computed #include above. @@ -217,13 +234,52 @@ static int ATTRIBUTE_NOINLINE frame_forcer(int rv) { return rv; } +static void init_default_stack_impl_inner(void); + +namespace tcmalloc { + bool EnterStacktraceScope(void); + void LeaveStacktraceScope(void); +} + +namespace { + using tcmalloc::EnterStacktraceScope; + using tcmalloc::LeaveStacktraceScope; + + class StacktraceScope { + bool stacktrace_allowed; + public: + StacktraceScope() { + stacktrace_allowed = true; + stacktrace_allowed = EnterStacktraceScope(); + } + bool IsStacktraceAllowed() { + return stacktrace_allowed; + } + ~StacktraceScope() { + if (stacktrace_allowed) { + LeaveStacktraceScope(); + } + } + }; +} + PERFTOOLS_DLL_DECL int GetStackFrames(void** result, int* sizes, int max_depth, int skip_count) { + StacktraceScope scope; + if (!scope.IsStacktraceAllowed()) { + return 0; + } + init_default_stack_impl_inner(); return frame_forcer(get_stack_impl->GetStackFramesPtr(result, sizes, max_depth, skip_count)); } PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int max_depth, int skip_count, const void *uc) { + StacktraceScope scope; + if (!scope.IsStacktraceAllowed()) { + return 0; + } + init_default_stack_impl_inner(); return frame_forcer(get_stack_impl->GetStackFramesWithContextPtr( result, sizes, max_depth, skip_count, uc)); @@ -231,17 +287,31 @@ PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int PERFTOOLS_DLL_DECL int GetStackTrace(void** result, int max_depth, int skip_count) { + StacktraceScope scope; + if (!scope.IsStacktraceAllowed()) { + return 0; + } + init_default_stack_impl_inner(); return frame_forcer(get_stack_impl->GetStackTracePtr(result, max_depth, skip_count)); } PERFTOOLS_DLL_DECL int GetStackTraceWithContext(void** result, int max_depth, int skip_count, const void *uc) { + StacktraceScope scope; + if (!scope.IsStacktraceAllowed()) { + return 0; + } + init_default_stack_impl_inner(); return frame_forcer(get_stack_impl->GetStackTraceWithContextPtr( result, max_depth, skip_count, uc)); } static void init_default_stack_impl_inner(void) { - char *val = getenv("TCMALLOC_STACKTRACE_METHOD"); + if (get_stack_impl_inited) { + return; + } + get_stack_impl_inited = true; + const char *val = TCMallocGetenvSafe("TCMALLOC_STACKTRACE_METHOD"); if (!val || !*val) { return; } diff --git a/tpl/gperftools/src/stacktrace_libgcc-inl.h b/tpl/gperftools/src/stacktrace_libgcc-inl.h new file mode 100644 index 0000000..ce9cf51 --- /dev/null +++ b/tpl/gperftools/src/stacktrace_libgcc-inl.h @@ -0,0 +1,111 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Copyright (c) 2016, gperftools Contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file implements backtrace capturing via libgcc's +// _Unwind_Backtrace. This generally works almost always. It will fail +// sometimes when we're trying to capture backtrace from signal +// handler (i.e. in cpu profiler) while some C++ code is throwing +// exception. + +#ifndef BASE_STACKTRACE_LIBGCC_INL_H_ +#define BASE_STACKTRACE_LIBGCC_INL_H_ +// Note: this file is included into stacktrace.cc more than once. +// Anything that should only be defined once should be here: + +extern "C" { +#include +#include // for memset() +} + +#include + +#include "gperftools/stacktrace.h" + +struct libgcc_backtrace_data { + void **array; + int skip; + int pos; + int limit; +}; + +static _Unwind_Reason_Code libgcc_backtrace_helper(struct _Unwind_Context *ctx, + void *_data) { + libgcc_backtrace_data *data = + reinterpret_cast(_data); + + if (data->skip > 0) { + data->skip--; + return _URC_NO_REASON; + } + + if (data->pos < data->limit) { + void *ip = reinterpret_cast(_Unwind_GetIP(ctx));; + data->array[data->pos++] = ip; + } + + return _URC_NO_REASON; +} + +#endif // BASE_STACKTRACE_LIBGCC_INL_H_ + +// Note: this part of the file is included several times. +// Do not put globals below. + +// The following 4 functions are generated from the code below: +// GetStack{Trace,Frames}() +// GetStack{Trace,Frames}WithContext() +// +// These functions take the following args: +// void** result: the stack-trace, as an array +// int* sizes: the size of each stack frame, as an array +// (GetStackFrames* only) +// int max_depth: the size of the result (and sizes) array(s) +// int skip_count: how many stack pointers to skip before storing in result +// void* ucp: a ucontext_t* (GetStack{Trace,Frames}WithContext only) +static int GET_STACK_TRACE_OR_FRAMES { + libgcc_backtrace_data data; + data.array = result; + // we're also skipping current and parent's frame + data.skip = skip_count + 2; + data.pos = 0; + data.limit = max_depth; + + _Unwind_Backtrace(libgcc_backtrace_helper, &data); + + if (data.pos > 1 && data.array[data.pos - 1] == NULL) + --data.pos; + +#if IS_STACK_FRAMES + // No implementation for finding out the stack frame sizes. + memset(sizes, 0, sizeof(*sizes) * data.pos); +#endif + + return data.pos; +} diff --git a/tpl/gperftools/src/stacktrace_libunwind-inl.h b/tpl/gperftools/src/stacktrace_libunwind-inl.h index 8a4a731..6f361ec 100644 --- a/tpl/gperftools/src/stacktrace_libunwind-inl.h +++ b/tpl/gperftools/src/stacktrace_libunwind-inl.h @@ -47,6 +47,8 @@ extern "C" { #include } #include "gperftools/stacktrace.h" + +#include "base/basictypes.h" #include "base/logging.h" // Sometimes, we can try to get a stack trace from within a stack @@ -56,7 +58,7 @@ extern "C" { // recursive request, we'd end up with infinite recursion or deadlock. // Luckily, it's safe to ignore those subsequent traces. In such // cases, we return 0 to indicate the situation. -static __thread int recursive; +static __thread int recursive ATTR_INITIAL_EXEC; #if defined(TCMALLOC_ENABLE_UNWIND_FROM_UCONTEXT) && (defined(__i386__) || defined(__x86_64__)) && defined(__GNU_LIBRARY__) #define BASE_STACKTRACE_UNW_CONTEXT_IS_UCONTEXT 1 diff --git a/tpl/gperftools/src/stacktrace_powerpc-linux-inl.h b/tpl/gperftools/src/stacktrace_powerpc-linux-inl.h index 5d16fa1..a301a46 100644 --- a/tpl/gperftools/src/stacktrace_powerpc-linux-inl.h +++ b/tpl/gperftools/src/stacktrace_powerpc-linux-inl.h @@ -44,6 +44,7 @@ #include // for uintptr_t #include // for NULL +#include // for siginfo_t #include #include @@ -52,7 +53,6 @@ #elif defined(HAVE_UCONTEXT_H) #include // for ucontext_t #endif -typedef ucontext ucontext_t; // PowerPC64 Little Endian follows BE wrt. backchain, condition register, // and LR save area, so no need to adjust the reading struct. @@ -201,7 +201,7 @@ static int GET_STACK_TRACE_OR_FRAMES { struct rt_signal_frame_32 { char dummy[64 + 16]; siginfo_t info; - struct ucontext uc; + ucontext_t uc; // We don't care about the rest, since IP value is at 'uc' field.A } *sigframe = reinterpret_cast(current); result[n] = (void*) sigframe->uc.uc_mcontext.uc_regs->gregs[PT_NIP]; diff --git a/tpl/gperftools/src/static_vars.cc b/tpl/gperftools/src/static_vars.cc index 6025e46..ab5a9cc 100644 --- a/tpl/gperftools/src/static_vars.cc +++ b/tpl/gperftools/src/static_vars.cc @@ -44,7 +44,6 @@ #include "getenv_safe.h" // TCMallocGetenvSafe #include "base/googleinit.h" #include "maybe_threads.h" -#include namespace tcmalloc { @@ -56,28 +55,27 @@ namespace tcmalloc { void CentralCacheLockAll() { Static::pageheap_lock()->Lock(); - for (int i = 0; i < kNumClasses; ++i) + for (int i = 0; i < Static::num_size_classes(); ++i) Static::central_cache()[i].Lock(); } void CentralCacheUnlockAll() { - for (int i = 0; i < kNumClasses; ++i) + for (int i = 0; i < Static::num_size_classes(); ++i) Static::central_cache()[i].Unlock(); Static::pageheap_lock()->Unlock(); } #endif +bool Static::inited_; SpinLock Static::pageheap_lock_(SpinLock::LINKER_INITIALIZED); SizeMap Static::sizemap_; -CentralFreeListPadded Static::central_cache_[kNumClasses]; +CentralFreeListPadded Static::central_cache_[kClassSizesMax]; PageHeapAllocator Static::span_allocator_; PageHeapAllocator Static::stacktrace_allocator_; Span Static::sampled_objects_; -PageHeapAllocator Static::bucket_allocator_; StackTrace* Static::growth_stacks_ = NULL; -PageHeap* Static::pageheap_ = NULL; - +Static::PageHeapStorage Static::pageheap_; void Static::InitStaticVars() { sizemap_.Init(); @@ -85,73 +83,70 @@ void Static::InitStaticVars() { span_allocator_.New(); // Reduce cache conflicts span_allocator_.New(); // Reduce cache conflicts stacktrace_allocator_.Init(); - bucket_allocator_.Init(); // Do a bit of sanitizing: make sure central_cache is aligned properly CHECK_CONDITION((sizeof(central_cache_[0]) % 64) == 0); - for (int i = 0; i < kNumClasses; ++i) { + for (int i = 0; i < num_size_classes(); ++i) { central_cache_[i].Init(i); } - // It's important to have PageHeap allocated, not in static storage, - // so that HeapLeakChecker does not consider all the byte patterns stored - // in is caches as pointers that are sources of heap object liveness, - // which leads to it missing some memory leaks. - pageheap_ = new (MetaDataAlloc(sizeof(PageHeap))) PageHeap; - - bool aggressive_decommit = - tcmalloc::commandlineflags::StringToBool( - TCMallocGetenvSafe("TCMALLOC_AGGRESSIVE_DECOMMIT"), true); - - pageheap_->SetAggressiveDecommit(aggressive_decommit); - - DLL_Init(&sampled_objects_); - Sampler::InitStatics(); -} + new (&pageheap_.memory) PageHeap; -/* -void Static::DeInitStaticVars() { - sizemap_.Init(); - span_allocator_.Init(); - span_allocator_.New(); // Reduce cache conflicts - span_allocator_.New(); // Reduce cache conflicts - stacktrace_allocator_.Init(); - bucket_allocator_.Init(); - // Do a bit of sanitizing: make sure central_cache is aligned properly - CHECK_CONDITION((sizeof(central_cache_[0]) % 64) == 0); - for (int i = 0; i < kNumClasses; ++i) { - central_cache_[i].Init(i); - } +#if defined(ENABLE_AGGRESSIVE_DECOMMIT_BY_DEFAULT) + const bool kDefaultAggressiveDecommit = true; +#else + const bool kDefaultAggressiveDecommit = false; +#endif - // It's important to have PageHeap allocated, not in static storage, - // so that HeapLeakChecker does not consider all the byte patterns stored - // in is caches as pointers that are sources of heap object liveness, - // which leads to it missing some memory leaks. - pageheap_ = new (MetaDataAlloc(sizeof(PageHeap))) PageHeap; bool aggressive_decommit = tcmalloc::commandlineflags::StringToBool( - TCMallocGetenvSafe("TCMALLOC_AGGRESSIVE_DECOMMIT"), true); + TCMallocGetenvSafe("TCMALLOC_AGGRESSIVE_DECOMMIT"), + kDefaultAggressiveDecommit); - pageheap_->SetAggressiveDecommit(aggressive_decommit); + pageheap()->SetAggressiveDecommit(aggressive_decommit); + + inited_ = true; DLL_Init(&sampled_objects_); - Sampler::InitStatics(); } -*/ - -#if defined(HAVE_FORK) && defined(HAVE_PTHREAD) && !defined(__APPLE__) +void Static::InitLateMaybeRecursive() { +#if defined(HAVE_FORK) && defined(HAVE_PTHREAD) \ + && !defined(__APPLE__) && !defined(TCMALLOC_NO_ATFORK) + // OSX has it's own way of handling atfork in malloc (see + // libc_override_osx.h). + // + // For other OSes we do pthread_atfork even if standard seemingly + // discourages pthread_atfork, asking apps to do only + // async-signal-safe calls between fork and exec. + // + // We're deliberately attempting to register atfork handlers as part + // of malloc initialization. So very early. This ensures that our + // handler is called last and that means fork will try to grab + // tcmalloc locks last avoiding possible issues with many other + // locks that are held around calls to malloc. I.e. if we don't do + // that, fork() grabbing malloc lock before such other lock would be + // prone to deadlock, if some other thread holds other lock and + // calls malloc. + // + // We still leave some way of disabling it via + // -DTCMALLOC_NO_ATFORK. It looks like on glibc even with fully + // static binaries malloc is really initialized very early. But I + // can see how combination of static linking and other libc-s could + // be less fortunate and allow some early app constructors to run + // before malloc is ever called. -static inline -void SetupAtForkLocksHandler() -{ perftools_pthread_atfork( CentralCacheLockAll, // parent calls before fork CentralCacheUnlockAll, // parent calls after fork CentralCacheUnlockAll); // child calls after fork -} -REGISTER_MODULE_INITIALIZER(tcmalloc_fork_handler, SetupAtForkLocksHandler()); +#endif +#ifndef NDEBUG + // pthread_atfork above may malloc sometimes. Lets ensure we test + // that malloc works from here. + free(malloc(1)); #endif +} } // namespace tcmalloc diff --git a/tpl/gperftools/src/static_vars.h b/tpl/gperftools/src/static_vars.h index 66373e0..bef0180 100644 --- a/tpl/gperftools/src/static_vars.h +++ b/tpl/gperftools/src/static_vars.h @@ -37,6 +37,7 @@ #define TCMALLOC_STATIC_VARS_H_ #include +#include "base/basictypes.h" #include "base/spinlock.h" #include "central_freelist.h" #include "common.h" @@ -54,7 +55,7 @@ class Static { // Must be called before calling any of the accessors below. static void InitStaticVars(); - static void DeInitStaticVars(); + static void InitLateMaybeRecursive(); // Central cache -- an array of free-lists, one per size-class. // We have a separate lock per free-list to reduce contention. @@ -62,12 +63,14 @@ class Static { static SizeMap* sizemap() { return &sizemap_; } + static unsigned num_size_classes() { return sizemap_.num_size_classes; } + ////////////////////////////////////////////////////////////////////// // In addition to the explicit initialization comment, the variables below // must be protected by pageheap_lock. // Page-level allocator. - static PageHeap* pageheap() { return pageheap_; } + static PageHeap* pageheap() { return reinterpret_cast(&pageheap_.memory); } static PageHeapAllocator* span_allocator() { return &span_allocator_; } @@ -80,35 +83,42 @@ class Static { // State kept for sampled allocations (/pprof/heap support) static Span* sampled_objects() { return &sampled_objects_; } - static PageHeapAllocator* bucket_allocator() { - return &bucket_allocator_; - } // Check if InitStaticVars() has been run. - static bool IsInited() { return pageheap() != NULL; } + static bool IsInited() { return inited_; } private: - static SpinLock pageheap_lock_; + // some unit tests depend on this and link to static vars + // imperfectly. Thus we keep those unhidden for now. Thankfully + // they're not performance-critical. + /* ATTRIBUTE_HIDDEN */ static bool inited_; + /* ATTRIBUTE_HIDDEN */ static SpinLock pageheap_lock_; // These static variables require explicit initialization. We cannot // count on their constructors to do any initialization because other // static variables may try to allocate memory before these variables // can run their constructors. - static SizeMap sizemap_; - static CentralFreeListPadded central_cache_[kNumClasses]; - static PageHeapAllocator span_allocator_; - static PageHeapAllocator stacktrace_allocator_; - static Span sampled_objects_; - static PageHeapAllocator bucket_allocator_; + ATTRIBUTE_HIDDEN static SizeMap sizemap_; + ATTRIBUTE_HIDDEN static CentralFreeListPadded central_cache_[kClassSizesMax]; + ATTRIBUTE_HIDDEN static PageHeapAllocator span_allocator_; + ATTRIBUTE_HIDDEN static PageHeapAllocator stacktrace_allocator_; + ATTRIBUTE_HIDDEN static Span sampled_objects_; // Linked list of stack traces recorded every time we allocated memory // from the system. Useful for finding allocation sites that cause // increase in the footprint of the system. The linked list pointer // is stored in trace->stack[kMaxStackDepth-1]. - static StackTrace* growth_stacks_; - - static PageHeap* pageheap_; + ATTRIBUTE_HIDDEN static StackTrace* growth_stacks_; + + // PageHeap uses a constructor for initialization. Like the members above, + // we can't depend on initialization order, so pageheap is new'd + // into this buffer. + union PageHeapStorage { + char memory[sizeof(PageHeap)]; + uintptr_t extra; // To force alignment + }; + ATTRIBUTE_HIDDEN static PageHeapStorage pageheap_; }; } // namespace tcmalloc diff --git a/tpl/gperftools/src/symbolize.cc b/tpl/gperftools/src/symbolize.cc index a27106e..d28a10d 100755 --- a/tpl/gperftools/src/symbolize.cc +++ b/tpl/gperftools/src/symbolize.cc @@ -60,19 +60,24 @@ #include "base/commandlineflags.h" #include "base/logging.h" #include "base/sysinfo.h" +#if defined(__FreeBSD__) +#include +#endif using std::string; using tcmalloc::DumpProcSelfMaps; // from sysinfo.h - -DEFINE_string(symbolize_pprof, - EnvToString("PPROF_PATH", "pprof"), - "Path to pprof to call for reporting function names."); - -// heap_profile_table_pprof may be referenced after destructors are +// pprof may be used after destructors are // called (since that's when leak-checking is done), so we make // a more-permanent copy that won't ever get destroyed. -static string* g_pprof_path = new string(FLAGS_symbolize_pprof); +static char* get_pprof_path() { + static char* result = ([] () { + string pprof_string = EnvToString("PPROF_PATH", "pprof-symbolize"); + return strdup(pprof_string.c_str()); + })(); + + return result; +} // Returns NULL if we're on an OS where we can't get the invocation name. // Using a static var is ok because we're not called from a thread. @@ -94,6 +99,13 @@ static const char* GetProgramInvocationName() { return NULL; } return program_invocation_name; +#elif defined(__FreeBSD__) + static char program_invocation_name[PATH_MAX]; + size_t len = sizeof(program_invocation_name); + static const int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + if (!sysctl(name, 4, program_invocation_name, &len, NULL, 0)) + return program_invocation_name; + return NULL; #else return NULL; // figure out a way to get argv[0] #endif @@ -134,7 +146,7 @@ int SymbolTable::Symbolize() { PrintError("Cannot figure out the name of this executable (argv0)"); return 0; } - if (access(g_pprof_path->c_str(), R_OK) != 0) { + if (access(get_pprof_path(), R_OK) != 0) { PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)"); return 0; } @@ -196,7 +208,7 @@ int SymbolTable::Symbolize() { unsetenv("HEAPPROFILE"); unsetenv("HEAPCHECK"); unsetenv("PERFTOOLS_VERBOSE"); - execlp(g_pprof_path->c_str(), g_pprof_path->c_str(), + execlp(get_pprof_path(), get_pprof_path(), "--symbols", argv0, NULL); _exit(3); // if execvp fails, it's bad news for us } @@ -238,6 +250,7 @@ int SymbolTable::Symbolize() { } write(child_in[1], pprof_buffer, strlen(pprof_buffer)); close(child_in[1]); // that's all we need to write + delete[] pprof_buffer; const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size(); int total_bytes_read = 0; diff --git a/tpl/gperftools/src/system-alloc.cc b/tpl/gperftools/src/system-alloc.cc index 4743b03..aeba8ed 100755 --- a/tpl/gperftools/src/system-alloc.cc +++ b/tpl/gperftools/src/system-alloc.cc @@ -55,9 +55,6 @@ #include "base/spinlock.h" // for SpinLockHolder, SpinLock, etc #include "common.h" #include "internal_logging.h" -#include "assert.h" - -#include // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old // form of the name instead. @@ -65,6 +62,14 @@ # define MAP_ANONYMOUS MAP_ANON #endif +// Linux added support for MADV_FREE in 4.5 but we aren't ready to use it +// yet. Among other things, using compile-time detection leads to poor +// results when compiling on a system with MADV_FREE and running on a +// system without it. See https://github.com/gperftools/gperftools/issues/780. +#if defined(__linux__) && defined(MADV_FREE) && !defined(TCMALLOC_USE_MADV_FREE) +# undef MADV_FREE +#endif + // MADV_FREE is specifically designed for use by malloc(), but only // FreeBSD supports it; in linux we fall back to the somewhat inferior // MADV_DONTNEED. @@ -91,21 +96,15 @@ static const bool kDebugMode = true; using tcmalloc::kLog; using tcmalloc::Log; -// Anonymous namespace to avoid name conflicts on "CheckAddressBits". -namespace { - // Check that no bit is set at position ADDRESS_BITS or higher. -template bool CheckAddressBits(uintptr_t ptr) { - return (ptr >> ADDRESS_BITS) == 0; +static bool CheckAddressBits(uintptr_t ptr) { + bool always_ok = (kAddressBits == 8 * sizeof(void*)); + // this is a bit insane but otherwise we get compiler warning about + // shifting right by word size even if this code is dead :( + int shift_bits = always_ok ? 0 : kAddressBits; + return always_ok || ((ptr >> shift_bits) == 0); } -// Specialize for the bit width of a pointer to avoid undefined shift. -template <> bool CheckAddressBits<8 * sizeof(void*)>(uintptr_t ptr) { - return true; -} - -} // Anonymous namespace to avoid name conflicts on "CheckAddressBits". - COMPILE_ASSERT(kAddressBits <= 8 * sizeof(void*), address_bits_larger_than_pointer_size); @@ -117,7 +116,7 @@ static size_t pagesize = 0; #endif // The current system allocator -SysAllocator* sys_alloc = NULL; +SysAllocator* tcmalloc_sys_alloc = NULL; // Number of bytes taken from system. size_t TCMalloc_SystemTaken = 0; @@ -149,13 +148,16 @@ class SbrkSysAllocator : public SysAllocator { } void* Alloc(size_t size, size_t *actual_size, size_t alignment); }; -static char sbrk_space[sizeof(SbrkSysAllocator)]; +static union { + char buf[sizeof(SbrkSysAllocator)]; + void *ptr; +} sbrk_space; class MemalignSysAllocator : public SysAllocator { public: MemalignSysAllocator() : SysAllocator() { } - void* Alloc(size_t size, size_t *actual_size, size_t alignment) override; + void* Alloc(size_t size, size_t *actual_size, size_t alignment); }; static char memalign_space[sizeof(MemalignSysAllocator)]; @@ -163,15 +165,18 @@ class MmapSysAllocator : public SysAllocator { public: MmapSysAllocator() : SysAllocator() { } - void* Alloc(size_t size, size_t *actual_size, size_t alignment) override; + void* Alloc(size_t size, size_t *actual_size, size_t alignment); }; -static char mmap_space[sizeof(MmapSysAllocator)]; +static union { + char buf[sizeof(MmapSysAllocator)]; + void *ptr; +} mmap_space; class DevMemSysAllocator : public SysAllocator { public: DevMemSysAllocator() : SysAllocator() { } - void* Alloc(size_t size, size_t *actual_size, size_t alignment) override; + void* Alloc(size_t size, size_t *actual_size, size_t alignment); }; class DefaultSysAllocator : public SysAllocator { @@ -191,7 +196,7 @@ class DefaultSysAllocator : public SysAllocator { names_[index] = name; } } - void* Alloc(size_t size, size_t *actual_size, size_t alignment) override; + void* Alloc(size_t size, size_t *actual_size, size_t alignment); private: static const int kMaxAllocators = 2; @@ -199,10 +204,13 @@ class DefaultSysAllocator : public SysAllocator { SysAllocator* allocs_[kMaxAllocators]; const char* names_[kMaxAllocators]; }; -static char default_space[sizeof(DefaultSysAllocator)]; +static union { + char buf[sizeof(DefaultSysAllocator)]; + void *ptr; +} default_space; static const char sbrk_name[] = "SbrkSysAllocator"; static const char mmap_name[] = "MmapSysAllocator"; -static const char memalign_name[] = "MemalignSysAllocator"; +static const char memalign_name[] = "MemalignAllocator"; void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, @@ -284,6 +292,12 @@ void* MemalignSysAllocator::Alloc(size_t size, size_t *actual_size, // [LUNASA] We don't currently support a mechanism for disabling this allocator. // Co-existing with other memory allocators means that we need to be sure // not to change the size of the heap directly (e.g., by using sbrk) + // +#if 0 + // !!!!! DEBUG !!!! + printf("MemalignSysAllocator::Alloc(%d, %p, %d)\n", size, actual_size, alignment); + fflush(stdout); +#endif void *ptr; int rc = posix_memalign(&ptr, alignment, size); @@ -291,7 +305,7 @@ void* MemalignSysAllocator::Alloc(size_t size, size_t *actual_size, return NULL; } *actual_size = size; - + return ptr; #endif // HAVE_MEMALIGN } @@ -481,11 +495,12 @@ SysAllocator *tc_get_sysalloc_override(SysAllocator *def) static bool system_alloc_inited = false; void InitSystemAllocators(void) { #if 0 - MmapSysAllocator *mmap = new (mmap_space) MmapSysAllocator(); - SbrkSysAllocator *sbrk = new (sbrk_space) SbrkSysAllocator(); + MmapSysAllocator *mmap = new (mmap_space.buf) MmapSysAllocator(); + SbrkSysAllocator *sbrk = new (sbrk_space.buf) SbrkSysAllocator(); #endif MemalignSysAllocator *memalign = new (memalign_space) MemalignSysAllocator(); + // In 64-bit debug mode, place the mmap allocator first since it // allocates pointers that do not fit in 32 bits and therefore gives // us better testing of code's 64-bit correctness. It also leads to @@ -493,14 +508,13 @@ void InitSystemAllocators(void) { // likely to look like pointers and therefore the conservative gc in // the heap-checker is less likely to misinterpret a number as a // pointer). - DefaultSysAllocator *sdef = new (default_space) DefaultSysAllocator(); - + DefaultSysAllocator *sdef = new (default_space.buf) DefaultSysAllocator(); // [LUNASA] Because we're operating in the presence of other memory allocators // (e.g., malloc/free), we should not use sbrk() directly. Also, // we're not currently using the heap-checking features of gperftools // so reducing false negatives is not necessary. sdef->SetChildAllocator(memalign, 0, memalign_name); - + #if 0 if (kDebugMode && sizeof(void*) > 4) { sdef->SetChildAllocator(mmap, 0, mmap_name); @@ -511,11 +525,7 @@ void InitSystemAllocators(void) { } #endif - sys_alloc = tc_get_sysalloc_override(sdef); -} - -void TCMalloc_ResetSystemAlloc () { - system_alloc_inited = false; + tcmalloc_sys_alloc = tc_get_sysalloc_override(sdef); } void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, @@ -526,9 +536,6 @@ void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, SpinLockHolder lock_holder(&spinlock); if (!system_alloc_inited) { -#ifdef DEBUG - std::cerr << "Initializing system allocator\n"; -#endif InitSystemAllocators(); system_alloc_inited = true; } @@ -541,12 +548,10 @@ void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, actual_size = &actual_size_storage; } - // std::cerr << "calling system alloc " << sys_alloc << " with size " << size << std::endl; - void* result = sys_alloc->Alloc(size, actual_size, alignment); + void* result = tcmalloc_sys_alloc->Alloc(size, actual_size, alignment); if (result != NULL) { CHECK_CONDITION( - CheckAddressBits( - reinterpret_cast(result) + *actual_size - 1)); + CheckAddressBits(reinterpret_cast(result) + *actual_size - 1)); TCMalloc_SystemTaken += *actual_size; } return result; @@ -578,15 +583,13 @@ bool TCMalloc_SystemRelease(void* start, size_t length) { ASSERT(new_end <= end); if (new_end > new_start) { - //int result; int result = 0; - do { #if 0 - printf("MADVISE %p %d\n", (void *)new_start, new_end-new_start); + do { result = madvise(reinterpret_cast(new_start), new_end - new_start, MADV_FREE); -#endif } while (result == -1 && errno == EAGAIN); +#endif return result != -1; } diff --git a/tpl/gperftools/src/system-alloc.h b/tpl/gperftools/src/system-alloc.h index c7531cf..655d470 100644 --- a/tpl/gperftools/src/system-alloc.h +++ b/tpl/gperftools/src/system-alloc.h @@ -59,7 +59,6 @@ class SysAllocator; // // Returns NULL when out of memory. extern PERFTOOLS_DLL_DECL -void TCMalloc_ResetSystemAlloc (); void* TCMalloc_SystemAlloc(size_t bytes, size_t *actual_bytes, size_t alignment = 0); @@ -85,7 +84,7 @@ extern PERFTOOLS_DLL_DECL void TCMalloc_SystemCommit(void* start, size_t length); // The current system allocator. -extern PERFTOOLS_DLL_DECL SysAllocator* sys_alloc; +extern PERFTOOLS_DLL_DECL SysAllocator* tcmalloc_sys_alloc; // Number of bytes taken from system. extern PERFTOOLS_DLL_DECL size_t TCMalloc_SystemTaken; diff --git a/tpl/gperftools/src/tcmalloc.cc b/tpl/gperftools/src/tcmalloc.cc index 0674799..6f304a4 100644 --- a/tpl/gperftools/src/tcmalloc.cc +++ b/tpl/gperftools/src/tcmalloc.cc @@ -34,7 +34,7 @@ // A malloc that uses a per-thread cache to satisfy small malloc requests. // (The time for malloc/free of a small object drops from 300 ns to 50 ns.) // -// See doc/tcmalloc.html for a high-level +// See docs/tcmalloc.html for a high-level // description of how this malloc works. // // SYNCHRONIZATION @@ -88,12 +88,12 @@ // goes from about 1100 ns to about 300 ns. #include "config.h" +// At least for gcc on Linux/i386 and Linux/amd64 not adding throw() +// to tc_xxx functions actually ends up generating better code. +#define PERFTOOLS_NOTHROW #include #include // for ENOMEM, EINVAL, errno -#ifdef HAVE_SYS_CDEFS_H -#include // for __THROW -#endif #if defined HAVE_STDINT_H #include #elif defined HAVE_INTTYPES_H @@ -114,6 +114,7 @@ #include #include // for MallocHook +#include #include "base/basictypes.h" // for int64 #include "base/commandlineflags.h" // for RegisterFlagValidator, etc #include "base/dynamic_annotations.h" // for RunningOnValgrind @@ -132,18 +133,7 @@ #include "tcmalloc_guard.h" // for TCMallocGuard #include "thread_cache.h" // for ThreadCache -#include - -#ifdef __clang__ -// clang's apparent focus on code size somehow causes it to ignore -// normal inline directives even for few functions which inlining is -// key for performance. In order to get performance of clang's -// generated code closer to normal, we're forcing inlining via -// attribute. -#define ALWAYS_INLINE inline __attribute__((always_inline)) -#else -#define ALWAYS_INLINE inline -#endif +#include "maybe_emergency_malloc.h" #if (defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)) && !defined(WIN32_OVERRIDE_ALLOCATORS) # define WIN32_DO_PATCHING 1 @@ -153,6 +143,7 @@ #undef small using STL_NAMESPACE::max; +using STL_NAMESPACE::min; using STL_NAMESPACE::numeric_limits; using STL_NAMESPACE::vector; @@ -162,14 +153,6 @@ using STL_NAMESPACE::vector; #include "libc_override.h" #endif -// __THROW is defined in glibc (via ). It means, -// counter-intuitively, "This function will never throw an exception." -// It's an optional optimization tool, but we may need to use it to -// match glibc prototypes. -#ifndef __THROW // I guess we're not on a glibc system -# define __THROW // __THROW is just an optimization, so ok to make it "" -#endif - using tcmalloc::AlignmentForSize; using tcmalloc::kLog; using tcmalloc::kCrash; @@ -183,9 +166,30 @@ using tcmalloc::StackTrace; using tcmalloc::Static; using tcmalloc::ThreadCache; -DECLARE_int64(tcmalloc_sample_parameter); DECLARE_double(tcmalloc_release_rate); +// Those common architectures are known to be safe w.r.t. aliasing function +// with "extra" unused args to function with fewer arguments (e.g. +// tc_delete_nothrow being aliased to tc_delete). +// +// Benefit of aliasing is relatively moderate. It reduces instruction +// cache pressure a bit (not relevant for largely unused +// tc_delete_nothrow, but is potentially relevant for +// tc_delete_aligned (or sized)). It also used to be the case that gcc +// 5+ optimization for merging identical functions kicked in and +// "screwed" one of the otherwise identical functions with extra +// jump. I am not able to reproduce that anymore. +#if !defined(__i386__) && !defined(__x86_64__) && \ + !defined(__ppc__) && !defined(__PPC__) && \ + !defined(__aarch64__) && !defined(__mips__) && !defined(__arm__) +#undef TCMALLOC_NO_ALIASES +#define TCMALLOC_NO_ALIASES +#endif + +#if defined(__GNUC__) && defined(__ELF__) && !defined(TCMALLOC_NO_ALIASES) +#define TC_ALIAS(name) __attribute__((alias(#name))) +#endif + // For windows, the printf we use to report large allocs is // potentially dangerous: it could cause a malloc that would cause an // infinite loop. So by default we set the threshold to a huge number @@ -219,64 +223,97 @@ DEFINE_int64(tcmalloc_large_alloc_report_threshold, // MallocHook::GetCallerStackTrace can function accurately. #ifndef _WIN32 // windows doesn't have attribute_section, so don't bother extern "C" { - void* tc_malloc(size_t size) __THROW + void* tc_malloc(size_t size) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_free(void* ptr) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void tc_free(void* ptr) __THROW + void tc_free_sized(void* ptr, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_realloc(void* ptr, size_t size) __THROW + void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_calloc(size_t nmemb, size_t size) __THROW + void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void tc_cfree(void* ptr) __THROW + void tc_cfree(void* ptr) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_memalign(size_t __alignment, size_t __size) __THROW + void* tc_memalign(size_t __alignment, size_t __size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - int tc_posix_memalign(void** ptr, size_t align, size_t size) __THROW + int tc_posix_memalign(void** ptr, size_t align, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_valloc(size_t __size) __THROW + void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_pvalloc(size_t __size) __THROW + void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void tc_malloc_stats(void) __THROW + void tc_malloc_stats(void) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - int tc_mallopt(int cmd, int value) __THROW + int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); #ifdef HAVE_STRUCT_MALLINFO - struct mallinfo tc_mallinfo(void) __THROW + struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); #endif void* tc_new(size_t size) ATTRIBUTE_SECTION(google_malloc); - void tc_delete(void* p) __THROW + void tc_delete(void* p) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); void* tc_newarray(size_t size) ATTRIBUTE_SECTION(google_malloc); - void tc_deletearray(void* p) __THROW + void tc_deletearray(void* p) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); // And the nothrow variants of these: - void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW + void* tc_new_nothrow(size_t size, const std::nothrow_t&) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); - void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) __THROW + void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); // Surprisingly, standard C++ library implementations use a // nothrow-delete internally. See, eg: // http://www.dinkumware.com/manuals/?manual=compleat&page=new.html - void tc_delete_nothrow(void* ptr, const std::nothrow_t&) __THROW + void tc_delete_nothrow(void* ptr, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_deletearray_nothrow(void* ptr, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + +#if defined(ENABLE_ALIGNED_NEW_DELETE) + + void* tc_new_aligned(size_t size, std::align_val_t al) ATTRIBUTE_SECTION(google_malloc); - void tc_deletearray_nothrow(void* ptr, const std::nothrow_t&) __THROW + void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void* tc_newarray_aligned(size_t size, std::align_val_t al) + ATTRIBUTE_SECTION(google_malloc); + void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); + // And the nothrow variants of these: + void* tc_new_aligned_nothrow(size_t size, std::align_val_t al, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_delete_aligned_nothrow(void* ptr, std::align_val_t al, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + void tc_deletearray_aligned_nothrow(void* ptr, std::align_val_t al, const std::nothrow_t&) PERFTOOLS_NOTHROW + ATTRIBUTE_SECTION(google_malloc); + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + // Some non-standard extensions that we support. // This is equivalent to // OS X: malloc_size() // glibc: malloc_usable_size() // Windows: _msize() - size_t tc_malloc_size(void* p) __THROW + size_t tc_malloc_size(void* p) PERFTOOLS_NOTHROW ATTRIBUTE_SECTION(google_malloc); } // extern "C" #endif // #ifndef _WIN32 @@ -291,7 +328,11 @@ static int tc_new_mode = 0; // See tc_set_new_mode(). // the pagemap cache has a non-zero sizeclass.) This is a cheap (source-editing // required) kind of exception handling for these routines. namespace { -void InvalidFree(void* ptr) { +ATTRIBUTE_NOINLINE void InvalidFree(void* ptr) { + if (tcmalloc::IsEmergencyPtr(ptr)) { + tcmalloc::EmergencyFree(ptr); + return; + } Log(kCrash, __FILE__, __LINE__, "Attempt to free invalid pointer", ptr); } @@ -326,7 +367,7 @@ static void ExtractStats(TCMallocStats* r, uint64_t* class_count, PageHeap::LargeSpanStats* large_spans) { r->central_bytes = 0; r->transfer_bytes = 0; - for (int cl = 0; cl < kNumClasses; ++cl) { + for (int cl = 0; cl < Static::num_size_classes(); ++cl) { const int length = Static::central_cache()[cl].length(); const int tc_length = Static::central_cache()[cl].tc_length(); const size_t cache_overhead = Static::central_cache()[cl].OverheadBytes(); @@ -365,7 +406,7 @@ static double PagesToMiB(uint64_t pages) { // WRITE stats to "out" static void DumpStats(TCMalloc_Printer* out, int level) { TCMallocStats stats; - uint64_t class_count[kNumClasses]; + uint64_t class_count[kClassSizesMax]; PageHeap::SmallSpanStats small; PageHeap::LargeSpanStats large; if (level >= 2) { @@ -432,14 +473,14 @@ static void DumpStats(TCMalloc_Printer* out, int level) { out->printf("transfer cache, and central cache, by size class\n"); out->printf("------------------------------------------------\n"); uint64_t cumulative = 0; - for (int cl = 0; cl < kNumClasses; ++cl) { + for (uint32 cl = 0; cl < Static::num_size_classes(); ++cl) { if (class_count[cl] > 0) { - uint64_t class_bytes = - class_count[cl] * Static::sizemap()->ByteSizeForClass(cl); + size_t cl_size = Static::sizemap()->ByteSizeForClass(cl); + uint64_t class_bytes = class_count[cl] * cl_size; cumulative += class_bytes; out->printf("class %3d [ %8" PRIuS " bytes ] : " "%8" PRIu64 " objs; %5.1f MiB; %5.1f cum MiB\n", - cl, Static::sizemap()->ByteSizeForClass(cl), + cl, cl_size, class_count[cl], class_bytes / MiB, cumulative / MiB); @@ -460,9 +501,9 @@ static void DumpStats(TCMalloc_Printer* out, int level) { out->printf("------------------------------------------------\n"); uint64_t total_normal = 0; uint64_t total_returned = 0; - for (int s = 0; s < kMaxPages; s++) { - const int n_length = small.normal_length[s]; - const int r_length = small.returned_length[s]; + for (int s = 1; s <= kMaxPages; s++) { + const int n_length = small.normal_length[s - 1]; + const int r_length = small.returned_length[s - 1]; if (n_length + r_length > 0) { uint64_t n_pages = s * n_length; uint64_t r_pages = s * r_length; @@ -481,8 +522,9 @@ static void DumpStats(TCMalloc_Printer* out, int level) { total_normal += large.normal_pages; total_returned += large.returned_pages; - out->printf(">255 large * %6u spans ~ %6.1f MiB; %6.1f MiB cum" + out->printf(">%-5u large * %6u spans ~ %6.1f MiB; %6.1f MiB cum" "; unmapped: %6.1f MiB; %6.1f MiB cum\n", + static_cast(kMaxPages), static_cast(large.spans), PagesToMiB(large.normal_pages + large.returned_pages), PagesToMiB(total_normal + total_returned), @@ -591,7 +633,7 @@ class TCMallocImplementation : public MallocExtension { : extra_bytes_released_(0) { } - void GetStats(char* buffer, int buffer_length) override { + virtual void GetStats(char* buffer, int buffer_length) { ASSERT(buffer_length > 0); TCMalloc_Printer printer(buffer, buffer_length); @@ -604,7 +646,7 @@ class TCMallocImplementation : public MallocExtension { } // We may print an extra, tcmalloc-specific warning message here. - void GetHeapSample(MallocExtensionWriter* writer) override { + virtual void GetHeapSample(MallocExtensionWriter* writer) { if (FLAGS_tcmalloc_sample_parameter == 0) { const char* const kWarningMsg = "%warn\n" @@ -619,7 +661,7 @@ class TCMallocImplementation : public MallocExtension { MallocExtension::GetHeapSample(writer); } - void** ReadStackTraces(int* sample_period) override { + virtual void** ReadStackTraces(int* sample_period) { tcmalloc::StackTraceTable table; { SpinLockHolder h(Static::pageheap_lock()); @@ -632,15 +674,26 @@ class TCMallocImplementation : public MallocExtension { return table.ReadStackTracesAndClear(); // grabs and releases pageheap_lock } - void** ReadHeapGrowthStackTraces() override { + virtual void** ReadHeapGrowthStackTraces() { return DumpHeapGrowthStackTraces(); } - void Ranges(void* arg, RangeFunction func) override { + virtual size_t GetThreadCacheSize() { + ThreadCache* tc = ThreadCache::GetCacheIfPresent(); + if (!tc) + return 0; + return tc->Size(); + } + + virtual void MarkThreadTemporarilyIdle() { + ThreadCache::BecomeTemporarilyIdle(); + } + + virtual void Ranges(void* arg, RangeFunction func) { IterateOverRanges(arg, func); } - bool GetNumericProperty(const char* name, size_t* value) override { + virtual bool GetNumericProperty(const char* name, size_t* value) { ASSERT(name != NULL); if (strcmp(name, "generic.current_allocated_bytes") == 0) { @@ -662,6 +715,14 @@ class TCMallocImplementation : public MallocExtension { return true; } + if (strcmp(name, "generic.total_physical_bytes") == 0) { + TCMallocStats stats; + ExtractStats(&stats, NULL, NULL, NULL); + *value = stats.pageheap.system_bytes + stats.metadata_bytes - + stats.pageheap.unmapped_bytes; + return true; + } + if (strcmp(name, "tcmalloc.slack_bytes") == 0) { // Kept for backwards compatibility. Now defined externally as: // pageheap_free_bytes + pageheap_unmapped_bytes. @@ -704,6 +765,54 @@ class TCMallocImplementation : public MallocExtension { return true; } + if (strcmp(name, "tcmalloc.pageheap_committed_bytes") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().committed_bytes; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_scavenge_count") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().scavenge_count; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_commit_count") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().commit_count; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_total_commit_bytes") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().total_commit_bytes; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_decommit_count") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().decommit_count; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_total_decommit_bytes") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().total_decommit_bytes; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_reserve_count") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().reserve_count; + return true; + } + + if (strcmp(name, "tcmalloc.pageheap_total_reserve_bytes") == 0) { + SpinLockHolder l(Static::pageheap_lock()); + *value = Static::pageheap()->stats().total_reserve_bytes; + return true; + } + if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) { SpinLockHolder l(Static::pageheap_lock()); *value = ThreadCache::overall_thread_cache_size(); @@ -718,14 +827,20 @@ class TCMallocImplementation : public MallocExtension { } if (strcmp(name, "tcmalloc.aggressive_memory_decommit") == 0) { + SpinLockHolder l(Static::pageheap_lock()); *value = size_t(Static::pageheap()->GetAggressiveDecommit()); return true; } + if (strcmp(name, "tcmalloc.min_system_alloc") == 0) { + *value = size_t(Static::pageheap()->GetMinSystemAlloc()); + return true; + } + return false; } - bool SetNumericProperty(const char* name, size_t value) override { + virtual bool SetNumericProperty(const char* name, size_t value) { ASSERT(name != NULL); if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) { @@ -735,30 +850,40 @@ class TCMallocImplementation : public MallocExtension { } if (strcmp(name, "tcmalloc.aggressive_memory_decommit") == 0) { + SpinLockHolder l(Static::pageheap_lock()); Static::pageheap()->SetAggressiveDecommit(value != 0); return true; } + if (strcmp(name, "tcmalloc.min_system_alloc") == 0) { + if( ((value >> kPageShift) << kPageShift) != value ) { + printf("WARNING: Minimum system allocation size truncated;\n" + "memory is managed in blocks of %d bytes\n", (1 << kPageShift)); + } + Static::pageheap()->SetMinSystemAlloc(value >> kPageShift); + return true; + } + return false; } - void MarkThreadIdle() override { + virtual void MarkThreadIdle() { ThreadCache::BecomeIdle(); } - void MarkThreadBusy() override; // Implemented below + virtual void MarkThreadBusy(); // Implemented below virtual SysAllocator* GetSystemAllocator() { SpinLockHolder h(Static::pageheap_lock()); - return sys_alloc; + return tcmalloc_sys_alloc; } - void SetSystemAllocator(SysAllocator* alloc) override { + virtual void SetSystemAllocator(SysAllocator* alloc) { SpinLockHolder h(Static::pageheap_lock()); - sys_alloc = alloc; + tcmalloc_sys_alloc = alloc; } - void ReleaseToSystem(size_t num_bytes) override { + virtual void ReleaseToSystem(size_t num_bytes) { SpinLockHolder h(Static::pageheap_lock()); if (num_bytes <= extra_bytes_released_) { // We released too much on a prior call, so don't release any @@ -783,33 +908,24 @@ class TCMallocImplementation : public MallocExtension { } } - void SetMemoryReleaseRate(double rate) override { + virtual void SetMemoryReleaseRate(double rate) { FLAGS_tcmalloc_release_rate = rate; } - double GetMemoryReleaseRate() override { + virtual double GetMemoryReleaseRate() { return FLAGS_tcmalloc_release_rate; } - - size_t GetEstimatedAllocatedSize(size_t size) override { - if (size <= kMaxSize) { - const size_t cl = Static::sizemap()->SizeClass(size); - const size_t alloc_size = Static::sizemap()->ByteSizeForClass(cl); - return alloc_size; - } else { - return tcmalloc::pages(size) << kPageShift; - } - } + virtual size_t GetEstimatedAllocatedSize(size_t size); // This just calls GetSizeWithCallback, but because that's in an // unnamed namespace, we need to move the definition below it in the // file. - size_t GetAllocatedSize(const void* ptr) override; + virtual size_t GetAllocatedSize(const void* ptr); // This duplicates some of the logic in GetSizeWithCallback, but is // faster. This is important on OS X, where this function is called // on every allocation operation. - Ownership GetOwnership(const void* ptr) override { + virtual Ownership GetOwnership(const void* ptr) { const PageID p = reinterpret_cast(ptr) >> kPageShift; // The rest of tcmalloc assumes that all allocated pointers use at // most kAddressBits bits. If ptr doesn't, then it definitely @@ -817,28 +933,28 @@ class TCMallocImplementation : public MallocExtension { if ((p >> (kAddressBits - kPageShift)) > 0) { return kNotOwned; } - size_t cl = Static::pageheap()->GetSizeClassIfCached(p); - if (cl != 0) { + uint32 cl; + if (Static::pageheap()->TryGetSizeClass(p, &cl)) { return kOwned; } const Span *span = Static::pageheap()->GetDescriptor(p); return span ? kOwned : kNotOwned; } - void GetFreeListSizes(vector* v) override { - static const char* kCentralCacheType = "tcmalloc.central"; - static const char* kTransferCacheType = "tcmalloc.transfer"; - static const char* kThreadCacheType = "tcmalloc.thread"; - static const char* kPageHeapType = "tcmalloc.page"; - static const char* kPageHeapUnmappedType = "tcmalloc.page_unmapped"; - static const char* kLargeSpanType = "tcmalloc.large"; - static const char* kLargeUnmappedSpanType = "tcmalloc.large_unmapped"; + virtual void GetFreeListSizes(vector* v) { + static const char kCentralCacheType[] = "tcmalloc.central"; + static const char kTransferCacheType[] = "tcmalloc.transfer"; + static const char kThreadCacheType[] = "tcmalloc.thread"; + static const char kPageHeapType[] = "tcmalloc.page"; + static const char kPageHeapUnmappedType[] = "tcmalloc.page_unmapped"; + static const char kLargeSpanType[] = "tcmalloc.large"; + static const char kLargeUnmappedSpanType[] = "tcmalloc.large_unmapped"; v->clear(); // central class information int64 prev_class_size = 0; - for (int cl = 1; cl < kNumClasses; ++cl) { + for (int cl = 1; cl < Static::num_size_classes(); ++cl) { size_t class_size = Static::sizemap()->ByteSizeForClass(cl); MallocExtension::FreeListInfo i; i.min_object_size = prev_class_size + 1; @@ -858,7 +974,7 @@ class TCMallocImplementation : public MallocExtension { } // Add stats from per-thread heaps - uint64_t class_count[kNumClasses]; + uint64_t class_count[kClassSizesMax]; memset(class_count, 0, sizeof(class_count)); { SpinLockHolder h(Static::pageheap_lock()); @@ -867,7 +983,7 @@ class TCMallocImplementation : public MallocExtension { } prev_class_size = 0; - for (int cl = 1; cl < kNumClasses; ++cl) { + for (int cl = 1; cl < Static::num_size_classes(); ++cl) { MallocExtension::FreeListInfo i; i.min_object_size = prev_class_size + 1; i.max_object_size = Static::sizemap()->ByteSizeForClass(cl); @@ -875,6 +991,8 @@ class TCMallocImplementation : public MallocExtension { class_count[cl] * Static::sizemap()->ByteSizeForClass(cl); i.type = kThreadCacheType; v->push_back(i); + + prev_class_size = Static::sizemap()->ByteSizeForClass(cl); } // append page heap info @@ -900,22 +1018,102 @@ class TCMallocImplementation : public MallocExtension { v->push_back(span_info); // small spans - for (int s = 1; s < kMaxPages; s++) { + for (int s = 1; s <= kMaxPages; s++) { MallocExtension::FreeListInfo i; i.max_object_size = (s << kPageShift); i.min_object_size = ((s - 1) << kPageShift); i.type = kPageHeapType; - i.total_bytes_free = (s << kPageShift) * small.normal_length[s]; + i.total_bytes_free = (s << kPageShift) * small.normal_length[s - 1]; v->push_back(i); i.type = kPageHeapUnmappedType; - i.total_bytes_free = (s << kPageShift) * small.returned_length[s]; + i.total_bytes_free = (s << kPageShift) * small.returned_length[s - 1]; v->push_back(i); } } }; +static inline ATTRIBUTE_ALWAYS_INLINE +size_t align_size_up(size_t size, size_t align) { + ASSERT(align <= kPageSize); + size_t new_size = (size + align - 1) & ~(align - 1); + if (PREDICT_FALSE(new_size == 0)) { + // Note, new_size == 0 catches both integer overflow and size + // being 0. + if (size == 0) { + new_size = align; + } else { + new_size = size; + } + } + return new_size; +} + +// Puts in *cl size class that is suitable for allocation of size bytes with +// align alignment. Returns true if such size class exists and false otherwise. +static bool size_class_with_alignment(size_t size, size_t align, uint32_t* cl) { + if (PREDICT_FALSE(align > kPageSize)) { + return false; + } + size = align_size_up(size, align); + if (PREDICT_FALSE(!Static::sizemap()->GetSizeClass(size, cl))) { + return false; + } + ASSERT((Static::sizemap()->class_to_size(*cl) & (align - 1)) == 0); + return true; +} + +// nallocx slow path. Moved to a separate function because +// ThreadCache::InitModule is not inlined which would cause nallocx to +// become non-leaf function with stack frame and stack spills. +static ATTRIBUTE_NOINLINE size_t nallocx_slow(size_t size, int flags) { + if (PREDICT_FALSE(!Static::IsInited())) ThreadCache::InitModule(); + + size_t align = static_cast(1ull << (flags & 0x3f)); + uint32 cl; + bool ok = size_class_with_alignment(size, align, &cl); + if (ok) { + return Static::sizemap()->ByteSizeForClass(cl); + } else { + return tcmalloc::pages(size) << kPageShift; + } +} + +// The nallocx function allocates no memory, but it performs the same size +// computation as the malloc function, and returns the real size of the +// allocation that would result from the equivalent malloc function call. +// nallocx is a malloc extension originally implemented by jemalloc: +// http://www.unix.com/man-page/freebsd/3/nallocx/ +extern "C" PERFTOOLS_DLL_DECL +size_t tc_nallocx(size_t size, int flags) { + if (PREDICT_FALSE(flags != 0)) { + return nallocx_slow(size, flags); + } + uint32 cl; + // size class 0 is only possible if malloc is not yet initialized + if (Static::sizemap()->GetSizeClass(size, &cl) && cl != 0) { + return Static::sizemap()->ByteSizeForClass(cl); + } else { + return nallocx_slow(size, 0); + } +} + +extern "C" PERFTOOLS_DLL_DECL +size_t nallocx(size_t size, int flags) +#ifdef TC_ALIAS + TC_ALIAS(tc_nallocx); +#else +{ + return nallocx_slow(size, flags); +} +#endif + + +size_t TCMallocImplementation::GetEstimatedAllocatedSize(size_t size) { + return tc_nallocx(size, 0); +} + // The constructor allocates an object to ensure that initialization // runs before main(), and therefore we do not have a chance to become // multi-threaded before initialization. We also create the TSD key @@ -930,19 +1128,13 @@ class TCMallocImplementation : public MallocExtension { // The destructor prints stats when the program exits. static int tcmallocguard_refcount = 0; // no lock needed: runs before main() TCMallocGuard::TCMallocGuard() { - if (tcmallocguard_refcount++ == 0) { - // ReplaceSystemAlloc(); // defined in libc_override_*.h #if 0 - std::cerr << "allocating the first\n"; + ReplaceSystemAlloc(); // defined in libc_override_*.h #endif tc_free(tc_malloc(1)); ThreadCache::InitTSD(); -#if 0 - std::cerr << "allocating the second\n"; -#endif tc_free(tc_malloc(1)); - // Either we, or debugallocation.cc, or valgrind will control memory // management. We register our extension if we're the winner. #ifdef TCMALLOC_USING_DEBUGALLOCATION @@ -972,29 +1164,7 @@ TCMallocGuard::~TCMallocGuard() { } } #ifndef WIN32_OVERRIDE_ALLOCATORS -TCMallocGuard module_enter_exit_hook; -#if 0 -extern "C" PERFTOOLS_DLL_DECL -void tc_register () { - std::cerr << "allocating the first\n"; - tc_free(tc_malloc(1)); - std::cerr << "initing TSD\n"; - ThreadCache::InitTSD(); - std::cerr << "allocating the second\n"; - tc_free(tc_malloc(1)); - MallocExtension::Register(new TCMallocImplementation); -} -extern "C" PERFTOOLS_DLL_DECL -void tc_unregister () { - MallocExtension* ptr = MallocExtension::instance (); - MallocExtension::Register (0); - MallocExtension::Register (0); - delete ptr; - TCMalloc_ResetSystemAlloc (); - // ThreadCache::DeInitTSD(); - // ThreadCache::DeInitModule(); -} -#endif /* 0 */ +static TCMallocGuard module_enter_exit_hook; #endif //------------------------------------------------------------------- @@ -1003,23 +1173,26 @@ void tc_unregister () { static inline bool CheckCachedSizeClass(void *ptr) { PageID p = reinterpret_cast(ptr) >> kPageShift; - size_t cached_value = Static::pageheap()->GetSizeClassIfCached(p); - return cached_value == 0 || - cached_value == Static::pageheap()->GetDescriptor(p)->sizeclass; + uint32 cached_value; + if (!Static::pageheap()->TryGetSizeClass(p, &cached_value)) { + return true; + } + return cached_value == Static::pageheap()->GetDescriptor(p)->sizeclass; } -static inline void* CheckedMallocResult(void *result) { +static inline ATTRIBUTE_ALWAYS_INLINE void* CheckedMallocResult(void *result) { ASSERT(result == NULL || CheckCachedSizeClass(result)); return result; } -static inline void* SpanToMallocResult(Span *span) { - Static::pageheap()->CacheSizeClass(span->start, 0); +static inline ATTRIBUTE_ALWAYS_INLINE void* SpanToMallocResult(Span *span) { + Static::pageheap()->InvalidateCachedSizeClass(span->start); return CheckedMallocResult(reinterpret_cast(span->start << kPageShift)); } static void* DoSampledAllocation(size_t size) { +#ifndef NO_TCMALLOC_SAMPLES // Grab the stack trace outside the heap lock StackTrace tmp; tmp.depth = GetStackTrace(tmp.stack, tcmalloc::kMaxStackDepth, 1); @@ -1028,13 +1201,13 @@ static void* DoSampledAllocation(size_t size) { SpinLockHolder h(Static::pageheap_lock()); // Allocate span Span *span = Static::pageheap()->New(tcmalloc::pages(size == 0 ? 1 : size)); - if (UNLIKELY(span == NULL)) { + if (PREDICT_FALSE(span == NULL)) { return NULL; } // Allocate stack trace StackTrace *stack = Static::stacktrace_allocator()->New(); - if (UNLIKELY(stack == NULL)) { + if (PREDICT_FALSE(stack == NULL)) { // Sampling failed because of lack of memory return span; } @@ -1044,6 +1217,9 @@ static void* DoSampledAllocation(size_t size) { tcmalloc::DLL_Prepend(Static::sampled_objects(), span); return SpanToMallocResult(span); +#else + abort(); +#endif } namespace { @@ -1056,6 +1232,16 @@ void* handle_oom(malloc_fn retry_fn, void* retry_arg, bool from_operator, bool nothrow) { + // we hit out of memory condition, usually if it happens we've + // called sbrk or mmap and failed, and thus errno is set. But there + // is support for setting up custom system allocator or setting up + // page heap size limit, in which cases errno may remain + // untouched. + // + // So we set errno here. C++ operator new doesn't require ENOMEM to + // be set, but doesn't forbid it too (and often C++ oom does happen + // with ENOMEM set). + errno = ENOMEM; if (!from_operator && !tc_new_mode) { // we're out of memory in C library function (malloc etc) and no // "new mode" forced on us. Just return NULL @@ -1114,9 +1300,11 @@ void* handle_oom(malloc_fn retry_fn, // Copy of FLAGS_tcmalloc_large_alloc_report_threshold with // automatic increases factored in. +#ifdef ENABLE_LARGE_ALLOC_REPORT static int64_t large_alloc_threshold = (kPageSize > FLAGS_tcmalloc_large_alloc_report_threshold ? kPageSize : FLAGS_tcmalloc_large_alloc_report_threshold); +#endif static void ReportLargeAlloc(Length num_pages, void* result) { StackTrace stack; @@ -1135,36 +1323,9 @@ static void ReportLargeAlloc(Length num_pages, void* result) { write(STDERR_FILENO, buffer, strlen(buffer)); } -void* do_memalign(size_t align, size_t size); - -struct retry_memaligh_data { - size_t align; - size_t size; -}; - -static void *retry_do_memalign(void *arg) { - retry_memaligh_data *data = static_cast(arg); - return do_memalign(data->align, data->size); -} - -static void *maybe_do_cpp_memalign_slow(size_t align, size_t size) { - retry_memaligh_data data; - data.align = align; - data.size = size; - return handle_oom(retry_do_memalign, &data, - false, true); -} - -inline void* do_memalign_or_cpp_memalign(size_t align, size_t size) { - void *rv = do_memalign(align, size); - if (LIKELY(rv != NULL)) { - return rv; - } - return maybe_do_cpp_memalign_slow(align, size); -} - // Must be called with the page lock held. inline bool should_report_large(Length num_pages) { +#ifdef ENABLE_LARGE_ALLOC_REPORT const int64 threshold = large_alloc_threshold; if (threshold > 0 && num_pages >= (threshold >> kPageShift)) { // Increase the threshold by 1/8 every time we generate a report. @@ -1173,18 +1334,24 @@ inline bool should_report_large(Length num_pages) { ? threshold + threshold/8 : 8ll<<30); return true; } +#endif return false; } // Helper for do_malloc(). -inline void* do_malloc_pages(ThreadCache* heap, size_t size) { +static void* do_malloc_pages(ThreadCache* heap, size_t size) { void* result; bool report_large; Length num_pages = tcmalloc::pages(size); - size = num_pages << kPageShift; - if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) { + // NOTE: we're passing original size here as opposed to rounded-up + // size as we do in do_malloc_small. The difference is small here + // (at most 4k out of at least 256k). And not rounding up saves us + // from possibility of overflow, which rounding up could produce. + // + // See https://github.com/gperftools/gperftools/issues/723 + if (heap->SampleAllocation(size)) { result = DoSampledAllocation(size); SpinLockHolder h(Static::pageheap_lock()); @@ -1192,7 +1359,7 @@ inline void* do_malloc_pages(ThreadCache* heap, size_t size) { } else { SpinLockHolder h(Static::pageheap_lock()); Span* span = Static::pageheap()->New(num_pages); - result = (UNLIKELY(span == NULL) ? NULL : SpanToMallocResult(span)); + result = (PREDICT_FALSE(span == NULL) ? NULL : SpanToMallocResult(span)); report_large = should_report_large(num_pages); } @@ -1202,53 +1369,57 @@ inline void* do_malloc_pages(ThreadCache* heap, size_t size) { return result; } -ALWAYS_INLINE void* do_malloc_small(ThreadCache* heap, size_t size) { +static void *nop_oom_handler(size_t size) { + return NULL; +} + +ATTRIBUTE_ALWAYS_INLINE inline void* do_malloc(size_t size) { + if (PREDICT_FALSE(ThreadCache::IsUseEmergencyMalloc())) { + return tcmalloc::EmergencyMalloc(size); + } + + // note: it will force initialization of malloc if necessary + ThreadCache* cache = ThreadCache::GetCache(); + uint32 cl; + ASSERT(Static::IsInited()); - ASSERT(heap != NULL); - size_t cl = Static::sizemap()->SizeClass(size); - size = Static::sizemap()->class_to_size(cl); + ASSERT(cache != NULL); - if (UNLIKELY(FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) { - return DoSampledAllocation(size); - } else { - // The common case, and also the simplest. This just pops the - // size-appropriate freelist, after replenishing it if it's empty. - return CheckedMallocResult(heap->Allocate(size, cl)); + if (PREDICT_FALSE(!Static::sizemap()->GetSizeClass(size, &cl))) { + return do_malloc_pages(cache, size); } -} -ALWAYS_INLINE void* do_malloc(size_t size) { - if (ThreadCache::have_tls && - LIKELY(size < ThreadCache::MinSizeForSlowPath())) { - return do_malloc_small(ThreadCache::GetCacheWhichMustBePresent(), size); - } else if (size <= kMaxSize) { - return do_malloc_small(ThreadCache::GetCache(), size); - } else { - return do_malloc_pages(ThreadCache::GetCache(), size); + size_t allocated_size = Static::sizemap()->class_to_size(cl); + if (PREDICT_FALSE(cache->SampleAllocation(allocated_size))) { + return DoSampledAllocation(size); } + + // The common case, and also the simplest. This just pops the + // size-appropriate freelist, after replenishing it if it's empty. + return CheckedMallocResult(cache->Allocate(allocated_size, cl, nop_oom_handler)); } static void *retry_malloc(void* size) { return do_malloc(reinterpret_cast(size)); } -ALWAYS_INLINE void* do_malloc_or_cpp_alloc(size_t size) { +ATTRIBUTE_ALWAYS_INLINE inline void* do_malloc_or_cpp_alloc(size_t size) { void *rv = do_malloc(size); - if (LIKELY(rv != NULL)) { + if (PREDICT_TRUE(rv != NULL)) { return rv; } return handle_oom(retry_malloc, reinterpret_cast(size), false, true); } -ALWAYS_INLINE void* do_calloc(size_t n, size_t elem_size) { +ATTRIBUTE_ALWAYS_INLINE inline void* do_calloc(size_t n, size_t elem_size) { // Overflow check const size_t size = n * elem_size; if (elem_size != 0 && size / elem_size != n) return NULL; void* result = do_malloc_or_cpp_alloc(size); if (result != NULL) { - memset(result, 0, size); + memset(result, 0, tc_nallocx(size, 0)); } return result; } @@ -1260,74 +1431,15 @@ inline void free_null_or_invalid(void* ptr, void (*invalid_free_fn)(void*)) { } } -// Helper for do_free_with_callback(), below. Inputs: -// ptr is object to be freed -// invalid_free_fn is a function that gets invoked on certain "bad frees" -// heap is the ThreadCache for this thread, or NULL if it isn't known -// heap_must_be_valid is whether heap is known to be non-NULL -// -// This function may only be used after Static::IsInited() is true. -// -// We can usually detect the case where ptr is not pointing to a page that -// tcmalloc is using, and in those cases we invoke invalid_free_fn. -// -// To maximize speed in the common case, we usually get here with -// heap_must_be_valid being a manifest constant equal to true. -ALWAYS_INLINE void do_free_helper(void* ptr, - void (*invalid_free_fn)(void*), - ThreadCache* heap, - bool heap_must_be_valid) { - ASSERT((Static::IsInited() && heap != NULL) || !heap_must_be_valid); - if (!heap_must_be_valid && !Static::IsInited()) { - // We called free() before malloc(). This can occur if the - // (system) malloc() is called before tcmalloc is loaded, and then - // free() is called after tcmalloc is loaded (and tc_free has - // replaced free), but before the global constructor has run that - // sets up the tcmalloc data structures. - free_null_or_invalid(ptr, invalid_free_fn); - return; - } - Span* span = NULL; - const PageID p = reinterpret_cast(ptr) >> kPageShift; - size_t cl = Static::pageheap()->GetSizeClassIfCached(p); - if (UNLIKELY(cl == 0)) { - span = Static::pageheap()->GetDescriptor(p); - if (UNLIKELY(!span)) { - // span can be NULL because the pointer passed in is NULL or invalid - // (not something returned by malloc or friends), or because the - // pointer was allocated with some other allocator besides - // tcmalloc. The latter can happen if tcmalloc is linked in via - // a dynamic library, but is not listed last on the link line. - // In that case, libraries after it on the link line will - // allocate with libc malloc, but free with tcmalloc's free. - free_null_or_invalid(ptr, invalid_free_fn); - return; - } - cl = span->sizeclass; - Static::pageheap()->CacheSizeClass(p, cl); - } - ASSERT(ptr != NULL); - if (LIKELY(cl != 0)) { - ASSERT(!Static::pageheap()->GetDescriptor(p)->sample); - if (heap_must_be_valid || heap != NULL) { - heap->Deallocate(ptr, cl); - } else { - // Delete directly into central cache - tcmalloc::SLL_SetNext(ptr, NULL); - Static::central_cache()[cl].InsertRange(ptr, ptr, 1); - } - } else { - SpinLockHolder h(Static::pageheap_lock()); - ASSERT(reinterpret_cast(ptr) % kPageSize == 0); - ASSERT(span != NULL && span->start == p); - if (span->sample) { - StackTrace* st = reinterpret_cast(span->objects); - tcmalloc::DLL_Remove(span); - Static::stacktrace_allocator()->Delete(st); - span->objects = NULL; - } - Static::pageheap()->Delete(span); +static ATTRIBUTE_NOINLINE void do_free_pages(Span* span, void* ptr) { + SpinLockHolder h(Static::pageheap_lock()); + if (span->sample) { + StackTrace* st = reinterpret_cast(span->objects); + tcmalloc::DLL_Remove(span); + Static::stacktrace_allocator()->Delete(st); + span->objects = NULL; } + Static::pageheap()->Delete(span); } // Helper for the object deletion (free, delete, etc.). Inputs: @@ -1336,21 +1448,77 @@ ALWAYS_INLINE void do_free_helper(void* ptr, // // We can usually detect the case where ptr is not pointing to a page that // tcmalloc is using, and in those cases we invoke invalid_free_fn. -ALWAYS_INLINE void do_free_with_callback(void* ptr, - void (*invalid_free_fn)(void*)) { - ThreadCache* heap = NULL; - if (LIKELY(ThreadCache::IsFastPathAllowed())) { - heap = ThreadCache::GetCacheWhichMustBePresent(); - do_free_helper(ptr, invalid_free_fn, heap, true); - } else { - heap = ThreadCache::GetCacheIfPresent(); - do_free_helper(ptr, invalid_free_fn, heap, false); +ATTRIBUTE_ALWAYS_INLINE inline +void do_free_with_callback(void* ptr, + void (*invalid_free_fn)(void*), + bool use_hint, size_t size_hint) { + ThreadCache* heap = ThreadCache::GetCacheIfPresent(); + + const PageID p = reinterpret_cast(ptr) >> kPageShift; + uint32 cl; + +#ifndef NO_TCMALLOC_SAMPLES + // we only pass size hint when ptr is not page aligned. Which + // implies that it must be very small object. + ASSERT(!use_hint || size_hint < kPageSize); +#endif + + if (!use_hint || PREDICT_FALSE(!Static::sizemap()->GetSizeClass(size_hint, &cl))) { + // if we're in sized delete, but size is too large, no need to + // probe size cache + bool cache_hit = !use_hint && Static::pageheap()->TryGetSizeClass(p, &cl); + if (PREDICT_FALSE(!cache_hit)) { + Span* span = Static::pageheap()->GetDescriptor(p); + if (PREDICT_FALSE(!span)) { + // span can be NULL because the pointer passed in is NULL or invalid + // (not something returned by malloc or friends), or because the + // pointer was allocated with some other allocator besides + // tcmalloc. The latter can happen if tcmalloc is linked in via + // a dynamic library, but is not listed last on the link line. + // In that case, libraries after it on the link line will + // allocate with libc malloc, but free with tcmalloc's free. + free_null_or_invalid(ptr, invalid_free_fn); + return; + } + cl = span->sizeclass; + if (PREDICT_FALSE(cl == 0)) { + ASSERT(reinterpret_cast(ptr) % kPageSize == 0); + ASSERT(span != NULL && span->start == p); + do_free_pages(span, ptr); + return; + } + if (!use_hint) { + Static::pageheap()->SetCachedSizeClass(p, cl); + } + } } + + if (PREDICT_TRUE(heap != NULL)) { + ASSERT(Static::IsInited()); + // If we've hit initialized thread cache, so we're done. + heap->Deallocate(ptr, cl); + return; + } + + if (PREDICT_FALSE(!Static::IsInited())) { + // if free was called very early we've could have missed the case + // of invalid or nullptr free. I.e. because probing size classes + // cache could return bogus result (cl = 0 as of this + // writing). But since there is no way we could be dealing with + // ptr we've allocated, since successfull malloc implies IsInited, + // we can just call "invalid free" handling code. + free_null_or_invalid(ptr, invalid_free_fn); + return; + } + + // Otherwise, delete directly into central cache + tcmalloc::SLL_SetNext(ptr, NULL); + Static::central_cache()[cl].InsertRange(ptr, ptr, 1); } // The default "do_free" that uses the default callback. -ALWAYS_INLINE void do_free(void* ptr) { - return do_free_with_callback(ptr, &InvalidFree); +ATTRIBUTE_ALWAYS_INLINE inline void do_free(void* ptr) { + return do_free_with_callback(ptr, &InvalidFree, false, 0); } // NOTE: some logic here is duplicated in GetOwnership (above), for @@ -1360,25 +1528,31 @@ inline size_t GetSizeWithCallback(const void* ptr, if (ptr == NULL) return 0; const PageID p = reinterpret_cast(ptr) >> kPageShift; - size_t cl = Static::pageheap()->GetSizeClassIfCached(p); - if (cl != 0) { + uint32 cl; + if (Static::pageheap()->TryGetSizeClass(p, &cl)) { return Static::sizemap()->ByteSizeForClass(cl); - } else { - const Span *span = Static::pageheap()->GetDescriptor(p); - if (UNLIKELY(span == NULL)) { // means we do not own this memory - return (*invalid_getsize_fn)(ptr); - } else if (span->sizeclass != 0) { - Static::pageheap()->CacheSizeClass(p, span->sizeclass); - return Static::sizemap()->ByteSizeForClass(span->sizeclass); - } else { - return span->length << kPageShift; - } } + + const Span *span = Static::pageheap()->GetDescriptor(p); + if (PREDICT_FALSE(span == NULL)) { // means we do not own this memory + return (*invalid_getsize_fn)(ptr); + } + + if (span->sizeclass != 0) { + return Static::sizemap()->ByteSizeForClass(span->sizeclass); + } + + if (span->sample) { + size_t orig_size = reinterpret_cast(span->objects)->size; + return tc_nallocx(orig_size, 0); + } + + return span->length << kPageShift; } // This lets you call back to a given function pointer if ptr is invalid. // It is used primarily by windows code which wants a specialized callback. -ALWAYS_INLINE void* do_realloc_with_callback( +ATTRIBUTE_ALWAYS_INLINE inline void* do_realloc_with_callback( void* old_ptr, size_t new_size, void (*invalid_free_fn)(void*), size_t (*invalid_get_size_fn)(const void*)) { @@ -1391,7 +1565,9 @@ ALWAYS_INLINE void* do_realloc_with_callback( // . If we need to grow, grow to max(new_size, old_size * 1.X) // . Don't shrink unless new_size < old_size * 0.Y // X and Y trade-off time for wasted space. For now we do 1.25 and 0.5. - const size_t lower_bound_to_grow = old_size + old_size / 4ul; + const size_t min_growth = min(old_size / 4, + (std::numeric_limits::max)() - old_size); // Avoid overflow. + const size_t lower_bound_to_grow = old_size + min_growth; const size_t upper_bound_to_shrink = old_size / 2ul; if ((new_size > old_size) || (new_size < upper_bound_to_shrink)) { // Need to reallocate. @@ -1404,7 +1580,7 @@ ALWAYS_INLINE void* do_realloc_with_callback( // Either new_size is not a tiny increment, or last do_malloc failed. new_ptr = do_malloc_or_cpp_alloc(new_size); } - if (UNLIKELY(new_ptr == NULL)) { + if (PREDICT_FALSE(new_ptr == NULL)) { return NULL; } MallocHook::InvokeNewHook(new_ptr, new_size); @@ -1413,7 +1589,7 @@ ALWAYS_INLINE void* do_realloc_with_callback( // We could use a variant of do_free() that leverages the fact // that we already know the sizeclass of old_ptr. The benefit // would be small, so don't bother. - do_free_with_callback(old_ptr, invalid_free_fn); + do_free_with_callback(old_ptr, invalid_free_fn, false, 0); return new_ptr; } else { // We still need to call hooks to report the updated size: @@ -1423,69 +1599,29 @@ ALWAYS_INLINE void* do_realloc_with_callback( } } -ALWAYS_INLINE void* do_realloc(void* old_ptr, size_t new_size) { +ATTRIBUTE_ALWAYS_INLINE inline void* do_realloc(void* old_ptr, size_t new_size) { return do_realloc_with_callback(old_ptr, new_size, &InvalidFree, &InvalidGetSizeForRealloc); } -// For use by exported routines below that want specific alignments -// -// Note: this code can be slow for alignments > 16, and can -// significantly fragment memory. The expectation is that -// memalign/posix_memalign/valloc/pvalloc will not be invoked very -// often. This requirement simplifies our implementation and allows -// us to tune for expected allocation patterns. -void* do_memalign(size_t align, size_t size) { +static ATTRIBUTE_ALWAYS_INLINE inline +void* do_memalign_pages(size_t align, size_t size) { ASSERT((align & (align - 1)) == 0); - ASSERT(align > 0); + ASSERT(align > kPageSize); if (size + align < size) return NULL; // Overflow - // Fall back to malloc if we would already align this memory access properly. - if (align <= AlignmentForSize(size)) { - void* p = do_malloc(size); - ASSERT((reinterpret_cast(p) % align) == 0); - return p; - } - - if (UNLIKELY(Static::pageheap() == NULL)) ThreadCache::InitModule(); + if (PREDICT_FALSE(Static::pageheap() == NULL)) ThreadCache::InitModule(); // Allocate at least one byte to avoid boundary conditions below if (size == 0) size = 1; - if (size <= kMaxSize && align < kPageSize) { - // Search through acceptable size classes looking for one with - // enough alignment. This depends on the fact that - // InitSizeClasses() currently produces several size classes that - // are aligned at powers of two. We will waste time and space if - // we miss in the size class array, but that is deemed acceptable - // since memalign() should be used rarely. - int cl = Static::sizemap()->SizeClass(size); - while (cl < kNumClasses && - ((Static::sizemap()->class_to_size(cl) & (align - 1)) != 0)) { - cl++; - } - if (cl < kNumClasses) { - ThreadCache* heap = ThreadCache::GetCache(); - size = Static::sizemap()->class_to_size(cl); - return CheckedMallocResult(heap->Allocate(size, cl)); - } - } - // We will allocate directly from the page heap SpinLockHolder h(Static::pageheap_lock()); - if (align <= kPageSize) { - // Any page-level allocation will be fine - // TODO: We could put the rest of this page in the appropriate - // TODO: cache but it does not seem worth it. - Span* span = Static::pageheap()->New(tcmalloc::pages(size)); - return UNLIKELY(span == NULL) ? NULL : SpanToMallocResult(span); - } - // Allocate extra pages and carve off an aligned portion const Length alloc = tcmalloc::pages(size + align); Span* span = Static::pageheap()->New(alloc); - if (UNLIKELY(span == NULL)) return NULL; + if (PREDICT_FALSE(span == NULL)) return NULL; // Skip starting portion so that we end up aligned Length skip = 0; @@ -1547,15 +1683,6 @@ inline struct mallinfo do_mallinfo() { } #endif // HAVE_STRUCT_MALLINFO -inline void* cpp_alloc(size_t size, bool nothrow) { - void* p = do_malloc(size); - if (LIKELY(p)) { - return p; - } - return handle_oom(retry_malloc, reinterpret_cast(size), - true, nothrow); -} - } // end unnamed namespace // As promised, the definition of this function, declared above. @@ -1578,7 +1705,7 @@ void TCMallocImplementation::MarkThreadBusy() { //------------------------------------------------------------------- extern "C" PERFTOOLS_DLL_DECL const char* tc_version( - int* major, int* minor, const char** patch) __THROW { + int* major, int* minor, const char** patch) PERFTOOLS_NOTHROW { if (major) *major = TC_VERSION_MAJOR; if (minor) *minor = TC_VERSION_MINOR; if (patch) *patch = TC_VERSION_PATCH; @@ -1590,12 +1717,16 @@ extern "C" PERFTOOLS_DLL_DECL const char* tc_version( // If flag is 1, calls to malloc will behave like calls to new, // and the std_new_handler will be invoked on failure. // Returns the previous mode. -extern "C" PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW { +extern "C" PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW { int old_mode = tc_new_mode; tc_new_mode = flag; return old_mode; } +extern "C" PERFTOOLS_DLL_DECL int tc_query_new_mode() PERFTOOLS_NOTHROW { + return tc_new_mode; +} + #ifndef TCMALLOC_USING_DEBUGALLOCATION // debugallocation.cc defines its own // CAVEAT: The code structure below ensures that MallocHook methods are always @@ -1603,31 +1734,259 @@ extern "C" PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW { // heap-checker.cc depends on this to start a stack trace from // the call to the (de)allocation function. -extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW { - void* result = do_malloc_or_cpp_alloc(size); - MallocHook::InvokeNewHook(result, size); - return result; -} +namespace tcmalloc { + -extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW { +static ATTRIBUTE_SECTION(google_malloc) +void invoke_hooks_and_free(void *ptr) { MallocHook::InvokeDeleteHook(ptr); do_free(ptr); } +ATTRIBUTE_SECTION(google_malloc) +void* cpp_throw_oom(size_t size) { + return handle_oom(retry_malloc, reinterpret_cast(size), + true, false); +} + +ATTRIBUTE_SECTION(google_malloc) +void* cpp_nothrow_oom(size_t size) { + return handle_oom(retry_malloc, reinterpret_cast(size), + true, true); +} + +ATTRIBUTE_SECTION(google_malloc) +void* malloc_oom(size_t size) { + return handle_oom(retry_malloc, reinterpret_cast(size), + false, true); +} + +// tcmalloc::allocate_full_XXX is called by fast-path malloc when some +// complex handling is needed (such as fetching object from central +// freelist or malloc sampling). It contains all 'operator new' logic, +// as opposed to malloc_fast_path which only deals with important +// subset of cases. +// +// Note that this is under tcmalloc namespace so that pprof +// can automatically filter it out of growthz/heapz profiles. +// +// We have slightly fancy setup because we need to call hooks from +// function in 'google_malloc' section and we cannot place template +// into this section. Thus 3 separate functions 'built' by macros. +// +// Also note that we're carefully orchestrating for +// MallocHook::GetCallerStackTrace to work even if compiler isn't +// optimizing tail calls (e.g. -O0 is given). We still require +// ATTRIBUTE_ALWAYS_INLINE to work for that case, but it was seen to +// work for -O0 -fno-inline across both GCC and clang. I.e. in this +// case we'll get stack frame for tc_new, followed by stack frame for +// allocate_full_cpp_throw_oom, followed by hooks machinery and user +// code's stack frames. So GetCallerStackTrace will find 2 +// subsequent stack frames in google_malloc section and correctly +// 'cut' stack trace just before tc_new. +template +ATTRIBUTE_ALWAYS_INLINE inline +static void* do_allocate_full(size_t size) { + void* p = do_malloc(size); + if (PREDICT_FALSE(p == NULL)) { + p = OOMHandler(size); + } + MallocHook::InvokeNewHook(p, size); + return CheckedMallocResult(p); +} + +#define AF(oom) \ + ATTRIBUTE_SECTION(google_malloc) \ + void* allocate_full_##oom(size_t size) { \ + return do_allocate_full(size); \ + } + +AF(cpp_throw_oom) +AF(cpp_nothrow_oom) +AF(malloc_oom) + +#undef AF + +template +static ATTRIBUTE_ALWAYS_INLINE inline void* dispatch_allocate_full(size_t size) { + if (OOMHandler == cpp_throw_oom) { + return allocate_full_cpp_throw_oom(size); + } + if (OOMHandler == cpp_nothrow_oom) { + return allocate_full_cpp_nothrow_oom(size); + } + ASSERT(OOMHandler == malloc_oom); + return allocate_full_malloc_oom(size); +} + +struct retry_memalign_data { + size_t align; + size_t size; +}; + +static void *retry_do_memalign(void *arg) { + retry_memalign_data *data = static_cast(arg); + return do_memalign_pages(data->align, data->size); +} + +static ATTRIBUTE_SECTION(google_malloc) +void* memalign_pages(size_t align, size_t size, + bool from_operator, bool nothrow) { + void *rv = do_memalign_pages(align, size); + if (PREDICT_FALSE(rv == NULL)) { + retry_memalign_data data; + data.align = align; + data.size = size; + rv = handle_oom(retry_do_memalign, &data, + from_operator, nothrow); + } + MallocHook::InvokeNewHook(rv, size); + return CheckedMallocResult(rv); +} + +} // namespace tcmalloc + +// This is quick, fast-path-only implementation of malloc/new. It is +// designed to only have support for fast-path. It checks if more +// complex handling is needed (such as a pageheap allocation or +// sampling) and only performs allocation if none of those uncommon +// conditions hold. When we have one of those odd cases it simply +// tail-calls to one of tcmalloc::allocate_full_XXX defined above. +// +// Such approach was found to be quite effective. Generated code for +// tc_{new,malloc} either succeeds quickly or tail-calls to +// allocate_full. Terseness of the source and lack of +// non-tail calls enables compiler to produce better code. Also +// produced code is short enough to enable effort-less human +// comprehension. Which itself led to elimination of various checks +// that were not necessary for fast-path. +template +ATTRIBUTE_ALWAYS_INLINE inline +static void * malloc_fast_path(size_t size) { + if (PREDICT_FALSE(!base::internal::new_hooks_.empty())) { + return tcmalloc::dispatch_allocate_full(size); + } + + ThreadCache *cache = ThreadCache::GetFastPathCache(); + + if (PREDICT_FALSE(cache == NULL)) { + return tcmalloc::dispatch_allocate_full(size); + } + + uint32 cl; + if (PREDICT_FALSE(!Static::sizemap()->GetSizeClass(size, &cl))) { + return tcmalloc::dispatch_allocate_full(size); + } + + size_t allocated_size = Static::sizemap()->ByteSizeForClass(cl); + + if (PREDICT_FALSE(!cache->TryRecordAllocationFast(allocated_size))) { + return tcmalloc::dispatch_allocate_full(size); + } + + return CheckedMallocResult(cache->Allocate(allocated_size, cl, OOMHandler)); +} + +template +ATTRIBUTE_ALWAYS_INLINE inline +static void* memalign_fast_path(size_t align, size_t size) { + if (PREDICT_FALSE(align > kPageSize)) { + if (OOMHandler == tcmalloc::cpp_throw_oom) { + return tcmalloc::memalign_pages(align, size, true, false); + } else if (OOMHandler == tcmalloc::cpp_nothrow_oom) { + return tcmalloc::memalign_pages(align, size, true, true); + } else { + ASSERT(OOMHandler == tcmalloc::malloc_oom); + return tcmalloc::memalign_pages(align, size, false, true); + } + } + + // Everything with alignment <= kPageSize we can easily delegate to + // regular malloc + + return malloc_fast_path(align_size_up(size, align)); +} + +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void* tc_malloc(size_t size) PERFTOOLS_NOTHROW { + return malloc_fast_path(size); +} + +static ATTRIBUTE_ALWAYS_INLINE inline +void free_fast_path(void *ptr) { + if (PREDICT_FALSE(!base::internal::delete_hooks_.empty())) { + tcmalloc::invoke_hooks_and_free(ptr); + return; + } + do_free(ptr); +} + +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void tc_free(void* ptr) PERFTOOLS_NOTHROW { + free_fast_path(ptr); +} + +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW { + if (PREDICT_FALSE(!base::internal::delete_hooks_.empty())) { + tcmalloc::invoke_hooks_and_free(ptr); + return; + } +#ifndef NO_TCMALLOC_SAMPLES + // if ptr is kPageSize-aligned, then it could be sampled allocation, + // thus we don't trust hint and just do plain free. It also handles + // nullptr for us. + if (PREDICT_FALSE((reinterpret_cast(ptr) & (kPageSize-1)) == 0)) { + tc_free(ptr); + return; + } +#else + if (!ptr) { + return; + } +#endif + do_free_with_callback(ptr, &InvalidFree, true, size); +} + +#ifdef TC_ALIAS + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized(void *p, size_t size) PERFTOOLS_NOTHROW + TC_ALIAS(tc_free_sized); +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void *p, size_t size) PERFTOOLS_NOTHROW + TC_ALIAS(tc_free_sized); + +#else + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized(void *p, size_t size) PERFTOOLS_NOTHROW { + tc_free_sized(p, size); +} +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void *p, size_t size) PERFTOOLS_NOTHROW { + tc_free_sized(p, size); +} + +#endif + extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t n, - size_t elem_size) __THROW { + size_t elem_size) PERFTOOLS_NOTHROW { + if (ThreadCache::IsUseEmergencyMalloc()) { + return tcmalloc::EmergencyCalloc(n, elem_size); + } void* result = do_calloc(n, elem_size); MallocHook::InvokeNewHook(result, n * elem_size); return result; } -extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW { - MallocHook::InvokeDeleteHook(ptr); - do_free(ptr); +extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_free); +#else +{ + free_fast_path(ptr); } +#endif extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr, - size_t new_size) __THROW { + size_t new_size) PERFTOOLS_NOTHROW { if (old_ptr == NULL) { void* result = do_malloc_or_cpp_alloc(new_size); MallocHook::InvokeNewHook(result, new_size); @@ -1638,85 +1997,95 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr, do_free(old_ptr); return NULL; } + if (PREDICT_FALSE(tcmalloc::IsEmergencyPtr(old_ptr))) { + return tcmalloc::EmergencyRealloc(old_ptr, new_size); + } return do_realloc(old_ptr, new_size); } -extern "C" PERFTOOLS_DLL_DECL void* tc_new(size_t size) { - void* p = cpp_alloc(size, false); - // We keep this next instruction out of cpp_alloc for a reason: when - // it's in, and new just calls cpp_alloc, the optimizer may fold the - // new call into cpp_alloc, which messes up our whole section-based - // stacktracing (see ATTRIBUTE_SECTION, above). This ensures cpp_alloc - // isn't the last thing this fn calls, and prevents the folding. - MallocHook::InvokeNewHook(p, size); - return p; +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void* tc_new(size_t size) { + return malloc_fast_path(size); } -extern "C" PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW { - void* p = cpp_alloc(size, true); - MallocHook::InvokeNewHook(p, size); - return p; +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void* tc_new_nothrow(size_t size, const std::nothrow_t&) PERFTOOLS_NOTHROW { + return malloc_fast_path(size); } -extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW { - MallocHook::InvokeDeleteHook(p); - do_free(p); +extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_free); +#else +{ + free_fast_path(p); } +#endif // Standard C++ library implementations define and use this // (via ::operator delete(ptr, nothrow)). // But it's really the same as normal delete, so we just do the same thing. -extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, const std::nothrow_t&) __THROW { - MallocHook::InvokeDeleteHook(p); +extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, const std::nothrow_t&) PERFTOOLS_NOTHROW +{ + if (PREDICT_FALSE(!base::internal::delete_hooks_.empty())) { + tcmalloc::invoke_hooks_and_free(p); + return; + } do_free(p); } -extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) { - void* p = cpp_alloc(size, false); - // We keep this next instruction out of cpp_alloc for a reason: when - // it's in, and new just calls cpp_alloc, the optimizer may fold the - // new call into cpp_alloc, which messes up our whole section-based - // stacktracing (see ATTRIBUTE_SECTION, above). This ensures cpp_alloc - // isn't the last thing this fn calls, and prevents the folding. - MallocHook::InvokeNewHook(p, size); - return p; +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) +#ifdef TC_ALIAS +TC_ALIAS(tc_new); +#else +{ + return malloc_fast_path(size); } +#endif extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) - __THROW { - void* p = cpp_alloc(size, true); - MallocHook::InvokeNewHook(p, size); - return p; + PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_new_nothrow); +#else +{ + return malloc_fast_path(size); } +#endif -extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW { - MallocHook::InvokeDeleteHook(p); - do_free(p); +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_free); +#else +{ + free_fast_path(p); } +#endif -extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, const std::nothrow_t&) __THROW { - MallocHook::InvokeDeleteHook(p); - do_free(p); +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, const std::nothrow_t&) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_delete_nothrow); +#else +{ + free_fast_path(p); } +#endif -extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align, - size_t size) __THROW { - void* result = do_memalign_or_cpp_memalign(align, size); - MallocHook::InvokeNewHook(result, size); - return result; +extern "C" PERFTOOLS_DLL_DECL CACHELINE_ALIGNED_FN +void* tc_memalign(size_t align, size_t size) PERFTOOLS_NOTHROW { + return memalign_fast_path(align, size); } extern "C" PERFTOOLS_DLL_DECL int tc_posix_memalign( - void** result_ptr, size_t align, size_t size) __THROW { + void** result_ptr, size_t align, size_t size) PERFTOOLS_NOTHROW { if (((align % sizeof(void*)) != 0) || ((align & (align - 1)) != 0) || (align == 0)) { return EINVAL; } - void* result = do_memalign_or_cpp_memalign(align, size); - MallocHook::InvokeNewHook(result, size); - if (UNLIKELY(result == NULL)) { + void* result = tc_memalign(align, size); + if (PREDICT_FALSE(result == NULL)) { return ENOMEM; } else { *result_ptr = result; @@ -1724,47 +2093,119 @@ extern "C" PERFTOOLS_DLL_DECL int tc_posix_memalign( } } +#if defined(ENABLE_ALIGNED_NEW_DELETE) + +extern "C" PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t align) { + return memalign_fast_path(static_cast(align), size); +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t align, const std::nothrow_t&) PERFTOOLS_NOTHROW { + return memalign_fast_path(static_cast(align), size); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t) PERFTOOLS_NOTHROW +{ + free_fast_path(p); +} + +// There is no easy way to obtain the actual size used by do_memalign to allocate aligned storage, so for now +// just ignore the size. It might get useful in the future. +extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t align) PERFTOOLS_NOTHROW +{ + free_fast_path(p); +} + +extern "C" PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t, const std::nothrow_t&) PERFTOOLS_NOTHROW +{ + free_fast_path(p); +} + +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t align) +#ifdef TC_ALIAS +TC_ALIAS(tc_new_aligned); +#else +{ + return memalign_fast_path(static_cast(align), size); +} +#endif + +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t align, const std::nothrow_t& nt) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_new_aligned_nothrow); +#else +{ + return memalign_fast_path(static_cast(align), size); +} +#endif + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_delete_aligned); +#else +{ + free_fast_path(p); +} +#endif + +// There is no easy way to obtain the actual size used by do_memalign to allocate aligned storage, so for now +// just ignore the size. It might get useful in the future. +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t align) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_delete_sized_aligned); +#else +{ + free_fast_path(p); +} +#endif + +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t, const std::nothrow_t&) PERFTOOLS_NOTHROW +#ifdef TC_ALIAS +TC_ALIAS(tc_delete_aligned_nothrow); +#else +{ + free_fast_path(p); +} +#endif + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + static size_t pagesize = 0; -extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) PERFTOOLS_NOTHROW { // Allocate page-aligned object of length >= size bytes if (pagesize == 0) pagesize = getpagesize(); - void* result = do_memalign_or_cpp_memalign(pagesize, size); - MallocHook::InvokeNewHook(result, size); - return result; + return tc_memalign(pagesize, size); } -extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) PERFTOOLS_NOTHROW { // Round up size to a multiple of pagesize if (pagesize == 0) pagesize = getpagesize(); if (size == 0) { // pvalloc(0) should allocate one page, according to size = pagesize; // http://man.free4web.biz/man3/libmpatrol.3.html } size = (size + pagesize - 1) & ~(pagesize - 1); - void* result = do_memalign_or_cpp_memalign(pagesize, size); - MallocHook::InvokeNewHook(result, size); - return result; + return tc_memalign(pagesize, size); } -extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW { +extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW { do_malloc_stats(); } -extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW { +extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW { return do_mallopt(cmd, value); } #ifdef HAVE_STRUCT_MALLINFO -extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW { +extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW { return do_mallinfo(); } #endif -extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW { +extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW { return MallocExtension::instance()->GetAllocatedSize(ptr); } -extern "C" PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) __THROW { +extern "C" PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW { void* result = do_malloc(size); MallocHook::InvokeNewHook(result, size); return result; diff --git a/tpl/gperftools/src/tcmalloc.h b/tpl/gperftools/src/tcmalloc.h index 2d64f4e..25cf982 100644 --- a/tpl/gperftools/src/tcmalloc.h +++ b/tpl/gperftools/src/tcmalloc.h @@ -56,15 +56,15 @@ #if !HAVE_CFREE_SYMBOL extern "C" void cfree(void* ptr) __THROW; #endif -#if !HAVE_POSIX_MEMALIGN_SYMBOL +#if !HAVE_DECL_POSIX_MEMALIGN extern "C" int posix_memalign(void** ptr, size_t align, size_t size) __THROW; #endif -#if !HAVE_MEMALIGN_SYMBOL +#if !HAVE_DECL_MEMALIGN extern "C" void* memalign(size_t __alignment, size_t __size) __THROW; #endif -#if !HAVE_VALLOC_SYMBOL +#if !HAVE_DECL_VALLOC extern "C" void* valloc(size_t __size) __THROW; #endif -#if !HAVE_PVALLOC_SYMBOL +#if !HAVE_DECL_PVALLOC extern "C" void* pvalloc(size_t __size) __THROW; #endif diff --git a/tpl/gperftools/src/tests/current_allocated_bytes_test.cc b/tpl/gperftools/src/tests/current_allocated_bytes_test.cc index eaa6a7b..49b7dc3 100644 --- a/tpl/gperftools/src/tests/current_allocated_bytes_test.cc +++ b/tpl/gperftools/src/tests/current_allocated_bytes_test.cc @@ -46,12 +46,12 @@ #include #include "base/logging.h" -const char kCurrent[] = "generic.current_allocated_bytes"; - int main() { // We don't do accounting right when using debugallocation.cc, so // turn off the test then. TODO(csilvers): get this working too. #ifdef NDEBUG + static const char kCurrent[] = "generic.current_allocated_bytes"; + size_t before_bytes, after_bytes; MallocExtension::instance()->GetNumericProperty(kCurrent, &before_bytes); free(malloc(200)); diff --git a/tpl/gperftools/src/tests/debugallocation_test.sh b/tpl/gperftools/src/tests/debugallocation_test.sh index faa6c79..0f94ad0 100755 --- a/tpl/gperftools/src/tests/debugallocation_test.sh +++ b/tpl/gperftools/src/tests/debugallocation_test.sh @@ -33,6 +33,9 @@ # Author: Craig Silverstein BINDIR="${BINDIR:-.}" +# We expect PPROF_PATH to be set in the environment. +# If not, we set it to some reasonable value +export PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}" if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then echo "USAGE: $0 [unittest dir]" diff --git a/tpl/gperftools/src/tests/heap-checker-death_unittest.sh b/tpl/gperftools/src/tests/heap-checker-death_unittest.sh index 752a7ad..69db0c9 100755 --- a/tpl/gperftools/src/tests/heap-checker-death_unittest.sh +++ b/tpl/gperftools/src/tests/heap-checker-death_unittest.sh @@ -157,7 +157,7 @@ Test 60 1 "Exiting .* because of .* leaks$" "" \ # Test that we produce a reasonable textual leak report. Test 60 1 "MakeALeak" "" \ - HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECK_TEST_NO_THREADS=1 \ + HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 \ || exit 10 # Test that very early log messages are present and controllable: diff --git a/tpl/gperftools/src/tests/heap-checker_unittest.cc b/tpl/gperftools/src/tests/heap-checker_unittest.cc index 8c8f865..ee60af5 100644 --- a/tpl/gperftools/src/tests/heap-checker_unittest.cc +++ b/tpl/gperftools/src/tests/heap-checker_unittest.cc @@ -1338,8 +1338,8 @@ static void* Mallocer(uintptr_t* addr_after_malloc_call) { return r; } -// to trick complier into preventing inlining -static void* (*mallocer_addr)(uintptr_t* addr) = &Mallocer; +// to trick compiler into preventing inlining +static void* (* volatile mallocer_addr)(uintptr_t* addr) = &Mallocer; // non-static for friendship with HeapProfiler // TODO(maxim): expand this test to include diff --git a/tpl/gperftools/src/tests/heap-profiler_unittest.sh b/tpl/gperftools/src/tests/heap-profiler_unittest.sh index b4c2e9f..91af04f 100755 --- a/tpl/gperftools/src/tests/heap-profiler_unittest.sh +++ b/tpl/gperftools/src/tests/heap-profiler_unittest.sh @@ -54,14 +54,11 @@ fi HEAP_PROFILER="${1:-$BINDIR/heap-profiler_unittest}" PPROF="${2:-$PPROF_PATH}" -TEST_TMPDIR=/tmp/heap_profile_info +TEST_TMPDIR=`mktemp -d /tmp/heap-profiler_unittest.XXXXXX` # It's meaningful to the profiler, so make sure we know its state unset HEAPPROFILE -rm -rf "$TEST_TMPDIR" -mkdir "$TEST_TMPDIR" || exit 2 - num_failures=0 # Given one profile (to check the contents of that profile) or two @@ -140,7 +137,7 @@ VerifyOutputContains "62 MB freed" # testing of the HeapProfileStart/Stop functionality. $HEAP_PROFILER >"$TEST_TMPDIR/output2" 2>&1 -rm -rf $TMPDIR # clean up +rm -rf $TEST_TMPDIR # clean up if [ $num_failures = 0 ]; then echo "PASS" diff --git a/tpl/gperftools/src/tests/markidle_unittest.cc b/tpl/gperftools/src/tests/markidle_unittest.cc index 827609f..92b4cc4 100644 --- a/tpl/gperftools/src/tests/markidle_unittest.cc +++ b/tpl/gperftools/src/tests/markidle_unittest.cc @@ -98,11 +98,29 @@ static void TestIdleUsage() { VLOG(0, "Post idle: %" PRIuS "\n", post_idle); } +static void TestTemporarilyIdleUsage() { + const size_t original = MallocExtension::instance()->GetThreadCacheSize(); + + TestAllocation(); + const size_t post_allocation = MallocExtension::instance()->GetThreadCacheSize(); + CHECK_GT(post_allocation, original); + + MallocExtension::instance()->MarkThreadIdle(); + const size_t post_idle = MallocExtension::instance()->GetThreadCacheSize(); + CHECK_EQ(post_idle, 0); + + // Log after testing because logging can allocate heap memory. + VLOG(0, "Original usage: %" PRIuS "\n", original); + VLOG(0, "Post allocation: %" PRIuS "\n", post_allocation); + VLOG(0, "Post idle: %" PRIuS "\n", post_idle); +} + int main(int argc, char** argv) { RunThread(&TestIdleUsage); RunThread(&TestAllocation); RunThread(&MultipleIdleCalls); RunThread(&MultipleIdleNonIdlePhases); + RunThread(&TestTemporarilyIdleUsage); printf("PASS\n"); return 0; diff --git a/tpl/gperftools/src/tests/packed-cache_test.cc b/tpl/gperftools/src/tests/packed-cache_test.cc index befbd77..3984594 100644 --- a/tpl/gperftools/src/tests/packed-cache_test.cc +++ b/tpl/gperftools/src/tests/packed-cache_test.cc @@ -35,24 +35,43 @@ #include "base/logging.h" #include "packed-cache-inl.h" -static const int kHashbits = PackedCache<64, uint64>::kHashbits; +static const int kHashbits = PackedCache<20>::kHashbits; + +template +static size_t MustGet(const PackedCache& cache, uintptr_t key) { + uint32 rv; + CHECK(cache.TryGet(key, &rv)); + return rv; +} + +template +static size_t Has(const PackedCache& cache, uintptr_t key) { + uint32 dummy; + return cache.TryGet(key, &dummy); +} // A basic sanity test. void PackedCacheTest_basic() { - PackedCache<32, uint32> cache(0); - CHECK_EQ(cache.GetOrDefault(0, 1), 0); + PackedCache<20> cache; + + CHECK(!Has(cache, 0)); cache.Put(0, 17); - CHECK(cache.Has(0)); - CHECK_EQ(cache.GetOrDefault(0, 1), 17); + CHECK(Has(cache, 0)); + CHECK_EQ(MustGet(cache, 0), 17); + cache.Put(19, 99); - CHECK(cache.Has(0) && cache.Has(19)); - CHECK_EQ(cache.GetOrDefault(0, 1), 17); - CHECK_EQ(cache.GetOrDefault(19, 1), 99); + CHECK_EQ(MustGet(cache, 0), 17); + CHECK_EQ(MustGet(cache, 19), 99); + // Knock <0, 17> out by using a conflicting key. cache.Put(1 << kHashbits, 22); - CHECK(!cache.Has(0)); - CHECK_EQ(cache.GetOrDefault(0, 1), 1); - CHECK_EQ(cache.GetOrDefault(1 << kHashbits, 1), 22); + CHECK(!Has(cache, 0)); + CHECK_EQ(MustGet(cache, 1 << kHashbits), 22); + + cache.Invalidate(19); + CHECK(!Has(cache, 19)); + CHECK(!Has(cache, 0)); + CHECK(Has(cache, 1 << kHashbits)); } int main(int argc, char **argv) { diff --git a/tpl/gperftools/src/tests/page_heap_test.cc b/tpl/gperftools/src/tests/page_heap_test.cc index e82a1da..3caacc0 100644 --- a/tpl/gperftools/src/tests/page_heap_test.cc +++ b/tpl/gperftools/src/tests/page_heap_test.cc @@ -6,9 +6,13 @@ // be found in the LICENSE file. #include "config_for_unittests.h" + +#include + +#include + #include "page_heap.h" #include "system-alloc.h" -#include #include "base/logging.h" #include "common.h" @@ -39,33 +43,63 @@ static void CheckStats(const tcmalloc::PageHeap* ph, } static void TestPageHeap_Stats() { - tcmalloc::PageHeap* ph = new tcmalloc::PageHeap(); + std::unique_ptr ph(new tcmalloc::PageHeap()); // Empty page heap - CheckStats(ph, 0, 0, 0); + CheckStats(ph.get(), 0, 0, 0); // Allocate a span 's1' tcmalloc::Span* s1 = ph->New(256); - CheckStats(ph, 256, 0, 0); + CheckStats(ph.get(), 256, 0, 0); // Split span 's1' into 's1', 's2'. Delete 's2' tcmalloc::Span* s2 = ph->Split(s1, 128); ph->Delete(s2); - CheckStats(ph, 256, 128, 0); + CheckStats(ph.get(), 256, 128, 0); // Unmap deleted span 's2' ph->ReleaseAtLeastNPages(1); - CheckStats(ph, 256, 0, 128); + CheckStats(ph.get(), 256, 0, 128); // Delete span 's1' ph->Delete(s1); - CheckStats(ph, 256, 128, 128); + CheckStats(ph.get(), 256, 128, 128); +} - delete ph; +// The number of kMaxPages-sized Spans we will allocate and free during the +// tests. +// We will also do twice this many kMaxPages/2-sized ones. +static constexpr int kNumberMaxPagesSpans = 10; + +// Allocates all the last-level page tables we will need. Doing this before +// calculating the base heap usage is necessary, because otherwise if any of +// these are allocated during the main test it will throw the heap usage +// calculations off and cause the test to fail. +static void AllocateAllPageTables() { + // Make a separate PageHeap from the main test so the test can start without + // any pages in the lists. + std::unique_ptr ph(new tcmalloc::PageHeap()); + tcmalloc::Span *spans[kNumberMaxPagesSpans * 2]; + for (int i = 0; i < kNumberMaxPagesSpans; ++i) { + spans[i] = ph->New(kMaxPages); + EXPECT_NE(spans[i], NULL); + } + for (int i = 0; i < kNumberMaxPagesSpans; ++i) { + ph->Delete(spans[i]); + } + for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) { + spans[i] = ph->New(kMaxPages >> 1); + EXPECT_NE(spans[i], NULL); + } + for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) { + ph->Delete(spans[i]); + } } static void TestPageHeap_Limit() { - tcmalloc::PageHeap* ph = new tcmalloc::PageHeap(); + AllocateAllPageTables(); + + std::unique_ptr ph(new tcmalloc::PageHeap()); CHECK_EQ(kMaxPages, 1 << (20 - kPageShift)); @@ -77,25 +111,26 @@ static void TestPageHeap_Limit() { while((s = ph->New(kMaxPages)) == NULL) { FLAGS_tcmalloc_heap_limit_mb++; } - FLAGS_tcmalloc_heap_limit_mb += 9; + FLAGS_tcmalloc_heap_limit_mb += kNumberMaxPagesSpans - 1; ph->Delete(s); // We are [10, 11) mb from the limit now. } // Test AllocLarge and GrowHeap first: { - tcmalloc::Span * spans[10]; - for (int i=0; i<10; ++i) { + tcmalloc::Span * spans[kNumberMaxPagesSpans]; + for (int i=0; iNew(kMaxPages); EXPECT_NE(spans[i], NULL); } EXPECT_EQ(ph->New(kMaxPages), NULL); - for (int i=0; i<10; i += 2) { + for (int i=0; iDelete(spans[i]); } - tcmalloc::Span *defragmented = ph->New(5 * kMaxPages); + tcmalloc::Span *defragmented = + ph->New(kNumberMaxPagesSpans / 2 * kMaxPages); if (HaveSystemRelease) { // EnsureLimit should release deleted normal spans @@ -109,15 +144,15 @@ static void TestPageHeap_Limit() { EXPECT_TRUE(ph->CheckExpensive()); } - for (int i=1; i<10; i += 2) { + for (int i=1; iDelete(spans[i]); } } // Once again, testing small lists this time (twice smaller spans): { - tcmalloc::Span * spans[20]; - for (int i=0; i<20; ++i) { + tcmalloc::Span * spans[kNumberMaxPagesSpans * 2]; + for (int i=0; iNew(kMaxPages >> 1); EXPECT_NE(spans[i], NULL); } @@ -125,12 +160,12 @@ static void TestPageHeap_Limit() { tcmalloc::Span * lastHalf = ph->New(kMaxPages >> 1); EXPECT_EQ(ph->New(kMaxPages >> 1), NULL); - for (int i=0; i<20; i += 2) { + for (int i=0; iDelete(spans[i]); } - for(Length len = kMaxPages >> 2; len < 5 * kMaxPages; len = len << 1) - { + for (Length len = kMaxPages >> 2; + len < kNumberMaxPagesSpans / 2 * kMaxPages; len = len << 1) { if(len <= kMaxPages >> 1 || HaveSystemRelease) { tcmalloc::Span *s = ph->New(len); EXPECT_NE(s, NULL); @@ -140,7 +175,7 @@ static void TestPageHeap_Limit() { EXPECT_TRUE(ph->CheckExpensive()); - for (int i=1; i<20; i += 2) { + for (int i=1; iDelete(spans[i]); } @@ -148,8 +183,6 @@ static void TestPageHeap_Limit() { ph->Delete(lastHalf); } } - - delete ph; } } // namespace diff --git a/tpl/gperftools/src/tests/profile-handler_unittest.cc b/tpl/gperftools/src/tests/profile-handler_unittest.cc index 2984d0d..a8afbca 100644 --- a/tpl/gperftools/src/tests/profile-handler_unittest.cc +++ b/tpl/gperftools/src/tests/profile-handler_unittest.cc @@ -8,13 +8,6 @@ // // // This file contains the unit tests for profile-handler.h interface. -// -// It is linked into three separate unit tests: -// profile-handler_unittest tests basic functionality -// profile-handler_disable_test tests that the profiler -// is disabled with --install_signal_handlers=false -// profile-handler_conflict_test tests that the profiler -// is disabled when a SIGPROF handler is registered before InitGoogle. #include "config.h" #include "profile-handler.h" @@ -33,12 +26,6 @@ DEFINE_bool(test_profiler_enabled, true, "expect profiler to be enabled during tests"); -// Should we look at the kernel signal handler settings during the test? -// Not if we're in conflict_test, because we can't distinguish its nop -// handler from the real one. -DEFINE_bool(test_profiler_signal_handler, true, - "check profiler signal handler during tests"); - namespace { // TODO(csilvers): error-checking on the pthreads routines @@ -81,11 +68,8 @@ int kSleepInterval = 200000000; // reset. int kTimerResetInterval = 5000000; -// Whether each thread has separate timers. static bool linux_per_thread_timers_mode_ = false; -static bool timer_separate_ = false; static int timer_type_ = ITIMER_PROF; -static int signal_number_ = SIGPROF; // Delays processing by the specified number of nano seconds. 'delay_ns' // must be less than the number of nano seconds in a second (1000000000). @@ -110,51 +94,6 @@ bool IsTimerEnabled() { current_timer.it_value.tv_usec != 0); } -class VirtualTimerGetterThread : public Thread { - public: - VirtualTimerGetterThread() { - memset(&virtual_timer_, 0, sizeof virtual_timer_); - } - struct itimerval virtual_timer_; - - private: - void Run() { - CHECK_EQ(0, getitimer(ITIMER_VIRTUAL, &virtual_timer_)); - } -}; - -// This function checks whether the timers are shared between thread. This -// function spawns a thread, so use it carefully when testing thread-dependent -// behaviour. -static bool threads_have_separate_timers() { - struct itimerval new_timer_val; - - // Enable the virtual timer in the current thread. - memset(&new_timer_val, 0, sizeof new_timer_val); - new_timer_val.it_value.tv_sec = 1000000; // seconds - CHECK_EQ(0, setitimer(ITIMER_VIRTUAL, &new_timer_val, NULL)); - - // Spawn a thread, get the virtual timer's value there. - VirtualTimerGetterThread thread; - thread.SetJoinable(true); - thread.Start(); - thread.Join(); - - // Disable timer here. - memset(&new_timer_val, 0, sizeof new_timer_val); - CHECK_EQ(0, setitimer(ITIMER_VIRTUAL, &new_timer_val, NULL)); - - bool target_timer_enabled = (thread.virtual_timer_.it_value.tv_sec != 0 || - thread.virtual_timer_.it_value.tv_usec != 0); - if (!target_timer_enabled) { - LOG(INFO, "threads have separate timers"); - return true; - } else { - LOG(INFO, "threads have shared timers"); - return false; - } -} - // Dummy worker thread to accumulate cpu time. class BusyThread : public Thread { public: @@ -181,16 +120,12 @@ class BusyThread : public Thread { void Run() { while (!stop_work()) { } - // If timers are separate, check that timer is enabled for this thread. - EXPECT_TRUE(linux_per_thread_timers_mode_ || !timer_separate_ || IsTimerEnabled()); } }; class NullThread : public Thread { private: void Run() { - // If timers are separate, check that timer is enabled for this thread. - EXPECT_TRUE(linux_per_thread_timers_mode_ || !timer_separate_ || IsTimerEnabled()); } }; @@ -205,45 +140,34 @@ static void TickCounter(int sig, siginfo_t* sig_info, void *vuc, class ProfileHandlerTest { protected: - // Determines whether threads have separate timers. + // Determines the timer type. static void SetUpTestCase() { timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF); - signal_number_ = (getenv("CPUPROFILE_REALTIME") ? SIGALRM : SIGPROF); - timer_separate_ = threads_have_separate_timers(); #if HAVE_LINUX_SIGEV_THREAD_ID linux_per_thread_timers_mode_ = (getenv("CPUPROFILE_PER_THREAD_TIMERS") != NULL); const char *signal_number = getenv("CPUPROFILE_TIMER_SIGNAL"); if (signal_number) { - signal_number_ = strtol(signal_number, NULL, 0); + //signal_number_ = strtol(signal_number, NULL, 0); linux_per_thread_timers_mode_ = true; + Delay(kTimerResetInterval); } #endif - Delay(kTimerResetInterval); } // Sets up the profile timers and SIGPROF/SIGALRM handler in a known state. // It does the following: - // 1. Unregisters all the callbacks, stops the timer (if shared) and - // clears out timer_sharing state in the ProfileHandler. This clears - // out any state left behind by the previous test or during module - // initialization when the test program was started. - // 2. Spawns two threads which will be registered with the ProfileHandler. - // At this time ProfileHandler knows if the timers are shared. + // 1. Unregisters all the callbacks, stops the timer and clears out + // timer_sharing state in the ProfileHandler. This clears out any state + // left behind by the previous test or during module initialization when + // the test program was started. // 3. Starts a busy worker thread to accumulate CPU usage. virtual void SetUp() { // Reset the state of ProfileHandler between each test. This unregisters - // all callbacks, stops timer (if shared) and clears timer sharing state. + // all callbacks and stops the timer. ProfileHandlerReset(); EXPECT_EQ(0, GetCallbackCount()); VerifyDisabled(); - // ProfileHandler requires at least two threads to be registerd to determine - // whether timers are shared. - RegisterThread(); - RegisterThread(); - // Now that two threads are started, verify that the signal handler is - // disabled and the timers are correctly enabled/disabled. - VerifyDisabled(); // Start worker to accumulate cpu usage. StartWorker(); } @@ -254,15 +178,6 @@ class ProfileHandlerTest { StopWorker(); } - // Starts a no-op thread that gets registered with the ProfileHandler. Waits - // for the thread to stop. - void RegisterThread() { - NullThread t; - t.SetJoinable(true); - t.Start(); - t.Join(); - } - // Starts a busy worker thread to accumulate cpu time. There should be only // one busy worker running. This is required for the case where there are // separate timers for each thread. @@ -282,14 +197,6 @@ class ProfileHandlerTest { delete busy_worker_; } - // Checks whether SIGPROF/SIGALRM signal handler is enabled. - bool IsSignalEnabled() { - struct sigaction sa; - CHECK_EQ(sigaction(signal_number_, NULL, &sa), 0); - return ((sa.sa_handler == SIG_IGN) || (sa.sa_handler == SIG_DFL)) ? - false : true; - } - // Gets the number of callbacks registered with the ProfileHandler. uint32 GetCallbackCount() { ProfileHandlerState state; @@ -311,10 +218,6 @@ class ProfileHandlerTest { EXPECT_GT(GetCallbackCount(), 0); // Check that the profile timer is enabled. EXPECT_EQ(FLAGS_test_profiler_enabled, linux_per_thread_timers_mode_ || IsTimerEnabled()); - // Check that the signal handler is enabled. - if (FLAGS_test_profiler_signal_handler) { - EXPECT_EQ(FLAGS_test_profiler_enabled, IsSignalEnabled()); - } uint64 interrupts_before = GetInterruptCount(); // Sleep for a bit and check that tick counter is making progress. int old_tick_count = tick_counter; @@ -337,38 +240,18 @@ class ProfileHandlerTest { Delay(kSleepInterval); int new_tick_count = tick_counter; EXPECT_EQ(old_tick_count, new_tick_count); - // If no callbacks, signal handler and shared timer should be disabled. + // If no callbacks, timer should be disabled. if (GetCallbackCount() == 0) { - if (FLAGS_test_profiler_signal_handler) { - EXPECT_FALSE(IsSignalEnabled()); - } - if (!linux_per_thread_timers_mode_) { - if (timer_separate_) { - EXPECT_TRUE(IsTimerEnabled()); - } else { - EXPECT_FALSE(IsTimerEnabled()); - } - } + EXPECT_FALSE(IsTimerEnabled()); } } - // Verifies that the SIGPROF/SIGALRM interrupt handler is disabled and the - // timer, if shared, is disabled. Expects the worker to be running. + // Verifies that the timer is disabled. Expects the worker to be running. void VerifyDisabled() { - // Check that the signal handler is disabled. - if (FLAGS_test_profiler_signal_handler) { - EXPECT_FALSE(IsSignalEnabled()); - } // Check that the callback count is 0. EXPECT_EQ(0, GetCallbackCount()); - // Check that the timer is disabled if shared, enabled otherwise. - if (!linux_per_thread_timers_mode_) { - if (timer_separate_) { - EXPECT_TRUE(IsTimerEnabled()); - } else { - EXPECT_FALSE(IsTimerEnabled()); - } - } + // Check that the timer is disabled. + EXPECT_FALSE(IsTimerEnabled()); // Verify that the ProfileHandler is not accumulating profile ticks. uint64 interrupts_before = GetInterruptCount(); Delay(kSleepInterval); @@ -435,14 +318,14 @@ TEST_F(ProfileHandlerTest, RegisterUnregisterCallback) { // Verifies that multiple callbacks can be registered. TEST_F(ProfileHandlerTest, MultipleCallbacks) { // Register first callback. - int first_tick_count; + int first_tick_count = 0; ProfileHandlerToken* token1 = RegisterCallback(&first_tick_count); // Check that callback was registered correctly. VerifyRegistration(first_tick_count); EXPECT_EQ(1, GetCallbackCount()); // Register second callback. - int second_tick_count; + int second_tick_count = 0; ProfileHandlerToken* token2 = RegisterCallback(&second_tick_count); // Check that callback was registered correctly. VerifyRegistration(second_tick_count); @@ -460,31 +343,31 @@ TEST_F(ProfileHandlerTest, MultipleCallbacks) { VerifyUnregistration(second_tick_count); EXPECT_EQ(0, GetCallbackCount()); - // Verify that the signal handler and timers are correctly disabled. - VerifyDisabled(); + // Verify that the timers is correctly disabled. + if (!linux_per_thread_timers_mode_) VerifyDisabled(); } // Verifies ProfileHandlerReset TEST_F(ProfileHandlerTest, Reset) { // Verify that the profile timer interrupt is disabled. - VerifyDisabled(); - int first_tick_count; + if (!linux_per_thread_timers_mode_) VerifyDisabled(); + int first_tick_count = 0; RegisterCallback(&first_tick_count); VerifyRegistration(first_tick_count); EXPECT_EQ(1, GetCallbackCount()); // Register second callback. - int second_tick_count; + int second_tick_count = 0; RegisterCallback(&second_tick_count); VerifyRegistration(second_tick_count); EXPECT_EQ(2, GetCallbackCount()); // Reset the profile handler and verify that callback were correctly - // unregistered and timer/signal are disabled. + // unregistered and the timer is disabled. ProfileHandlerReset(); VerifyUnregistration(first_tick_count); VerifyUnregistration(second_tick_count); - VerifyDisabled(); + if (!linux_per_thread_timers_mode_) VerifyDisabled(); } // Verifies that ProfileHandler correctly handles a case where a callback was @@ -492,30 +375,20 @@ TEST_F(ProfileHandlerTest, Reset) { TEST_F(ProfileHandlerTest, RegisterCallbackBeforeThread) { // Stop the worker. StopWorker(); - // Unregister all existing callbacks, stop the timer (if shared), disable - // the signal handler and reset the timer sharing state in the Profile - // Handler. + // Unregister all existing callbacks and stop the timer. ProfileHandlerReset(); EXPECT_EQ(0, GetCallbackCount()); VerifyDisabled(); - // Start the worker. At this time ProfileHandler doesn't know if timers are - // shared as only one thread has registered so far. + // Start the worker. StartWorker(); - // Register a callback and check that profile ticks are being delivered. - int tick_count; + // Register a callback and check that profile ticks are being delivered and + // the timer is enabled. + int tick_count = 0; RegisterCallback(&tick_count); EXPECT_EQ(1, GetCallbackCount()); VerifyRegistration(tick_count); - - // Register a second thread and verify that timer and signal handler are - // correctly enabled. - RegisterThread(); - EXPECT_EQ(1, GetCallbackCount()); EXPECT_EQ(FLAGS_test_profiler_enabled, linux_per_thread_timers_mode_ || IsTimerEnabled()); - if (FLAGS_test_profiler_signal_handler) { - EXPECT_EQ(FLAGS_test_profiler_enabled, IsSignalEnabled()); - } } } // namespace diff --git a/tpl/gperftools/src/tests/profiledata_unittest.cc b/tpl/gperftools/src/tests/profiledata_unittest.cc index 972c1b0..3286b9c 100644 --- a/tpl/gperftools/src/tests/profiledata_unittest.cc +++ b/tpl/gperftools/src/tests/profiledata_unittest.cc @@ -366,6 +366,7 @@ class ProfileDataTest { RUN(CollectTwoMatching); RUN(CollectTwoFlush); RUN(StartResetRestart); + RUN(StartStopNoOptionsEmpty); return 0; } }; diff --git a/tpl/gperftools/src/tests/profiler_unittest.cc b/tpl/gperftools/src/tests/profiler_unittest.cc index 321f848..dfc653f 100644 --- a/tpl/gperftools/src/tests/profiler_unittest.cc +++ b/tpl/gperftools/src/tests/profiler_unittest.cc @@ -46,7 +46,7 @@ #include "base/simple_mutex.h" #include "tests/testutil.h" -static int result = 0; +static volatile int result = 0; static int g_iters = 0; // argv[1] Mutex mutex(Mutex::LINKER_INITIALIZED); diff --git a/tpl/gperftools/src/tests/sampler_test.cc b/tpl/gperftools/src/tests/sampler_test.cc index cd64b0f..e0d24d4 100755 --- a/tpl/gperftools/src/tests/sampler_test.cc +++ b/tpl/gperftools/src/tests/sampler_test.cc @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include "base/logging.h" #include "base/commandlineflags.h" #include "sampler.h" // The Sampler class being tested @@ -325,28 +325,6 @@ void TestLRand64Spread() { } -// Test for Fastlog2 code -// We care about the percentage error because we're using this -// for choosing step sizes, so "close" is relative to the size of -// the step we would get if we used the built-in log function -TEST(Sampler, FastLog2) { - tcmalloc::Sampler sampler; - sampler.Init(1); - double max_ratio_error = 0; - for (double d = -1021.9; d < 1; d+= 0.13124235) { - double e = pow(2.0, d); - double truelog = log(e) / log(2.0); // log_2(e) - double fastlog = sampler.FastLog2(e); - max_ratio_error = max(max_ratio_error, - max(truelog/fastlog-1, fastlog/truelog-1)); - CHECK_LE(max_ratio_error, 0.01); - // << StringPrintf("d = %f, e=%f, truelog = %f, fastlog= %f\n", - // d, e, truelog, fastlog); - } - LOG(INFO) << StringPrintf("Fastlog2: max_ratio_error = %f\n", - max_ratio_error); -} - // Futher tests bool CheckMean(size_t mean, int num_samples) { @@ -392,11 +370,11 @@ TEST(Sampler, LargeAndSmallAllocs_CombinedTest) { int num_iters = 128*4*8; // Allocate in mixed chunks for (int i = 0; i < num_iters; i++) { - if (sampler.SampleAllocation(size_big)) { + if (!sampler.RecordAllocation(size_big)) { counter_big += 1; } for (int i = 0; i < 129; i++) { - if (sampler.SampleAllocation(size_small)) { + if (!sampler.RecordAllocation(size_small)) { counter_small += 1; } } @@ -540,12 +518,9 @@ TEST(Sampler, bytes_until_sample_Overflow_Underflow) { uint64_t largest_prng_value = (static_cast(1)<<48) - 1; double q = (largest_prng_value >> (prng_mod_power - 26)) + 1.0; LOG(INFO) << StringPrintf("q = %f\n", q); - LOG(INFO) << StringPrintf("FastLog2(q) = %f\n", sampler.FastLog2(q)); LOG(INFO) << StringPrintf("log2(q) = %f\n", log(q)/log(2.0)); - // Replace min(sampler.FastLog2(q) - 26, 0.0) with - // (sampler.FastLog2(q) - 26.000705) when using that optimization uint64_t smallest_sample_step - = static_cast(min(sampler.FastLog2(q) - 26, 0.0) + = static_cast(min(log2(q) - 26, 0.0) * sample_scaling + 1); LOG(INFO) << "Smallest sample step is " << smallest_sample_step; uint64_t cutoff = static_cast(10) @@ -558,10 +533,8 @@ TEST(Sampler, bytes_until_sample_Overflow_Underflow) { uint64_t smallest_prng_value = 0; q = (smallest_prng_value >> (prng_mod_power - 26)) + 1.0; LOG(INFO) << StringPrintf("q = %f\n", q); - // Replace min(sampler.FastLog2(q) - 26, 0.0) with - // (sampler.FastLog2(q) - 26.000705) when using that optimization uint64_t largest_sample_step - = static_cast(min(sampler.FastLog2(q) - 26, 0.0) + = static_cast(min(log2(q) - 26, 0.0) * sample_scaling + 1); LOG(INFO) << "Largest sample step is " << largest_sample_step; CHECK_LE(largest_sample_step, one<<63); @@ -604,7 +577,7 @@ TEST(Sampler, arithmetic_1) { CHECK_GE(q, 0); // << rnd << " " << prng_mod_power; } // Test some potentially out of bounds value for rnd - for (int i = 1; i <= 66; i++) { + for (int i = 1; i <= 63; i++) { rnd = one << i; double q = (rnd >> (prng_mod_power - 26)) + 1.0; LOG(INFO) << "rnd = " << rnd << " i=" << i << " q=" << q; diff --git a/tpl/gperftools/src/tests/simple_compat_test.cc b/tpl/gperftools/src/tests/simple_compat_test.cc index 5dbfd7a..24583a0 100644 --- a/tpl/gperftools/src/tests/simple_compat_test.cc +++ b/tpl/gperftools/src/tests/simple_compat_test.cc @@ -38,6 +38,9 @@ #include #include + +#define GPERFTOOLS_SUPPRESS_LEGACY_WARNING + #include #include #include diff --git a/tpl/gperftools/src/tests/stack_trace_table_test.cc b/tpl/gperftools/src/tests/stack_trace_table_test.cc index 3cacd2d..393ebbe 100644 --- a/tpl/gperftools/src/tests/stack_trace_table_test.cc +++ b/tpl/gperftools/src/tests/stack_trace_table_test.cc @@ -70,18 +70,10 @@ int main(int argc, char **argv) { AddTrace(&table, t2); CHECK_EQ(table.depth_total(), 4); CHECK_EQ(table.bucket_total(), 2); - static const uintptr_t k3[] = {1, 1024, 2, 1, 2, 1, 512, 2, 2, 1, 0}; + static const uintptr_t k3[] = {1, 512, 2, 2, 1, 1, 1024, 2, 1, 2, 0}; CheckTracesAndReset(&table, k3, ARRAYSIZE(k3)); - // Table w/ 2 x t1, 1 x t2 - AddTrace(&table, t1); - AddTrace(&table, t2); - AddTrace(&table, t1); - CHECK_EQ(table.depth_total(), 4); - CHECK_EQ(table.bucket_total(), 2); - static const uintptr_t k4[] = {2, 2048, 2, 1, 2, 1, 512, 2, 2, 1, 0}; - CheckTracesAndReset(&table, k4, ARRAYSIZE(k4)); - + // Table w/ t1, t3 // Same stack as t1, but w/ different size tcmalloc::StackTrace t3; t3.size = static_cast(2); @@ -89,12 +81,11 @@ int main(int argc, char **argv) { t3.stack[0] = reinterpret_cast(1); t3.stack[1] = reinterpret_cast(2); - // Table w/ t1, t3 AddTrace(&table, t1); AddTrace(&table, t3); - CHECK_EQ(table.depth_total(), 2); - CHECK_EQ(table.bucket_total(), 1); - static const uintptr_t k5[] = {2, 1026, 2, 1, 2, 0}; + CHECK_EQ(table.depth_total(), 4); + CHECK_EQ(table.bucket_total(), 2); + static const uintptr_t k5[] = {1, 2, 2, 1, 2, 1, 1024, 2, 1, 2, 0}; CheckTracesAndReset(&table, k5, ARRAYSIZE(k5)); puts("PASS"); diff --git a/tpl/gperftools/src/tests/tcmalloc_unittest.cc b/tpl/gperftools/src/tests/tcmalloc_unittest.cc index 8d2911f..7ab6b90 100644 --- a/tpl/gperftools/src/tests/tcmalloc_unittest.cc +++ b/tpl/gperftools/src/tests/tcmalloc_unittest.cc @@ -69,7 +69,7 @@ #include #include #include -#if defined HAVE_STDINT_H +#ifdef HAVE_STDINT_H #include // for intptr_t #endif #include // for size_t @@ -91,6 +91,7 @@ #include "base/simple_mutex.h" #include "gperftools/malloc_hook.h" #include "gperftools/malloc_extension.h" +#include "gperftools/nallocx.h" #include "gperftools/tcmalloc.h" #include "thread_cache.h" #include "system-alloc.h" @@ -143,6 +144,27 @@ static inline int PosixMemalign(void** ptr, size_t align, size_t size) { #endif +#if defined(ENABLE_ALIGNED_NEW_DELETE) + +#define OVERALIGNMENT 64 + +struct overaligned_type +{ +#if defined(__GNUC__) + __attribute__((__aligned__(OVERALIGNMENT))) +#elif defined(_MSC_VER) + __declspec(align(OVERALIGNMENT)) +#else + alignas(OVERALIGNMENT) +#endif + unsigned char data[OVERALIGNMENT * 2]; // make the object size different from + // alignment to make sure the correct + // values are passed to the new/delete + // implementation functions +}; + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old // form of the name instead. #ifndef MAP_ANONYMOUS @@ -158,6 +180,37 @@ DECLARE_double(tcmalloc_release_rate); DECLARE_int32(max_free_queue_size); // in debugallocation.cc DECLARE_int64(tcmalloc_sample_parameter); +struct OOMAbleSysAlloc : public SysAllocator { + SysAllocator *child; + int simulate_oom; + + void* Alloc(size_t size, size_t* actual_size, size_t alignment) { + if (simulate_oom) { + return NULL; + } + return child->Alloc(size, actual_size, alignment); + } +}; + +static union { + char buf[sizeof(OOMAbleSysAlloc)]; + void *ptr; +} test_sys_alloc_space; + +static OOMAbleSysAlloc* get_test_sys_alloc() { + return reinterpret_cast(&test_sys_alloc_space); +} + +void setup_oomable_sys_alloc() { + SysAllocator *def = MallocExtension::instance()->GetSystemAllocator(); + + OOMAbleSysAlloc *alloc = get_test_sys_alloc(); + new (alloc) OOMAbleSysAlloc; + alloc->child = def; + + MallocExtension::instance()->SetSystemAllocator(alloc); +} + namespace testing { static const int FLAGS_numtests = 50000; @@ -626,7 +679,7 @@ static void TestRealloc() { #endif } -static void TestNewHandler() throw (std::bad_alloc) { +static void TestNewHandler() { ++news_handled; throw std::bad_alloc(); } @@ -725,9 +778,9 @@ static void TestNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) { // that we used the tcmalloc version of the call, and not the libc. // Note the ... in the hook signature: we don't care what arguments // the hook takes. -#define MAKE_HOOK_CALLBACK(hook_type) \ +#define MAKE_HOOK_CALLBACK(hook_type, ...) \ static volatile int g_##hook_type##_calls = 0; \ - static void IncrementCallsTo##hook_type(...) { \ + static void IncrementCallsTo##hook_type(__VA_ARGS__) { \ g_##hook_type##_calls++; \ } \ static void Verify##hook_type##WasCalled() { \ @@ -744,12 +797,14 @@ static void TestNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) { } // We do one for each hook typedef in malloc_hook.h -MAKE_HOOK_CALLBACK(NewHook); -MAKE_HOOK_CALLBACK(DeleteHook); -MAKE_HOOK_CALLBACK(MmapHook); -MAKE_HOOK_CALLBACK(MremapHook); -MAKE_HOOK_CALLBACK(MunmapHook); -MAKE_HOOK_CALLBACK(SbrkHook); +MAKE_HOOK_CALLBACK(NewHook, const void*, size_t); +MAKE_HOOK_CALLBACK(DeleteHook, const void*); +MAKE_HOOK_CALLBACK(MmapHook, const void*, const void*, size_t, int, int, int, + off_t); +MAKE_HOOK_CALLBACK(MremapHook, const void*, const void*, size_t, size_t, int, + const void*); +MAKE_HOOK_CALLBACK(MunmapHook, const void *, size_t); +MAKE_HOOK_CALLBACK(SbrkHook, const void *, ptrdiff_t); static void TestAlignmentForSize(int size) { fprintf(LOGSTREAM, "Testing alignment of malloc(%d)\n", size); @@ -1067,12 +1122,89 @@ static void TestErrno(void) { EXPECT_EQ(ENOMEM, errno); } + +#ifndef DEBUGALLOCATION +// Ensure that nallocx works before main. +struct GlobalNallocx { + GlobalNallocx() { CHECK_GT(nallocx(99, 0), 99); } +} global_nallocx; + +#if defined(__GNUC__) + +static void check_global_nallocx() __attribute__((constructor)); +static void check_global_nallocx() { CHECK_GT(nallocx(99, 0), 99); } + +#endif // __GNUC__ + +static void TestNAllocX() { + for (size_t size = 0; size <= (1 << 20); size += 7) { + size_t rounded = nallocx(size, 0); + ASSERT_GE(rounded, size); + void* ptr = malloc(size); + ASSERT_EQ(rounded, MallocExtension::instance()->GetAllocatedSize(ptr)); + free(ptr); + } +} + +static void TestNAllocXAlignment() { + for (size_t size = 0; size <= (1 << 20); size += 7) { + for (size_t align = 0; align < 10; align++) { + size_t rounded = nallocx(size, MALLOCX_LG_ALIGN(align)); + ASSERT_GE(rounded, size); + ASSERT_EQ(rounded % (1 << align), 0); + void* ptr = tc_memalign(1 << align, size); + ASSERT_EQ(rounded, MallocExtension::instance()->GetAllocatedSize(ptr)); + free(ptr); + } + } +} + +static int saw_new_handler_runs; +static void* volatile oom_test_last_ptr; + +static void test_new_handler() { + get_test_sys_alloc()->simulate_oom = false; + void *ptr = oom_test_last_ptr; + oom_test_last_ptr = NULL; + ::operator delete[](ptr); + saw_new_handler_runs++; +} + +static ATTRIBUTE_NOINLINE void TestNewOOMHandling() { + // debug allocator does internal allocations and crashes when such + // internal allocation fails. So don't test it. + setup_oomable_sys_alloc(); + + std::new_handler old = std::set_new_handler(test_new_handler); + get_test_sys_alloc()->simulate_oom = true; + + ASSERT_EQ(saw_new_handler_runs, 0); + + for (int i = 0; i < 10240; i++) { + oom_test_last_ptr = new char [512]; + ASSERT_NE(oom_test_last_ptr, NULL); + if (saw_new_handler_runs) { + break; + } + } + + ASSERT_GE(saw_new_handler_runs, 1); + + get_test_sys_alloc()->simulate_oom = false; + std::set_new_handler(old); +} +#endif // !DEBUGALLOCATION + static int RunAllTests(int argc, char** argv) { // Optional argv[1] is the seed AllocatorState rnd(argc > 1 ? atoi(argv[1]) : 100); SetTestResourceLimit(); +#ifndef DEBUGALLOCATION + TestNewOOMHandling(); +#endif + // TODO(odo): This test has been disabled because it is only by luck that it // does not result in fragmentation. When tcmalloc makes an allocation which // spans previously unused leaves of the pagemap it will allocate and fill in @@ -1244,9 +1376,86 @@ static int RunAllTests(int argc, char** argv) { ::operator delete(p2, std::nothrow); VerifyDeleteHookWasCalled(); +#ifdef ENABLE_SIZED_DELETE + p2 = new char; + CHECK(p2 != NULL); + VerifyNewHookWasCalled(); + ::operator delete(p2, sizeof(char)); + VerifyDeleteHookWasCalled(); + + p2 = new char[100]; + CHECK(p2 != NULL); + VerifyNewHookWasCalled(); + ::operator delete[](p2, sizeof(char) * 100); + VerifyDeleteHookWasCalled(); +#endif + +#if defined(ENABLE_ALIGNED_NEW_DELETE) + + overaligned_type* poveraligned = new overaligned_type; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + delete poveraligned; + VerifyDeleteHookWasCalled(); + + poveraligned = new overaligned_type[10]; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + delete[] poveraligned; + VerifyDeleteHookWasCalled(); + + poveraligned = new(std::nothrow) overaligned_type; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + delete poveraligned; + VerifyDeleteHookWasCalled(); + + poveraligned = new(std::nothrow) overaligned_type[10]; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + delete[] poveraligned; + VerifyDeleteHookWasCalled(); + + // Another way of calling operator new + p2 = static_cast(::operator new(100, std::align_val_t(OVERALIGNMENT))); + CHECK(p2 != NULL); + CHECK((((size_t)p2) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + ::operator delete(p2, std::align_val_t(OVERALIGNMENT)); + VerifyDeleteHookWasCalled(); + + p2 = static_cast(::operator new(100, std::align_val_t(OVERALIGNMENT), std::nothrow)); + CHECK(p2 != NULL); + CHECK((((size_t)p2) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + ::operator delete(p2, std::align_val_t(OVERALIGNMENT), std::nothrow); + VerifyDeleteHookWasCalled(); + +#ifdef ENABLE_SIZED_DELETE + poveraligned = new overaligned_type; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + ::operator delete(poveraligned, sizeof(overaligned_type), std::align_val_t(OVERALIGNMENT)); + VerifyDeleteHookWasCalled(); + + poveraligned = new overaligned_type[10]; + CHECK(poveraligned != NULL); + CHECK((((size_t)poveraligned) % OVERALIGNMENT) == 0u); + VerifyNewHookWasCalled(); + ::operator delete[](poveraligned, sizeof(overaligned_type) * 10, std::align_val_t(OVERALIGNMENT)); + VerifyDeleteHookWasCalled(); +#endif + +#endif // defined(ENABLE_ALIGNED_NEW_DELETE) + // Try strdup(), which the system allocates but we must free. If // all goes well, libc will use our malloc! - p2 = strdup("test"); + p2 = strdup("in memory of James Golick"); CHECK(p2 != NULL); VerifyNewHookWasCalled(); free(p2); @@ -1282,9 +1491,9 @@ static int RunAllTests(int argc, char** argv) { VerifyMunmapHookWasCalled(); close(fd); #else // this is just to quiet the compiler: make sure all fns are called - IncrementCallsToMmapHook(); - IncrementCallsToMunmapHook(); - IncrementCallsToMremapHook(); + IncrementCallsToMmapHook(NULL, NULL, 0, 0, 0, 0, 0); + IncrementCallsToMunmapHook(NULL, 0); + IncrementCallsToMremapHook(NULL, NULL, 0, 0, 0, NULL); VerifyMmapHookWasCalled(); VerifyMremapHookWasCalled(); VerifyMunmapHookWasCalled(); @@ -1305,7 +1514,7 @@ static int RunAllTests(int argc, char** argv) { CHECK(p1 != NULL); CHECK_EQ(g_SbrkHook_calls, 0); #else // this is just to quiet the compiler: make sure all fns are called - IncrementCallsToSbrkHook(); + IncrementCallsToSbrkHook(NULL, 0); VerifySbrkHookWasCalled(); #endif @@ -1401,6 +1610,12 @@ static int RunAllTests(int argc, char** argv) { TestSetNewMode(); TestErrno(); +// GetAllocatedSize under DEBUGALLOCATION returns the size that we asked for. +#ifndef DEBUGALLOCATION + TestNAllocX(); + TestNAllocXAlignment(); +#endif + return 0; } diff --git a/tpl/gperftools/src/tests/tcmalloc_unittest.sh b/tpl/gperftools/src/tests/tcmalloc_unittest.sh index 755241e..0e7996a 100755 --- a/tpl/gperftools/src/tests/tcmalloc_unittest.sh +++ b/tpl/gperftools/src/tests/tcmalloc_unittest.sh @@ -69,12 +69,16 @@ run_check_transfer_num_obj "" run_check_transfer_num_obj "40" run_check_transfer_num_obj "4096" -echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_AGGRESSIVE_DECOMMIT=f ... " +echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_AGGRESSIVE_DECOMMIT=t ... " -TCMALLOC_AGGRESSIVE_DECOMMIT=f run_unittest +TCMALLOC_AGGRESSIVE_DECOMMIT=t run_unittest echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_HEAP_LIMIT_MB=512 ... " TCMALLOC_HEAP_LIMIT_MB=512 run_unittest +echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_ENABLE_SIZED_DELETE=t ..." + +TCMALLOC_ENABLE_SIZED_DELETE=t run_unittest + echo "PASS" diff --git a/tpl/gperftools/src/thread_cache.cc b/tpl/gperftools/src/thread_cache.cc index 539c944..97f3ffe 100644 --- a/tpl/gperftools/src/thread_cache.cc +++ b/tpl/gperftools/src/thread_cache.cc @@ -70,8 +70,7 @@ int ThreadCache::thread_heap_count_ = 0; ThreadCache* ThreadCache::next_memory_steal_ = NULL; #ifdef HAVE_TLS __thread ThreadCache::ThreadLocalData ThreadCache::threadlocal_data_ - ATTR_INITIAL_EXEC - = {0, 0}; + ATTR_INITIAL_EXEC CACHELINE_ALIGNED; #endif bool ThreadCache::tsd_inited_ = false; pthread_key_t ThreadCache::heap_key_; @@ -84,7 +83,7 @@ void ThreadCache::Init(pthread_t tid) { if (max_size_ == 0) { // There isn't enough memory to go around. Just give the minimum to // this thread. - max_size_ = kMinThreadCacheSize; + SetMaxSize(kMinThreadCacheSize); // Take unclaimed_cache_space_ negative. unclaimed_cache_space_ -= kMinThreadCacheSize; @@ -95,8 +94,8 @@ void ThreadCache::Init(pthread_t tid) { prev_ = NULL; tid_ = tid; in_setspecific_ = false; - for (size_t cl = 0; cl < kNumClasses; ++cl) { - list_[cl].Init(); + for (uint32 cl = 0; cl < Static::num_size_classes(); ++cl) { + list_[cl].Init(Static::sizemap()->class_to_size(cl)); } uint32_t sampler_seed; @@ -106,7 +105,7 @@ void ThreadCache::Init(pthread_t tid) { void ThreadCache::Cleanup() { // Put unused memory back into central cache - for (int cl = 0; cl < kNumClasses; ++cl) { + for (uint32 cl = 0; cl < Static::num_size_classes(); ++cl) { if (list_[cl].length() > 0) { ReleaseToCentralCache(&list_[cl], cl, list_[cl].length()); } @@ -115,7 +114,8 @@ void ThreadCache::Cleanup() { // Remove some objects of class "cl" from central cache and add to thread heap. // On success, return the first object for immediate use; otherwise return NULL. -void* ThreadCache::FetchFromCentralCache(size_t cl, size_t byte_size) { +void* ThreadCache::FetchFromCentralCache(uint32 cl, int32_t byte_size, + void *(*oom_handler)(size_t size)) { FreeList* list = &list_[cl]; ASSERT(list->empty()); const int batch_size = Static::sizemap()->num_objects_to_move(cl); @@ -125,7 +125,12 @@ void* ThreadCache::FetchFromCentralCache(size_t cl, size_t byte_size) { int fetch_count = Static::central_cache()[cl].RemoveRange( &start, &end, num_to_move); - ASSERT((start == NULL) == (fetch_count == 0)); + if (fetch_count == 0) { + ASSERT(start == NULL); + return oom_handler(byte_size); + } + ASSERT(start != NULL); + if (--fetch_count >= 0) { size_ += byte_size * fetch_count; list->PushRange(fetch_count, SLL_Next(start), end); @@ -152,7 +157,9 @@ void* ThreadCache::FetchFromCentralCache(size_t cl, size_t byte_size) { return start; } -void ThreadCache::ListTooLong(FreeList* list, size_t cl) { +void ThreadCache::ListTooLong(FreeList* list, uint32 cl) { + size_ += list->object_size(); + const int batch_size = Static::sizemap()->num_objects_to_move(cl); ReleaseToCentralCache(list, cl, batch_size); @@ -174,10 +181,14 @@ void ThreadCache::ListTooLong(FreeList* list, size_t cl) { list->set_length_overages(0); } } + + if (PREDICT_FALSE(size_ > max_size_)) { + Scavenge(); + } } // Remove some objects of class "cl" from thread heap and add to central cache -void ThreadCache::ReleaseToCentralCache(FreeList* src, size_t cl, int N) { +void ThreadCache::ReleaseToCentralCache(FreeList* src, uint32 cl, int N) { ASSERT(src == &list_[cl]); if (N > src->length()) N = src->length(); size_t delta_bytes = N * Static::sizemap()->ByteSizeForClass(cl); @@ -205,7 +216,7 @@ void ThreadCache::Scavenge() { // that situation by dropping L/2 nodes from the free list. This // may not release much memory, but if so we will call scavenge again // pretty soon and the low-water marks will be high on that call. - for (int cl = 0; cl < kNumClasses; cl++) { + for (int cl = 0; cl < Static::num_size_classes(); cl++) { FreeList* list = &list_[cl]; const int lowmark = list->lowwatermark(); if (lowmark > 0) { @@ -240,7 +251,7 @@ void ThreadCache::IncreaseCacheLimitLocked() { if (unclaimed_cache_space_ > 0) { // Possibly make unclaimed_cache_space_ negative. unclaimed_cache_space_ -= kStealAmount; - max_size_ += kStealAmount; + SetMaxSize(max_size_ + kStealAmount); return; } // Don't hold pageheap_lock too long. Try to steal from 10 other @@ -258,8 +269,8 @@ void ThreadCache::IncreaseCacheLimitLocked() { next_memory_steal_->max_size_ <= kMinThreadCacheSize) { continue; } - next_memory_steal_->max_size_ -= kStealAmount; - max_size_ += kStealAmount; + next_memory_steal_->SetMaxSize(next_memory_steal_->max_size_ - kStealAmount); + SetMaxSize(max_size_ + kStealAmount); next_memory_steal_ = next_memory_steal_->next_; return; @@ -271,8 +282,11 @@ int ThreadCache::GetSamplePeriod() { } void ThreadCache::InitModule() { - SpinLockHolder h(Static::pageheap_lock()); - if (!phinited) { + { + SpinLockHolder h(Static::pageheap_lock()); + if (phinited) { + return; + } const char *tcb = TCMallocGetenvSafe("TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES"); if (tcb) { set_overall_thread_cache_size(strtoll(tcb, NULL, 10)); @@ -281,14 +295,10 @@ void ThreadCache::InitModule() { threadcache_allocator.Init(); phinited = 1; } -} -void ThreadCache::DeInitModule () { - SpinLockHolder h(Static::pageheap_lock()); - if (phinited) { - // Static::DeInitStaticVars(); - phinited = false; - } + // We do "late" part of initialization without holding lock since + // there is chance it'll recurse into malloc + Static::InitLateMaybeRecursive(); } void ThreadCache::InitTSD() { @@ -309,13 +319,36 @@ void ThreadCache::InitTSD() { #endif } -void ThreadCache::DeInitTSD() { - tsd_inited_ = false; -} - ThreadCache* ThreadCache::CreateCacheIfNecessary() { + if (!tsd_inited_) { +#ifndef NDEBUG + // tests that freeing nullptr very early is working + free(NULL); +#endif + + InitModule(); + } + // Initialize per-thread data if necessary ThreadCache* heap = NULL; + + bool seach_condition = true; +#ifdef HAVE_TLS + static __thread ThreadCache** current_heap_ptr ATTR_INITIAL_EXEC; + if (tsd_inited_) { + // In most common case we're avoiding expensive linear search + // through all heaps (see below). Working TLS enables faster + // protection from malloc recursion in pthread_setspecific + seach_condition = false; + + if (current_heap_ptr != NULL) { + // we're being recursively called by pthread_setspecific below. + return *current_heap_ptr; + } + current_heap_ptr = &heap; + } +#endif + { SpinLockHolder h(Static::pageheap_lock()); // On some old glibc's, and on freebsd's libc (as of freebsd 8.1), @@ -339,10 +372,12 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { // This may be a recursive malloc call from pthread_setspecific() // In that case, the heap for this thread has already been created // and added to the linked list. So we search for that first. - for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) { - if (h->tid_ == me) { - heap = h; - break; + if (seach_condition) { + for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) { + if (h->tid_ == me) { + heap = h; + break; + } } } @@ -359,10 +394,13 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { #ifdef HAVE_TLS // Also keep a copy in __thread for faster retrieval threadlocal_data_.heap = heap; - SetMinSizeForSlowPath(kMaxSize + 1); + threadlocal_data_.fast_path_heap = heap; #endif heap->in_setspecific_ = false; } +#ifdef HAVE_TLS + current_heap_ptr = NULL; +#endif return heap; } @@ -395,7 +433,7 @@ void ThreadCache::BecomeIdle() { #ifdef HAVE_TLS // Also update the copy in __thread threadlocal_data_.heap = NULL; - SetMinSizeForSlowPath(0); + threadlocal_data_.fast_path_heap = NULL; #endif heap->in_setspecific_ = false; if (GetThreadHeap() == heap) { @@ -408,6 +446,12 @@ void ThreadCache::BecomeIdle() { DeleteCache(heap); } +void ThreadCache::BecomeTemporarilyIdle() { + ThreadCache* heap = GetCacheIfPresent(); + if (heap) + heap->Cleanup(); +} + void ThreadCache::DestroyThreadCache(void* ptr) { // Note that "ptr" cannot be NULL since pthread promises not // to invoke the destructor on NULL values, but for safety, @@ -416,7 +460,7 @@ void ThreadCache::DestroyThreadCache(void* ptr) { #ifdef HAVE_TLS // Prevent fast path of GetThreadHeap() from returning heap. threadlocal_data_.heap = NULL; - SetMinSizeForSlowPath(0); + threadlocal_data_.fast_path_heap = NULL; #endif DeleteCache(reinterpret_cast(ptr)); } @@ -454,7 +498,7 @@ void ThreadCache::RecomputePerThreadCacheSize() { // Increasing the total cache size should not circumvent the // slow-start growth of max_size_. if (ratio < 1.0) { - h->max_size_ = static_cast(h->max_size_ * ratio); + h->SetMaxSize(h->max_size_ * ratio); } claimed += h->max_size_; } @@ -466,7 +510,7 @@ void ThreadCache::GetThreadStats(uint64_t* total_bytes, uint64_t* class_count) { for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) { *total_bytes += h->Size(); if (class_count) { - for (int cl = 0; cl < kNumClasses; ++cl) { + for (int cl = 0; cl < Static::num_size_classes(); ++cl) { class_count[cl] += h->freelist_length(cl); } } diff --git a/tpl/gperftools/src/thread_cache.h b/tpl/gperftools/src/thread_cache.h index bb93aee..f8be152 100644 --- a/tpl/gperftools/src/thread_cache.h +++ b/tpl/gperftools/src/thread_cache.h @@ -43,6 +43,7 @@ #include // for uint32_t, uint64_t #endif #include // for ssize_t +#include "base/commandlineflags.h" #include "common.h" #include "linked_list.h" #include "maybe_threads.h" @@ -57,6 +58,8 @@ #include "sampler.h" // for Sampler #include "static_vars.h" // for Static +DECLARE_int64(tcmalloc_sample_parameter); + namespace tcmalloc { //------------------------------------------------------------------- @@ -71,23 +74,19 @@ class ThreadCache { enum { have_tls = false }; #endif - // All ThreadCache objects are kept in a linked list (for stats collection) - ThreadCache* next_; - ThreadCache* prev_; - void Init(pthread_t tid); void Cleanup(); // Accessors (mostly just for printing stats) - int freelist_length(size_t cl) const { return list_[cl].length(); } + int freelist_length(uint32 cl) const { return list_[cl].length(); } // Total byte size in cache size_t Size() const { return size_; } // Allocate an object of the given size and class. The size given // must be the same as the size of the class in the size map. - void* Allocate(size_t size, size_t cl); - void Deallocate(void* ptr, size_t size_class); + void* Allocate(size_t size, uint32 cl, void *(*oom_handler)(size_t size)); + void Deallocate(void* ptr, uint32 size_class); void Scavenge(); @@ -97,20 +96,21 @@ class ThreadCache { // should be sampled bool SampleAllocation(size_t k); + bool TryRecordAllocationFast(size_t k); + static void InitModule(); - static void DeInitModule (); static void InitTSD(); - static void DeInitTSD(); static ThreadCache* GetThreadHeap(); static ThreadCache* GetCache(); static ThreadCache* GetCacheIfPresent(); + static ThreadCache* GetFastPathCache(); static ThreadCache* GetCacheWhichMustBePresent(); static ThreadCache* CreateCacheIfNecessary(); static void BecomeIdle(); - static size_t MinSizeForSlowPath(); - static void SetMinSizeForSlowPath(size_t size); - - static bool IsFastPathAllowed() { return MinSizeForSlowPath() != 0; } + static void BecomeTemporarilyIdle(); + static void SetUseEmergencyMalloc(); + static void ResetUseEmergencyMalloc(); + static bool IsUseEmergencyMalloc(); // Return the number of thread heaps in use. static inline int HeapsInUse(); @@ -152,13 +152,16 @@ class ThreadCache { uint16_t length_overages_; #endif + int32_t size_; + public: - void Init() { + void Init(size_t size) { list_ = NULL; length_ = 0; lowater_ = 0; max_length_ = 1; length_overages_ = 0; + size_ = size; } // Return current length of list @@ -166,6 +169,10 @@ class ThreadCache { return length_; } + int32_t object_size() const { + return size_; + } + // Return the maximum length of the list. size_t max_length() const { return max_length_; @@ -195,9 +202,11 @@ class ThreadCache { int lowwatermark() const { return lowater_; } void clear_lowwatermark() { lowater_ = length_; } - void Push(void* ptr) { + uint32_t Push(void* ptr) { + uint32_t length = length_ + 1; SLL_Push(&list_, ptr); - length_++; + length_ = length; + return length; } void* Pop() { @@ -207,6 +216,15 @@ class ThreadCache { return SLL_Pop(&list_); } + bool TryPop(void **rv) { + if (SLL_TryPop(&list_, rv)) { + length_--; + if (PREDICT_FALSE(length_ < lowater_)) lowater_ = length_; + return true; + } + return false; + } + void* Next() { return SLL_Next(&list_); } @@ -226,14 +244,19 @@ class ThreadCache { // Gets and returns an object from the central cache, and, if possible, // also adds some objects of that size class to this thread cache. - void* FetchFromCentralCache(size_t cl, size_t byte_size); + void* FetchFromCentralCache(uint32 cl, int32_t byte_size, + void *(*oom_handler)(size_t size)); + + void ListTooLong(void* ptr, uint32 cl); // Releases some number of items from src. Adjusts the list's max_length // to eventually converge on num_objects_to_move(cl). - void ListTooLong(FreeList* src, size_t cl); + void ListTooLong(FreeList* src, uint32 cl); // Releases N items from this thread cache. - void ReleaseToCentralCache(FreeList* src, size_t cl, int N); + void ReleaseToCentralCache(FreeList* src, uint32 cl, int N); + + void SetMaxSize(int32 new_max_size); // Increase max_size_ by reducing unclaimed_cache_space_ or by // reducing the max_size_ of some other thread. In both cases, @@ -254,24 +277,15 @@ class ThreadCache { // Since we don't really use dlopen in google code -- and using dlopen // on a malloc replacement is asking for trouble in any case -- that's // a good tradeoff for us. -#ifdef HAVE___ATTRIBUTE__ -#define ATTR_INITIAL_EXEC __attribute__ ((tls_model ("initial-exec"))) -#else -#define ATTR_INITIAL_EXEC -#endif - #ifdef HAVE_TLS struct ThreadLocalData { + ThreadCache* fast_path_heap; ThreadCache* heap; - // min_size_for_slow_path is 0 if heap is NULL or kMaxSize + 1 otherwise. - // The latter is the common case and allows allocation to be faster - // than it would be otherwise: typically a single branch will - // determine that the requested allocation is no more than kMaxSize - // and we can then proceed, knowing that global and thread-local tcmalloc - // state is initialized. - size_t min_size_for_slow_path; + bool use_emergency_malloc; }; - static __thread ThreadLocalData threadlocal_data_ ATTR_INITIAL_EXEC; + static __thread ThreadLocalData threadlocal_data_ + CACHELINE_ALIGNED ATTR_INITIAL_EXEC; + #endif // Thread-specific key. Initialization here is somewhat tricky @@ -279,7 +293,7 @@ class ThreadCache { // is in a good enough state to handle pthread_keycreate(). // Therefore, we use TSD keys only after tsd_inited is set to true. // Until then, we use a slow path to get the heap object. - static bool tsd_inited_; + static ATTRIBUTE_HIDDEN bool tsd_inited_; static pthread_key_t heap_key_; // Linked list of heap objects. Protected by Static::pageheap_lock. @@ -308,14 +322,14 @@ class ThreadCache { // This class is laid out with the most frequently used fields // first so that hot elements are placed on the same cache line. - size_t size_; // Combined size of data - size_t max_size_; // size_ > max_size_ --> Scavenge() + FreeList list_[kClassSizesMax]; // Array indexed by size-class + + int32 size_; // Combined size of data + int32 max_size_; // size_ > max_size_ --> Scavenge() // We sample allocations, biased by the size of the allocation Sampler sampler_; // A sampler - FreeList list_[kNumClasses]; // Array indexed by size-class - pthread_t tid_; // Which thread owns it bool in_setspecific_; // In call to pthread_setspecific? @@ -328,6 +342,12 @@ class ThreadCache { static void DeleteCache(ThreadCache* heap); static void RecomputePerThreadCacheSize(); +public: + + // All ThreadCache objects are kept in a linked list (for stats collection) + ThreadCache* next_; + ThreadCache* prev_; + // Ensure that this class is cacheline-aligned. This is critical for // performance, as false sharing would negate many of the benefits // of a per-thread cache. @@ -343,44 +363,45 @@ inline int ThreadCache::HeapsInUse() { return threadcache_allocator.inuse(); } -inline bool ThreadCache::SampleAllocation(size_t k) { - return sampler_.SampleAllocation(k); -} +inline ATTRIBUTE_ALWAYS_INLINE void* ThreadCache::Allocate( + size_t size, uint32 cl, void *(*oom_handler)(size_t size)) { + FreeList* list = &list_[cl]; + +#ifdef NO_TCMALLOC_SAMPLES + size = list->object_size(); +#endif -inline void* ThreadCache::Allocate(size_t size, size_t cl) { ASSERT(size <= kMaxSize); - ASSERT(size == Static::sizemap()->ByteSizeForClass(cl)); + ASSERT(size != 0); + ASSERT(size == 0 || size == Static::sizemap()->ByteSizeForClass(cl)); - FreeList* list = &list_[cl]; - if (UNLIKELY(list->empty())) { - return FetchFromCentralCache(cl, size); + void* rv; + if (!list->TryPop(&rv)) { + return FetchFromCentralCache(cl, size, oom_handler); } size_ -= size; - return list->Pop(); + return rv; } -inline void ThreadCache::Deallocate(void* ptr, size_t cl) { +inline ATTRIBUTE_ALWAYS_INLINE void ThreadCache::Deallocate(void* ptr, uint32 cl) { + ASSERT(list_[cl].max_length() > 0); FreeList* list = &list_[cl]; - size_ += Static::sizemap()->ByteSizeForClass(cl); - ssize_t size_headroom = max_size_ - size_ - 1; // This catches back-to-back frees of allocs in the same size // class. A more comprehensive (and expensive) test would be to walk // the entire freelist. But this might be enough to find some bugs. ASSERT(ptr != list->Next()); - list->Push(ptr); - ssize_t list_headroom = - static_cast(list->max_length()) - list->length(); + uint32_t length = list->Push(ptr); - // There are two relatively uncommon things that require further work. - // In the common case we're done, and in that case we need a single branch - // because of the bitwise-or trick that follows. - if (UNLIKELY((list_headroom | size_headroom) < 0)) { - if (list_headroom < 0) { - ListTooLong(list, cl); - } - if (size_ >= max_size_) Scavenge(); + if (PREDICT_FALSE(length > list->max_length())) { + ListTooLong(list, cl); + return; + } + + size_ += list->object_size(); + if (PREDICT_FALSE(size_ > max_size_)){ + Scavenge(); } } @@ -405,12 +426,14 @@ inline ThreadCache* ThreadCache::GetCacheWhichMustBePresent() { } inline ThreadCache* ThreadCache::GetCache() { +#ifdef HAVE_TLS + ThreadCache* ptr = GetThreadHeap(); +#else ThreadCache* ptr = NULL; - if (!tsd_inited_) { - InitModule(); - } else { + if (PREDICT_TRUE(tsd_inited_)) { ptr = GetThreadHeap(); } +#endif if (ptr == NULL) ptr = CreateCacheIfNecessary(); return ptr; } @@ -419,24 +442,69 @@ inline ThreadCache* ThreadCache::GetCache() { // because we may be in the thread destruction code and may have // already cleaned up the cache for this thread. inline ThreadCache* ThreadCache::GetCacheIfPresent() { - if (!tsd_inited_) return NULL; +#ifndef HAVE_TLS + if (PREDICT_FALSE(!tsd_inited_)) return NULL; +#endif return GetThreadHeap(); } -inline size_t ThreadCache::MinSizeForSlowPath() { -#ifdef HAVE_TLS - return threadlocal_data_.min_size_for_slow_path; +inline ThreadCache* ThreadCache::GetFastPathCache() { +#ifndef HAVE_TLS + return GetCacheIfPresent(); #else - return 0; + return threadlocal_data_.fast_path_heap; +#endif +} + +inline void ThreadCache::SetUseEmergencyMalloc() { +#ifdef HAVE_TLS + threadlocal_data_.fast_path_heap = NULL; + threadlocal_data_.use_emergency_malloc = true; #endif } -inline void ThreadCache::SetMinSizeForSlowPath(size_t size) { +inline void ThreadCache::ResetUseEmergencyMalloc() { #ifdef HAVE_TLS - threadlocal_data_.min_size_for_slow_path = size; + ThreadCache *heap = threadlocal_data_.heap; + threadlocal_data_.fast_path_heap = heap; + threadlocal_data_.use_emergency_malloc = false; +#endif +} + +inline bool ThreadCache::IsUseEmergencyMalloc() { +#if defined(HAVE_TLS) && defined(ENABLE_EMERGENCY_MALLOC) + return PREDICT_FALSE(threadlocal_data_.use_emergency_malloc); +#else + return false; #endif } +inline void ThreadCache::SetMaxSize(int32 new_max_size) { + max_size_ = new_max_size; +} + +#ifndef NO_TCMALLOC_SAMPLES + +inline bool ThreadCache::SampleAllocation(size_t k) { + return !sampler_.RecordAllocation(k); +} + +inline bool ThreadCache::TryRecordAllocationFast(size_t k) { + return sampler_.TryRecordAllocationFast(k); +} + +#else + +inline bool ThreadCache::SampleAllocation(size_t k) { + return false; +} + +inline bool ThreadCache::TryRecordAllocationFast(size_t k) { + return true; +} + +#endif + } // namespace tcmalloc #endif // TCMALLOC_THREAD_CACHE_H_ diff --git a/tpl/gperftools/src/windows/addr2line-pdb.c b/tpl/gperftools/src/windows/addr2line-pdb.c index 5c65a03..ca699d2 100644 --- a/tpl/gperftools/src/windows/addr2line-pdb.c +++ b/tpl/gperftools/src/windows/addr2line-pdb.c @@ -1,10 +1,10 @@ /* Copyright (c) 2008, Google Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -35,9 +35,17 @@ * c:\websymbols without asking. */ +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE +#endif #include #include @@ -49,8 +57,8 @@ #define WEBSYM "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols" void usage() { - fprintf(stderr, "usage: " - "addr2line-pdb [-f|--functions] [-C|--demangle] [-e filename]\n"); + fprintf(stderr, "usage: addr2line-pdb " + "[-f|--functions] [-C|--demangle] [-e|--exe filename]\n"); fprintf(stderr, "(Then list the hex addresses on stdin, one per line)\n"); } @@ -73,7 +81,8 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "--demangle") == 0 || strcmp(argv[i], "-C") == 0) { symopts |= SYMOPT_UNDNAME; - } else if (strcmp(argv[i], "-e") == 0) { + } else if (strcmp(argv[i], "--exe") == 0 || + strcmp(argv[i], "-e") == 0) { if (i + 1 >= argc) { fprintf(stderr, "FATAL ERROR: -e must be followed by a filename\n"); return 1; @@ -93,7 +102,7 @@ int main(int argc, char *argv[]) { if (!SymInitialize(process, NULL, FALSE)) { error = GetLastError(); - fprintf(stderr, "SymInitialize returned error : %d\n", error); + fprintf(stderr, "SymInitialize returned error : %lu\n", error); return 1; } @@ -107,13 +116,13 @@ int main(int argc, char *argv[]) { strcat(search, ";" WEBSYM); } else { error = GetLastError(); - fprintf(stderr, "SymGetSearchPath returned error : %d\n", error); + fprintf(stderr, "SymGetSearchPath returned error : %lu\n", error); rv = 1; /* An error, but not a fatal one */ strcpy(search, WEBSYM); /* Use a default value */ } if (!SymSetSearchPath(process, search)) { error = GetLastError(); - fprintf(stderr, "SymSetSearchPath returned error : %d\n", error); + fprintf(stderr, "SymSetSearchPath returned error : %lu\n", error); rv = 1; /* An error, but not a fatal one */ } @@ -122,7 +131,7 @@ int main(int argc, char *argv[]) { if (!module_base) { /* SymLoadModuleEx failed */ error = GetLastError(); - fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n", + fprintf(stderr, "SymLoadModuleEx returned error : %lu for %s\n", error, filename); SymCleanup(process); return 1; @@ -133,25 +142,35 @@ int main(int argc, char *argv[]) { /* GNU addr2line seems to just do a strtol and ignore any * weird characters it gets, so we will too. */ - unsigned __int64 addr = _strtoui64(buf, NULL, 16); + unsigned __int64 reladdr = _strtoui64(buf, NULL, 16); ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; + memset(buffer, 0, sizeof(buffer)); PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; IMAGEHLP_LINE64 line; DWORD dummy; + + // Just ignore overflow. In an overflow scenario, the resulting address + // will be lower than module_base which hasn't been mapped by any prior + // SymLoadModuleEx() command. This will cause SymFromAddr() and + // SymGetLineFromAddr64() both to return failures and print the correct + // ?? and ??:0 message variant. + ULONG64 absaddr = reladdr + module_base; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); - pSymbol->MaxNameLen = MAX_SYM_NAME; + // The length of the name is not including the null-terminating character. + pSymbol->MaxNameLen = MAX_SYM_NAME - 1; if (print_function_name) { - if (SymFromAddr(process, (DWORD64)addr, NULL, pSymbol)) { + if (SymFromAddr(process, (DWORD64)absaddr, NULL, pSymbol)) { printf("%s\n", pSymbol->Name); } else { printf("??\n"); } } line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - if (SymGetLineFromAddr64(process, (DWORD64)addr, &dummy, &line)) { + if (SymGetLineFromAddr64(process, (DWORD64)absaddr, &dummy, &line)) { printf("%s:%d\n", line.FileName, (int)line.LineNumber); } else { printf("??:0\n"); diff --git a/tpl/gperftools/src/windows/config.h b/tpl/gperftools/src/windows/config.h index c1ab03e..e860bc3 100644 --- a/tpl/gperftools/src/windows/config.h +++ b/tpl/gperftools/src/windows/config.h @@ -15,238 +15,262 @@ #ifndef GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_ #define GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_ +/* used by tcmalloc.h */ +#define GPERFTOOLS_CONFIG_H_ /* define this if you are linking tcmalloc statically and overriding the * default allocators. * For instructions on how to use this mode, see * http://groups.google.com/group/google-perftools/browse_thread/thread/41cd3710af85e57b */ -#undef WIN32_OVERRIDE_ALLOCATORS +/* #undef WIN32_OVERRIDE_ALLOCATORS */ -/* Define to 1 if your libc has a snprintf implementation */ -#if defined(_MSC_VER) && _MSC_VER >= 1900 -#define HAVE_SNPRINTF 1 -#else -#undef HAVE_SNPRINTF -#endif +/* Build new/delete operators for overaligned types */ +/* #undef ENABLE_ALIGNED_NEW_DELETE */ + +/* Build runtime detection for sized delete */ +/* #undef ENABLE_DYNAMIC_SIZED_DELETE */ + +/* Build sized deletion operators */ +/* #undef ENABLE_SIZED_DELETE */ + +/* Define to 1 if compiler supports __builtin_expect */ +/* #undef HAVE_BUILTIN_EXPECT */ /* Define to 1 if compiler supports __builtin_stack_pointer */ -#undef HAVE_BUILTIN_STACK_POINTER +/* #undef HAVE_BUILTIN_STACK_POINTER */ /* Define to 1 if you have the header file. */ -#undef HAVE_CONFLICT_SIGNAL_H +/* #undef HAVE_CONFLICT_SIGNAL_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_CYGWIN_SIGNAL_H +/* #undef HAVE_CYGWIN_SIGNAL_H */ + +/* Define to 1 if you have the declaration of `backtrace', and to 0 if you + don't. */ +/* #undef HAVE_DECL_BACKTRACE */ /* Define to 1 if you have the declaration of `cfree', and to 0 if you don't. */ -#undef HAVE_DECL_CFREE +#define HAVE_DECL_CFREE 0 /* Define to 1 if you have the declaration of `memalign', and to 0 if you don't. */ -#undef HAVE_DECL_MEMALIGN +#define HAVE_DECL_MEMALIGN 0 + +/* Define to 1 if you have the declaration of `nanosleep', and to 0 if you + don't. */ +#define HAVE_DECL_NANOSLEEP 0 /* Define to 1 if you have the declaration of `posix_memalign', and to 0 if you don't. */ -#undef HAVE_DECL_POSIX_MEMALIGN +#define HAVE_DECL_POSIX_MEMALIGN 0 /* Define to 1 if you have the declaration of `pvalloc', and to 0 if you don't. */ -#undef HAVE_DECL_PVALLOC +#define HAVE_DECL_PVALLOC 0 + +/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't. + */ +#define HAVE_DECL_SLEEP 0 /* Define to 1 if you have the declaration of `uname', and to 0 if you don't. */ -#undef HAVE_DECL_UNAME +#define HAVE_DECL_UNAME 0 /* Define to 1 if you have the declaration of `valloc', and to 0 if you don't. */ -#undef HAVE_DECL_VALLOC +#define HAVE_DECL_VALLOC 0 /* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H +/* #undef HAVE_DLFCN_H */ /* Define to 1 if the system has the type `Elf32_Versym'. */ -#undef HAVE_ELF32_VERSYM +/* #undef HAVE_ELF32_VERSYM */ /* Define to 1 if you have the header file. */ -#undef HAVE_EXECINFO_H +/* #undef HAVE_EXECINFO_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H +#define HAVE_FCNTL_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_FEATURES_H +/* #undef HAVE_FEATURES_H */ + +/* Define to 1 if you have the `fork' function. */ +/* #undef HAVE_FORK */ /* Define to 1 if you have the `geteuid' function. */ -#undef HAVE_GETEUID +/* #undef HAVE_GETEUID */ /* Define to 1 if you have the `getpagesize' function. */ #define HAVE_GETPAGESIZE 1 /* we define it in windows/port.cc */ /* Define to 1 if you have the header file. */ -#undef HAVE_GLOB_H +/* #undef HAVE_GLOB_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_GRP_H +/* #undef HAVE_GRP_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#define HAVE_INTTYPES_H 1 +#endif /* Define to 1 if you have the header file. */ -#undef HAVE_LIBUNWIND_H +/* #undef HAVE_LIBUNWIND_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_PTRACE_H +/* #undef HAVE_LINUX_PTRACE_H */ + +/* Define if this is Linux that has SIGEV_THREAD_ID */ +/* #undef HAVE_LINUX_SIGEV_THREAD_ID */ /* Define to 1 if you have the header file. */ #define HAVE_MALLOC_H 1 -/* Define to 1 if you have the header file. */ -#undef HAVE_MALLOC_MALLOC_H - /* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H +#define HAVE_MEMORY_H 1 /* Define to 1 if you have a working `mmap' system call. */ -#undef HAVE_MMAP +/* #undef HAVE_MMAP */ /* define if the compiler implements namespaces */ #define HAVE_NAMESPACES 1 /* Define to 1 if you have the header file. */ -#undef HAVE_POLL_H +/* #undef HAVE_POLL_H */ /* define if libc has program_invocation_name */ -#undef HAVE_PROGRAM_INVOCATION_NAME +/* #undef HAVE_PROGRAM_INVOCATION_NAME */ /* Define if you have POSIX threads libraries and header files. */ -#undef HAVE_PTHREAD +/* #undef HAVE_PTHREAD */ + +/* defined to 1 if pthread symbols are exposed even without include pthread.h + */ +/* #undef HAVE_PTHREAD_DESPITE_ASKING_FOR */ /* Define to 1 if you have the header file. */ -#undef HAVE_PWD_H +/* #undef HAVE_PWD_H */ /* Define to 1 if you have the `sbrk' function. */ -#undef HAVE_SBRK +/* #undef HAVE_SBRK */ /* Define to 1 if you have the header file. */ -#undef HAVE_SCHED_H +/* #undef HAVE_SCHED_H */ /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) && _MSC_VER >= 1900 #define HAVE_STDINT_H 1 -#else -#undef HAVE_STDINT_H #endif /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H +/* #undef HAVE_STRINGS_H */ /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if the system has the type `struct mallinfo'. */ -#undef HAVE_STRUCT_MALLINFO +/* #undef HAVE_STRUCT_MALLINFO */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_CDEFS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_MALLOC_H +/* #undef HAVE_SYS_CDEFS_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PARAM_H +/* #undef HAVE_SYS_PARAM_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PRCTL_H +/* #undef HAVE_SYS_PRCTL_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_RESOURCE_H +/* #undef HAVE_SYS_RESOURCE_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H +/* #undef HAVE_SYS_SOCKET_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SYSCALL_H +/* #undef HAVE_SYS_SYSCALL_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 -/* is broken on redhat 7 */ -#undef HAVE_SYS_UCONTEXT_H +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UCONTEXT_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_WAIT_H +/* #undef HAVE_SYS_WAIT_H */ /* Define to 1 if compiler supports __thread */ #define HAVE_TLS 1 /* Define to 1 if you have the header file. */ -#undef HAVE_UCONTEXT_H +/* #undef HAVE_UCONTEXT_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H +/* #undef HAVE_UNISTD_H */ + +/* Whether contains _Unwind_Backtrace */ +/* #undef HAVE_UNWIND_BACKTRACE */ /* Define to 1 if you have the header file. */ -#undef HAVE_UNWIND_H +/* #undef HAVE_UNWIND_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_VALGRIND_H +/* #undef HAVE_VALGRIND_H */ /* define if your compiler has __attribute__ */ -#undef HAVE___ATTRIBUTE__ +/* #undef HAVE___ATTRIBUTE__ */ + +/* define if your compiler supports alignment of functions */ +/* #undef HAVE___ATTRIBUTE__ALIGNED_FN */ /* Define to 1 if compiler supports __environ */ -#undef HAVE___ENVIRON +/* #undef HAVE___ENVIRON */ /* Define to 1 if the system has the type `__int64'. */ #define HAVE___INT64 1 /* prefix where we look for installed files */ -#undef INSTALL_PREFIX +/* #undef INSTALL_PREFIX */ /* Define to 1 if int32_t is equivalent to intptr_t */ -#undef INT32_EQUALS_INTPTR - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#undef LT_OBJDIR - -/* Define to 'volatile' if __malloc_hook is declared volatile */ -#undef MALLOC_HOOK_MAYBE_VOLATILE +#ifndef _WIN64 +#define INT32_EQUALS_INTPTR 1 +#endif -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -#undef NO_MINUS_C_MINUS_O +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +/* #undef LT_OBJDIR */ /* Name of package */ #define PACKAGE "gperftools" /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "opensource@google.com" +#define PACKAGE_BUGREPORT "gperftools@googlegroups.com" /* Define to the full name of this package. */ #define PACKAGE_NAME "gperftools" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "gperftools 2.4" +#define PACKAGE_STRING "gperftools 2.7" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gperftools" /* Define to the home page for this package. */ -#undef PACKAGE_URL +#define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.4" +#define PACKAGE_VERSION "2.7" /* How to access the PC from a struct ucontext */ -#undef PC_FROM_UCONTEXT +/* #undef PC_FROM_UCONTEXT */ /* Always the empty-string on non-windows systems. On windows, should be "__declspec(dllexport)". This way, when we compile the dll, we export our @@ -254,19 +278,31 @@ used internally, to compile the DLL, and every DLL source file #includes "config.h" before anything else. */ #ifndef PERFTOOLS_DLL_DECL -# define PERFTOOLS_IS_A_DLL 1 /* not set if you're statically linking */ -# define PERFTOOLS_DLL_DECL __declspec(dllexport) -# define PERFTOOLS_DLL_DECL_FOR_UNITTESTS __declspec(dllimport) +# define PERFTOOLS_IS_A_DLL 1 /* not set if you're statically linking */ +# define PERFTOOLS_DLL_DECL __declspec(dllexport) +# define PERFTOOLS_DLL_DECL_FOR_UNITTESTS __declspec(dllimport) #endif /* printf format code for printing a size_t and ssize_t */ -#define PRIdS "Id" +#ifdef _WIN64 +#define PRIdS "lld" +#else +#define PRIdS "d" +#endif /* printf format code for printing a size_t and ssize_t */ -#define PRIuS "Iu" +#ifdef _WIN64 +#define PRIuS "llu" +#else +#define PRIuS "u" +#endif /* printf format code for printing a size_t and ssize_t */ -#define PRIxS "Ix" +#ifdef _WIN64 +#define PRIxS "llx" +#else +#define PRIxS "x" +#endif /* Mark the systems where we know it's bad if pthreads runs too early before main (before threads are initialized, presumably). */ @@ -276,16 +312,25 @@ /* Define to necessary symbol if this constant uses a non-standard name on your system. */ -#undef PTHREAD_CREATE_JOINABLE +/* #undef PTHREAD_CREATE_JOINABLE */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* the namespace where STL code like vector<> is defined */ -#define STL_NAMESPACE std +#define STL_NAMESPACE std + +/* Define 32K of internal pages size for tcmalloc */ +/* #undef TCMALLOC_32K_PAGES */ + +/* Define 64K of internal pages size for tcmalloc */ +/* #undef TCMALLOC_64K_PAGES */ + +/* Define 8 bytes of allocation alignment for tcmalloc */ +/* #undef TCMALLOC_ALIGN_8BYTES */ /* Version number of package */ -#undef VERSION +#define VERSION "2.7" /* C99 says: define this to get the PRI... macros from stdint.h */ #ifndef __STDC_FORMAT_MACROS @@ -295,7 +340,7 @@ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus -#undef inline +/* #undef inline */ #endif // --------------------------------------------------------------------- @@ -309,6 +354,10 @@ # define _WIN32_WINNT 0x0501 #endif +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#define HAVE_SNPRINTF 1 +#endif + // We want to make sure not to ever try to #include heap-checker.h #define NO_HEAP_CHECK 1 diff --git a/tpl/gperftools/src/windows/gperftools/tcmalloc.h.in b/tpl/gperftools/src/windows/gperftools/tcmalloc.h.in index f521711..adb7962 100644 --- a/tpl/gperftools/src/windows/gperftools/tcmalloc.h.in +++ b/tpl/gperftools/src/windows/gperftools/tcmalloc.h.in @@ -1,11 +1,11 @@ -// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- /* Copyright (c) 2003, Google Inc. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -30,33 +30,39 @@ * * --- * Author: Sanjay Ghemawat - * .h.in file by Craig Silverstein + * .h file by Craig Silverstein */ #ifndef TCMALLOC_TCMALLOC_H_ #define TCMALLOC_TCMALLOC_H_ -#include // for size_t -#ifdef HAVE_SYS_CDEFS_H -#include // where glibc defines __THROW +#include /* for size_t */ +#ifdef __cplusplus +#include /* for std::nothrow_t, std::align_val_t */ #endif -// __THROW is defined in glibc systems. It means, counter-intuitively, -// "This function will never throw an exception." It's an optional -// optimization tool, but we may need to use it to match glibc prototypes. -#ifndef __THROW /* I guess we're not on a glibc system */ -# define __THROW /* __THROW is just an optimization, so ok to make it "" */ -#endif +/* Define the version number so folks can check against it */ +#define TC_VERSION_MAJOR @TC_VERSION_MAJOR@ +#define TC_VERSION_MINOR @TC_VERSION_MINOR@ +#define TC_VERSION_PATCH "@TC_VERSION_PATCH@" +#define TC_VERSION_STRING "gperftools @TC_VERSION_MAJOR@.@TC_VERSION_MINOR@@TC_VERSION_PATCH@" -// Define the version number so folks can check against it -#define TC_VERSION_MAJOR @Faodel_PERFTOOLS_TC_VERSION_MAJOR@ -#define TC_VERSION_MINOR @Faodel_PERFTOOLS_TC_VERSION_MINOR@ -#define TC_VERSION_PATCH "@Faodel_PERFTOOLS_TC_VERSION_PATCH@" -#define TC_VERSION_STRING "gperftools @Faodel_PERFTOOLS_TC_VERSION_MAJOR@.@Faodel_PERFTOOLS_TC_VERSION_MINOR@@Faodel_PERFTOOLS_TC_VERSION_PATCH@" +#ifndef PERFTOOLS_NOTHROW -#include // for struct mallinfo, if it's defined +#if __cplusplus >= 201103L +#define PERFTOOLS_NOTHROW noexcept +#elif defined(__cplusplus) +#define PERFTOOLS_NOTHROW throw() +#else +# ifdef __GNUC__ +# define PERFTOOLS_NOTHROW __attribute__((__nothrow__)) +# else +# define PERFTOOLS_NOTHROW +# endif +#endif + +#endif -// Annoying stuff for windows -- makes sure clients can import these functions #ifndef PERFTOOLS_DLL_DECL # ifdef _WIN32 # define PERFTOOLS_DLL_DECL __declspec(dllimport) @@ -66,60 +72,84 @@ #endif #ifdef __cplusplus -namespace std { -struct nothrow_t; -} - extern "C" { #endif - // Returns a human-readable version string. If major, minor, - // and/or patch are not NULL, they are set to the major version, - // minor version, and patch-code (a string, usually ""). + /* + * Returns a human-readable version string. If major, minor, + * and/or patch are not NULL, they are set to the major version, + * minor version, and patch-code (a string, usually ""). + */ PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor, - const char** patch) __THROW; + const char** patch) PERFTOOLS_NOTHROW; - PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW; - PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) __THROW; - PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW; + PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment, - size_t __size) __THROW; + size_t __size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr, - size_t align, size_t size) __THROW; - PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) __THROW; - PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) __THROW; - - PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW; - PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW; -#if 0 - PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW; -#endif + size_t align, size_t size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW; - // This is an alias for MallocExtension::instance()->GetAllocatedSize(). - // It is equivalent to - // OS X: malloc_size() - // glibc: malloc_usable_size() - // Windows: _msize() - PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW; + PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW; + + /* + * This is an alias for MallocExtension::instance()->GetAllocatedSize(). + * It is equivalent to + * OS X: malloc_size() + * glibc: malloc_usable_size() + * Windows: _msize() + */ + PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW; #ifdef __cplusplus - PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW; + PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_new(size_t size); PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, - const std::nothrow_t&) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void* tc_newarray(size_t size); PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, - const std::nothrow_t&) __THROW; - PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW; PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, - const std::nothrow_t&) __THROW; + const std::nothrow_t&) PERFTOOLS_NOTHROW; + +#if defined(__cpp_aligned_new) || (defined(_MSVC_LANG) && _MSVC_LANG > 201402L) + PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t al); + PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t al); + PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW; + PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t al, + const std::nothrow_t&) PERFTOOLS_NOTHROW; +#endif } #endif -#endif // #ifndef TCMALLOC_TCMALLOC_H_ +/* We're only un-defining for public */ +#if !defined(GPERFTOOLS_CONFIG_H_) + +#undef PERFTOOLS_NOTHROW + +#endif /* GPERFTOOLS_CONFIG_H_ */ + +#endif /* #ifndef TCMALLOC_TCMALLOC_H_ */ diff --git a/tpl/gperftools/src/windows/ia32_modrm_map.cc b/tpl/gperftools/src/windows/ia32_modrm_map.cc index f1f1906..142c7cb 100644 --- a/tpl/gperftools/src/windows/ia32_modrm_map.cc +++ b/tpl/gperftools/src/windows/ia32_modrm_map.cc @@ -31,8 +31,8 @@ * Author: Joi Sigurdsson * * Table of relevant information about how to decode the ModR/M byte. - * Based on information in the IA-32 Intel® Architecture - * Software Developer’s Manual Volume 2: Instruction Set Reference. + * Based on information in the IA-32 Intel® Architecture + * Software Developer's Manual Volume 2: Instruction Set Reference. */ #include "mini_disassembler.h" diff --git a/tpl/gperftools/src/windows/ia32_opcode_map.cc b/tpl/gperftools/src/windows/ia32_opcode_map.cc index ba6a79e..e14279c 100644 --- a/tpl/gperftools/src/windows/ia32_opcode_map.cc +++ b/tpl/gperftools/src/windows/ia32_opcode_map.cc @@ -30,8 +30,8 @@ * --- * Author: Joi Sigurdsson * - * Opcode decoding maps. Based on the IA-32 Intel® Architecture - * Software Developer’s Manual Volume 2: Instruction Set Reference. Idea + * Opcode decoding maps. Based on the IA-32 Intel® Architecture + * Software Developer's Manual Volume 2: Instruction Set Reference. Idea * for how to lay out the tables in memory taken from the implementation * in the Bastard disassembly environment. */ diff --git a/tpl/gperftools/src/windows/mingw.h b/tpl/gperftools/src/windows/mingw.h index 0586e62..c91a313 100644 --- a/tpl/gperftools/src/windows/mingw.h +++ b/tpl/gperftools/src/windows/mingw.h @@ -63,6 +63,8 @@ #undef HAVE_PTHREAD #endif +#undef HAVE_FORK + #define HAVE_PID_T #include "windows/port.h" diff --git a/tpl/gperftools/src/windows/mini_disassembler.h b/tpl/gperftools/src/windows/mini_disassembler.h index 93bdc06..93a522e 100644 --- a/tpl/gperftools/src/windows/mini_disassembler.h +++ b/tpl/gperftools/src/windows/mini_disassembler.h @@ -73,7 +73,7 @@ namespace sidestep { // Disassemble() method. // // If you would like to extend this disassembler, please refer to the -// IA-32 Intel® Architecture Software Developer’s Manual Volume 2: +// IA-32 Intel® Architecture Software Developer's Manual Volume 2: // Instruction Set Reference for information about operand decoding // etc. class PERFTOOLS_DLL_DECL MiniDisassembler { diff --git a/tpl/gperftools/src/windows/mini_disassembler_types.h b/tpl/gperftools/src/windows/mini_disassembler_types.h index 06d4755..aceecf4 100644 --- a/tpl/gperftools/src/windows/mini_disassembler_types.h +++ b/tpl/gperftools/src/windows/mini_disassembler_types.h @@ -188,9 +188,9 @@ struct Opcode { // Description of the type of the dest, src and aux operands, // put together from an enOperandType flag and an enAddressingMethod // flag. - int flag_dest_; - int flag_source_; - int flag_aux_; + unsigned flag_dest_; + unsigned flag_source_; + unsigned flag_aux_; // We indicate the mnemonic for debugging purposes const char* mnemonic_; diff --git a/tpl/gperftools/src/windows/override_functions.cc b/tpl/gperftools/src/windows/override_functions.cc index e7917d3..06c4c17 100644 --- a/tpl/gperftools/src/windows/override_functions.cc +++ b/tpl/gperftools/src/windows/override_functions.cc @@ -52,26 +52,70 @@ #include "tcmalloc.cc" -extern "C" void* _recalloc(void* p, size_t n, size_t size) { - void* result = realloc(p, n * size); - memset(result, 0, n * size); - return result; +extern "C" { + +void* _malloc_base(size_t size) { + return malloc(size); } -extern "C" void* _calloc_impl(size_t n, size_t size) { +void _free_base(void* p) { + free(p); +} + +void* _calloc_base(size_t n, size_t size) { return calloc(n, size); } -extern "C" size_t _msize(void* p) { +void* _recalloc(void* old_ptr, size_t n, size_t size) { + // Ensure that (n * size) does not overflow + if (!(n == 0 || (std::numeric_limits::max)() / n >= size)) { + errno = ENOMEM; + return NULL; + } + + const size_t old_size = tc_malloc_size(old_ptr); + const size_t new_size = n * size; + + void* new_ptr = realloc(old_ptr, new_size); + + // If the reallocation succeeded and the new block is larger, zero-fill the + // new bytes: + if (new_ptr != NULL && new_size > old_size) { + memset(static_cast(new_ptr) + old_size, 0, tc_nallocx(new_size, 0) - old_size); + } + + return new_ptr; +} + +void* _calloc_impl(size_t n, size_t size) { + return calloc(n, size); +} + +size_t _msize(void* p) { return MallocExtension::instance()->GetAllocatedSize(p); } -extern "C" intptr_t _get_heap_handle() { +HANDLE __acrt_heap = nullptr; + +bool __acrt_initialize_heap() { + new TCMallocGuard(); + return true; +} + +bool __acrt_uninitialize_heap(bool) { + return true; +} + +intptr_t _get_heap_handle() { return 0; } +HANDLE __acrt_getheap() { + return __acrt_heap; +} + // The CRT heap initialization stub. -extern "C" int _heap_init() { +int _heap_init() { // We intentionally leak this object. It lasts for the process // lifetime. Trying to teardown at _heap_term() is so late that // you can't do anything useful anyway. @@ -80,13 +124,25 @@ extern "C" int _heap_init() { } // The CRT heap cleanup stub. -extern "C" void _heap_term() { +void _heap_term() { } -extern "C" int _set_new_mode(int flag) { +// We set this to 1 because part of the CRT uses a check of _crtheap != 0 +// to test whether the CRT has been initialized. Once we've ripped out +// the allocators from libcmt, we need to provide this definition so that +// the rest of the CRT is still usable. +void* _crtheap = reinterpret_cast(1); + +int _set_new_mode(int flag) { return tc_set_new_mode(flag); } +int _query_new_mode() { + return tc_query_new_mode(); +} + +} // extern "C" + #ifndef NDEBUG #undef malloc #undef free @@ -115,9 +171,3 @@ extern "C" void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { return calloc(n, size); } #endif // NDEBUG - -// We set this to 1 because part of the CRT uses a check of _crtheap != 0 -// to test whether the CRT has been initialized. Once we've ripped out -// the allocators from libcmt, we need to provide this definition so that -// the rest of the CRT is still usable. -extern "C" void* _crtheap = reinterpret_cast(1); diff --git a/tpl/gperftools/src/windows/patch_functions.cc b/tpl/gperftools/src/windows/patch_functions.cc index 52e3870..80e9218 100644 --- a/tpl/gperftools/src/windows/patch_functions.cc +++ b/tpl/gperftools/src/windows/patch_functions.cc @@ -194,6 +194,8 @@ class LibcInfo { k_Msize, k_Expand, // A MS CRT "internal" function, implemented using _calloc_impl k_CallocCrt, + // Underlying deallocation functions called by CRT internal functions or operator delete + kFreeBase, kFreeDbg, kNumFunctions }; @@ -276,6 +278,8 @@ template class LibcInfoWithPatchFunctions : public LibcInfo { static void* Perftools_malloc(size_t size) __THROW; static void Perftools_free(void* ptr) __THROW; + static void Perftools_free_base(void* ptr) __THROW; + static void Perftools_free_dbg(void* ptr, int block_use) __THROW; static void* Perftools_realloc(void* ptr, size_t size) __THROW; static void* Perftools_calloc(size_t nmemb, size_t size) __THROW; static void* Perftools_new(size_t size); @@ -417,7 +421,7 @@ const char* const LibcInfo::function_name_[] = { NULL, // kMangledNewArrayNothrow, NULL, // kMangledDeleteNothrow, NULL, // kMangledDeleteArrayNothrow, - "_msize", "_expand", "_calloc_crt", + "_msize", "_expand", "_calloc_crt", "_free_base", "_free_dbg" }; // For mingw, I can't patch the new/delete here, because the @@ -449,6 +453,8 @@ const GenericFnPtr LibcInfo::static_fn_[] = { (GenericFnPtr)&::_msize, (GenericFnPtr)&::_expand, (GenericFnPtr)&::calloc, + (GenericFnPtr)&::free, + (GenericFnPtr)&::free }; template GenericFnPtr LibcInfoWithPatchFunctions::origstub_fn_[] = { @@ -472,6 +478,8 @@ const GenericFnPtr LibcInfoWithPatchFunctions::perftools_fn_[] = { (GenericFnPtr)&Perftools__msize, (GenericFnPtr)&Perftools__expand, (GenericFnPtr)&Perftools_calloc, + (GenericFnPtr)&Perftools_free_base, + (GenericFnPtr)&Perftools_free_dbg }; /*static*/ WindowsInfo::FunctionInfo WindowsInfo::function_info_[] = { @@ -802,9 +810,7 @@ bool PatchAllModules() { template void* LibcInfoWithPatchFunctions::Perftools_malloc(size_t size) __THROW { - void* result = do_malloc_or_cpp_alloc(size); - MallocHook::InvokeNewHook(result, size); - return result; + return malloc_fast_path(size); } template @@ -814,7 +820,28 @@ void LibcInfoWithPatchFunctions::Perftools_free(void* ptr) __THROW { // allocated by tcmalloc. Note it calls the origstub_free from // *this* templatized instance of LibcInfo. See "template // trickiness" above. - do_free_with_callback(ptr, (void (*)(void*))origstub_fn_[kFree]); + do_free_with_callback(ptr, (void (*)(void*))origstub_fn_[kFree], false, 0); +} + +template +void LibcInfoWithPatchFunctions::Perftools_free_base(void* ptr) __THROW{ + MallocHook::InvokeDeleteHook(ptr); + // This calls the windows free if do_free decides ptr was not + // allocated by tcmalloc. Note it calls the origstub_free from + // *this* templatized instance of LibcInfo. See "template + // trickiness" above. + do_free_with_callback(ptr, (void(*)(void*))origstub_fn_[kFreeBase], false, 0); +} + +template +void LibcInfoWithPatchFunctions::Perftools_free_dbg(void* ptr, int block_use) __THROW { + MallocHook::InvokeDeleteHook(ptr); + // The windows _free_dbg is called if ptr isn't owned by tcmalloc. + if (MallocExtension::instance()->GetOwnership(ptr) == MallocExtension::kOwned) { + do_free(ptr); + } else { + reinterpret_cast(origstub_fn_[kFreeDbg])(ptr, block_use); + } } template @@ -828,7 +855,7 @@ void* LibcInfoWithPatchFunctions::Perftools_realloc( if (new_size == 0) { MallocHook::InvokeDeleteHook(old_ptr); do_free_with_callback(old_ptr, - (void (*)(void*))origstub_fn_[kFree]); + (void (*)(void*))origstub_fn_[kFree], false, 0); return NULL; } return do_realloc_with_callback( @@ -847,58 +874,50 @@ void* LibcInfoWithPatchFunctions::Perftools_calloc( template void* LibcInfoWithPatchFunctions::Perftools_new(size_t size) { - void* p = cpp_alloc(size, false); - MallocHook::InvokeNewHook(p, size); - return p; + return malloc_fast_path(size); } template void* LibcInfoWithPatchFunctions::Perftools_newarray(size_t size) { - void* p = cpp_alloc(size, false); - MallocHook::InvokeNewHook(p, size); - return p; + return malloc_fast_path(size); } template void LibcInfoWithPatchFunctions::Perftools_delete(void *p) { MallocHook::InvokeDeleteHook(p); - do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree]); + do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree], false, 0); } template void LibcInfoWithPatchFunctions::Perftools_deletearray(void *p) { MallocHook::InvokeDeleteHook(p); - do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree]); + do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree], false, 0); } template void* LibcInfoWithPatchFunctions::Perftools_new_nothrow( size_t size, const std::nothrow_t&) __THROW { - void* p = cpp_alloc(size, true); - MallocHook::InvokeNewHook(p, size); - return p; + return malloc_fast_path(size); } template void* LibcInfoWithPatchFunctions::Perftools_newarray_nothrow( size_t size, const std::nothrow_t&) __THROW { - void* p = cpp_alloc(size, true); - MallocHook::InvokeNewHook(p, size); - return p; + return malloc_fast_path(size); } template void LibcInfoWithPatchFunctions::Perftools_delete_nothrow( void *p, const std::nothrow_t&) __THROW { MallocHook::InvokeDeleteHook(p); - do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree]); + do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree], false, 0); } template void LibcInfoWithPatchFunctions::Perftools_deletearray_nothrow( void *p, const std::nothrow_t&) __THROW { MallocHook::InvokeDeleteHook(p); - do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree]); + do_free_with_callback(p, (void (*)(void*))origstub_fn_[kFree], false, 0); } @@ -982,16 +1001,6 @@ BOOL WINAPI WindowsInfo::Perftools_UnmapViewOfFile(LPCVOID lpBaseAddress) { lpBaseAddress); } -// g_load_map holds a copy of windows' refcount for how many times -// each currently loaded module has been loaded and unloaded. We use -// it as an optimization when the same module is loaded more than -// once: as long as the refcount stays above 1, we don't need to worry -// about patching because it's already patched. Likewise, we don't -// need to unpatch until the refcount drops to 0. load_map is -// maintained in LoadLibraryExW and FreeLibrary, and only covers -// modules explicitly loaded/freed via those interfaces. -static std::map* g_load_map = NULL; - HMODULE WINAPI WindowsInfo::Perftools_LoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags) { diff --git a/tpl/gperftools/src/windows/port.cc b/tpl/gperftools/src/windows/port.cc index 76224a2..a8dd1d9 100644 --- a/tpl/gperftools/src/windows/port.cc +++ b/tpl/gperftools/src/windows/port.cc @@ -85,7 +85,8 @@ extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { // Windows doesn't support pthread_key_create's destr_function, and in // fact it's a bit tricky to get code to run when a thread exits. This -// is cargo-cult magic from http://www.codeproject.com/threads/tls.asp. +// is cargo-cult magic from https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// and http://lallouslab.net/2017/05/30/using-cc-tls-callbacks-in-visual-studio-with-your-32-or-64bits-programs/. // This code is for VC++ 7.1 and later; VC++ 6.0 support is possible // but more busy-work -- see the webpage for how to do it. If all // this fails, we could use DllMain instead. The big problem with @@ -147,8 +148,11 @@ static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { // extern "C" suppresses C++ name mangling so we know the symbol names // for the linker /INCLUDE:symbol pragmas above. +// Note that for some unknown reason, the extern "C" {} construct is ignored +// by the MSVC VS2017 compiler (at least) when a const modifier is used +#if defined(_M_IX86) extern "C" { -// This tells the linker to run these functions. +// In x86, the PE loader looks for callbacks in a data segment #pragma data_seg(push, old_seg) #pragma data_seg(".CRT$XLB") void (NTAPI *p_thread_callback_tcmalloc)( @@ -157,6 +161,16 @@ void (NTAPI *p_thread_callback_tcmalloc)( int (*p_process_term_tcmalloc)(void) = on_process_term; #pragma data_seg(pop, old_seg) } // extern "C" +#elif defined(_M_X64) +// In x64, the PE loader looks for callbacks in a constant segment +#pragma const_seg(push, oldseg) +#pragma const_seg(".CRT$XLB") +extern "C" void (NTAPI * const p_thread_callback_tcmalloc)( + HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback; +#pragma const_seg(".CRT$XTU") +extern "C" int (NTAPI * const p_process_term_tcmalloc)(void) = on_process_term; +#pragma const_seg(pop, oldseg) +#endif #else // #ifdef _MSC_VER [probably msys/mingw] diff --git a/tpl/gperftools/src/windows/port.h b/tpl/gperftools/src/windows/port.h index 87db9dd..eb9702b 100644 --- a/tpl/gperftools/src/windows/port.h +++ b/tpl/gperftools/src/windows/port.h @@ -340,6 +340,7 @@ inline int snprintf(char *str, size_t size, const char *format, ...) { } #endif +#ifndef HAVE_INTTYPES_H #define PRIx64 "I64x" #define SCNx64 "I64x" #define PRId64 "I64d" @@ -352,6 +353,7 @@ inline int snprintf(char *str, size_t size, const char *format, ...) { # define PRIuPTR "lu" # define PRIxPTR "lx" #endif +#endif /* ----------------------------------- FILE IO */ diff --git a/tpl/gperftools/src/windows/preamble_patcher.cc b/tpl/gperftools/src/windows/preamble_patcher.cc index ec05537..9ce0816 100644 --- a/tpl/gperftools/src/windows/preamble_patcher.cc +++ b/tpl/gperftools/src/windows/preamble_patcher.cc @@ -510,7 +510,7 @@ void* PreamblePatcher::AllocPageNear(void* target) { reinterpret_cast<__int64>(target) - val > INT_MAX) { // We're further than 2GB from the target break; - } else if (val <= NULL) { + } else if (val <= 0) { // Less than 0 break; } diff --git a/tpl/gperftools/src/windows/system-alloc.cc b/tpl/gperftools/src/windows/system-alloc.cc index 9537745..ea1f17d 100644 --- a/tpl/gperftools/src/windows/system-alloc.cc +++ b/tpl/gperftools/src/windows/system-alloc.cc @@ -46,7 +46,7 @@ static SpinLock spinlock(SpinLock::LINKER_INITIALIZED); // The current system allocator declaration -SysAllocator* sys_alloc = NULL; +SysAllocator* tcmalloc_sys_alloc = NULL; // Number of bytes taken from system. size_t TCMalloc_SystemTaken = 0; @@ -121,7 +121,7 @@ SysAllocator* tc_get_sysalloc_override(SysAllocator *def) static bool system_alloc_inited = false; void InitSystemAllocators(void) { VirtualSysAllocator *alloc = new (virtual_space) VirtualSysAllocator(); - sys_alloc = tc_get_sysalloc_override(alloc); + tcmalloc_sys_alloc = tc_get_sysalloc_override(alloc); } extern PERFTOOLS_DLL_DECL @@ -134,7 +134,7 @@ void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, system_alloc_inited = true; } - void* result = sys_alloc->Alloc(size, actual_size, alignment); + void* result = tcmalloc_sys_alloc->Alloc(size, actual_size, alignment); if (result != NULL) { if (actual_size) { TCMalloc_SystemTaken += *actual_size;