From 45ac7c1d9eab7df86e21b83131ba04a9c530f334 Mon Sep 17 00:00:00 2001 From: Nikita <69168929+nikitalita@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:01:18 -0700 Subject: [PATCH] ZScript DAP Debug server (#3009) * Add STL-standard type traits and functions to TMap to enable for loop iteration * add third-party range-map library I have e-mailed the author for clarifcation on the license, will update this when they respond * vcpkg: Add cppdap and eventpp libraries, update baseline * DAP implementation * Add `FileSystem::FileHash()` to get the CRC32 hash * add `starting_offset` param to `VMDisasm()` for debugger disassembly view Defaults to `0`, should not change output if it's not set * Add `VMFrameStack::HasFrames()` to prevent assertions when inspecting in the debugger * Add `PC` field to VMFrame, ensure that it is updated whenever vm increments/decrements the pc Does not change alignment, the offsets used in VMFrame still work We need this for the debugger because we otherwise have no way to get the pc; it was a local in `ExecScriptFunc()` * `ZCCCompiler::CreateClassTypes()`: ensure SourceLumpName is set for classes derived from non-native classes * start debug server in d_main, add `vm_debug` cvars and `-debug` CLI arg * Add documentation for `vm_debug` cvars * vm_exec: Add debugger hooks for instruction execution events * c_console: Add debugger hooks for logging events in `PrintString` * add `.cache/` to gitignore * vendor cppdap on main @ 6464cd7 Patches: removed .gitmodules (submodules were thirdparty/json, thirdparty/googletest) removed thirdparty/googletest, not needed removed thirdparty/json/docs, thirdparty/json/test, thirdparty/json/benchmarks to prevent massive bloat * vendor eventpp on master @ 1224dd6 * build: use internal cppdap and eventpp by default * dap: fix Binary::GetFunctionLineRange() * fix bug in range_map::find_ranges * make Binary dap::Source dynamic * cache source code upon retrieval * refactor Binary into a class, cleanup PexCache * fix ending session gracefully * d_main: Stop debug server in D_Cleanup() * Fix connecting to debugger when session already started * cleanup unused stuff in ZScriptDebugger * always send TerminateEvents on disconnect if initialized * tweak color display * Don't cache disassembly lines when scanning scripts * Cache nodes when getting runtime state * WIP display locals * Fix display of static arrays * Ensure names display in proper order * dap: Fix struct locals display, add args display * Support `start` parameter * Support `filter` parameter * remove struct unbound native data display Practically useless for debugging zscript and didn't work properly anyway * d_main: fix vm_jit and vm_jit not being disabled soon enough * support breaking on abort exceptions * dap: refactor game event emitter functions into seperate header * dap: show native functions on stack * dap: Remove "Native" from exception handling, simplify exception event emit * dap: add instruction breakpoints * dap: fix display of locals not in scope yet * dap: Make disassembly view display invalid instructions for non-code addresses * dap: remove dot initializers * dap: fix local structs in scope * dap: don't parse the non-used options in the launch/attach requests * dap: fix local struct view * dap: Fix displaying objects that aren't their actual types * dap: Fix action and state handling * dap: stack display view * dap: fix object display view * d_dehacked: set qualified name in addition to the printable name * dap: fix displaying breakpoint errors when script isn't loaded * dap: remove debug print * dap: Display parameter names * dap: Turn down verbosity of logging * dap: fix disassembly view * dap: fix performance problems with arrays * c_console: emit event only if not PRINT_NODAPEVENT * dap: improve logging * dap: update upstream cppdap library to fix deadlocks on no bind * dap: Fix ending session on client socket closed * dap: prevent DebugServer.h from pulling in `dap` and `ZScriptDebugger.h` * dap: fix pause event not being emitted on pause * dap: remove eventpp emitters, way too slow * Remove eventpp dependency * dap: Display correct register names * dap: Show special inits in registers * dap: Add stack offset to VMLocalVariable * dap: fix display of static arrays and local variables on stack vs. registers * dap: fix displaying function pointers * dap: tweak color display * dap: fix scalar display < 4 * dap: add Globals display to debugger * dap: unify methods to get vmvalue * dap: rename free method to freeValue to avoid running afoul of macro defs * fix windows builds * fix compile on linux * cleanup * Fix display of function breakpoints * dap: Don't send back binary files * dap: include sbarinfo in script types (no debug support for anything but zscript yet, this is just for returning source info) * dap: don't show ending session message unless initialized * fix erroneous commit * dap: handle evaluate requests * Fix getting bitfield values * Add CVars Scope and evaluation * add running console commands from repl * dap: disable commands via repl for now * fix loading functions DECORATE scripts * Add source information to Dehacked VM functions, add debugging support * dap: cleanup * fix resolving archive paths * don't send source back on native stack frames * handle `modules` request * cleanup * allow evaluating cvars on hover * fix oob bpinfos * fix restarting the game blowing out the debug server * dap: process input events while paused to prevent deadlocks * fix getting local state * dap: fix LocalState alignment * dap: fix DumpStateHelper * update cppdap protocol version to 1.68.0 * remove cppdap from vcpkg deps We can't use the upstream version anyway because the maintainers are not merging our patches * dap: make named variable nodes derive from the same class * dap: make cvar scope available in native stack frames * handle local variables with conflicting names * add I_GetWindowEvent() to win32 to only process window events when debugging is paused * dap: fix evaluate * dap: fix display of `out` variables --- .gitignore | 1 + CMakeLists.txt | 17 + docs/console.html | 8 + libraries/cppdap/.gitattributes | 4 + libraries/cppdap/.github/workflows/main.yml | 48 + libraries/cppdap/.gitignore | 6 + libraries/cppdap/CMakeLists.txt | 394 + libraries/cppdap/CONTRIBUTING | 28 + libraries/cppdap/LICENSE | 202 + libraries/cppdap/README.md | 79 + libraries/cppdap/clang-format-all.sh | 21 + libraries/cppdap/cmake/Config.cmake.in | 25 + libraries/cppdap/examples/hello_debugger.cpp | 469 + .../examples/simple_net_client_server.cpp | 109 + libraries/cppdap/examples/vscode/package.json | 28 + libraries/cppdap/fuzz/dictionary.txt | 372 + libraries/cppdap/fuzz/fuzz.cpp | 128 + libraries/cppdap/fuzz/fuzz.h | 76 + libraries/cppdap/fuzz/run.sh | 19 + libraries/cppdap/fuzz/seed/empty_json | 1 + libraries/cppdap/fuzz/seed/request | 8 + libraries/cppdap/include/dap/any.h | 211 + libraries/cppdap/include/dap/dap.h | 35 + libraries/cppdap/include/dap/future.h | 179 + libraries/cppdap/include/dap/io.h | 97 + libraries/cppdap/include/dap/network.h | 71 + libraries/cppdap/include/dap/optional.h | 263 + libraries/cppdap/include/dap/protocol.h | 2909 ++ libraries/cppdap/include/dap/serialization.h | 253 + libraries/cppdap/include/dap/session.h | 460 + libraries/cppdap/include/dap/traits.h | 159 + libraries/cppdap/include/dap/typeinfo.h | 59 + libraries/cppdap/include/dap/typeof.h | 266 + libraries/cppdap/include/dap/types.h | 104 + libraries/cppdap/include/dap/variant.h | 108 + .../cppdap/kokoro/license-check/build.sh | 25 + .../cppdap/kokoro/license-check/presubmit.cfg | 4 + .../macos/clang-x64/cmake/presubmit.cfg | 14 + libraries/cppdap/kokoro/macos/presubmit.sh | 37 + .../ubuntu/gcc-x64/cmake/asan/presubmit.cfg | 19 + .../kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg | 14 + .../ubuntu/gcc-x64/cmake/tsan/presubmit.cfg | 19 + .../cppdap/kokoro/ubuntu/presubmit-docker.sh | 56 + libraries/cppdap/kokoro/ubuntu/presubmit.sh | 32 + libraries/cppdap/kokoro/windows/presubmit.bat | 45 + .../windows/vs2022-amd64/cmake/presubmit.cfg | 19 + .../windows/vs2022-x86/cmake/presubmit.cfg | 19 + libraries/cppdap/license-checker.cfg | 28 + libraries/cppdap/src/any_test.cpp | 262 + libraries/cppdap/src/chan.h | 90 + libraries/cppdap/src/chan_test.cpp | 35 + libraries/cppdap/src/content_stream.cpp | 198 + libraries/cppdap/src/content_stream.h | 73 + libraries/cppdap/src/content_stream_test.cpp | 126 + libraries/cppdap/src/dap_test.cpp | 72 + libraries/cppdap/src/io.cpp | 258 + libraries/cppdap/src/json_serializer.h | 47 + libraries/cppdap/src/json_serializer_test.cpp | 266 + .../cppdap/src/jsoncpp_json_serializer.cpp | 272 + .../cppdap/src/jsoncpp_json_serializer.h | 134 + libraries/cppdap/src/network.cpp | 107 + libraries/cppdap/src/network_test.cpp | 110 + .../cppdap/src/nlohmann_json_serializer.cpp | 260 + .../cppdap/src/nlohmann_json_serializer.h | 133 + libraries/cppdap/src/null_json_serializer.cpp | 23 + libraries/cppdap/src/null_json_serializer.h | 47 + libraries/cppdap/src/optional_test.cpp | 169 + libraries/cppdap/src/protocol_events.cpp | 127 + libraries/cppdap/src/protocol_requests.cpp | 293 + libraries/cppdap/src/protocol_response.cpp | 262 + libraries/cppdap/src/protocol_types.cpp | 333 + .../cppdap/src/rapid_json_serializer.cpp | 290 + libraries/cppdap/src/rapid_json_serializer.h | 138 + libraries/cppdap/src/rwmutex.h | 172 + libraries/cppdap/src/rwmutex_test.cpp | 113 + libraries/cppdap/src/session.cpp | 521 + libraries/cppdap/src/session_test.cpp | 625 + libraries/cppdap/src/socket.cpp | 337 + libraries/cppdap/src/socket.h | 47 + libraries/cppdap/src/socket_test.cpp | 104 + libraries/cppdap/src/string_buffer.h | 95 + libraries/cppdap/src/traits_test.cpp | 387 + libraries/cppdap/src/typeinfo.cpp | 21 + libraries/cppdap/src/typeinfo_test.cpp | 65 + libraries/cppdap/src/typeof.cpp | 144 + libraries/cppdap/src/variant_test.cpp | 94 + .../third_party/json/.circleci/config.yml | 27 + libraries/cppdap/third_party/json/.clang-tidy | 26 + .../cppdap/third_party/json/.doozer.json | 82 + .../third_party/json/.github/CODEOWNERS | 6 + .../third_party/json/.github/CONTRIBUTING.md | 71 + .../third_party/json/.github/FUNDING.yml | 1 + .../json/.github/ISSUE_TEMPLATE/Bug_report.md | 22 + .../.github/ISSUE_TEMPLATE/Feature_request.md | 12 + .../json/.github/ISSUE_TEMPLATE/question.md | 16 + .../json/.github/PULL_REQUEST_TEMPLATE.md | 19 + .../third_party/json/.github/SECURITY.md | 5 + .../third_party/json/.github/config.yml | 19 + .../cppdap/third_party/json/.github/stale.yml | 17 + .../json/.github/workflows/ccpp.yml | 19 + libraries/cppdap/third_party/json/.gitignore | 25 + libraries/cppdap/third_party/json/.travis.yml | 345 + .../cppdap/third_party/json/CMakeLists.txt | 131 + .../third_party/json/CODE_OF_CONDUCT.md | 46 + .../cppdap/third_party/json/ChangeLog.md | 1670 ++ libraries/cppdap/third_party/json/LICENSE.MIT | 21 + libraries/cppdap/third_party/json/Makefile | 629 + libraries/cppdap/third_party/json/README.md | 1371 + .../cppdap/third_party/json/appveyor.yml | 111 + .../third_party/json/cmake/config.cmake.in | 15 + .../json/include/nlohmann/adl_serializer.hpp | 49 + .../nlohmann/detail/conversions/from_json.hpp | 389 + .../nlohmann/detail/conversions/to_chars.hpp | 1106 + .../nlohmann/detail/conversions/to_json.hpp | 347 + .../include/nlohmann/detail/exceptions.hpp | 356 + .../nlohmann/detail/input/binary_reader.hpp | 1983 ++ .../nlohmann/detail/input/input_adapters.hpp | 442 + .../nlohmann/detail/input/json_sax.hpp | 701 + .../include/nlohmann/detail/input/lexer.hpp | 1512 + .../include/nlohmann/detail/input/parser.hpp | 498 + .../nlohmann/detail/input/position_t.hpp | 27 + .../detail/iterators/internal_iterator.hpp | 25 + .../nlohmann/detail/iterators/iter_impl.hpp | 638 + .../detail/iterators/iteration_proxy.hpp | 176 + .../detail/iterators/iterator_traits.hpp | 51 + .../iterators/json_reverse_iterator.hpp | 119 + .../detail/iterators/primitive_iterator.hpp | 120 + .../include/nlohmann/detail/json_pointer.hpp | 1011 + .../json/include/nlohmann/detail/json_ref.hpp | 69 + .../include/nlohmann/detail/macro_scope.hpp | 121 + .../include/nlohmann/detail/macro_unscope.hpp | 21 + .../nlohmann/detail/meta/cpp_future.hpp | 63 + .../include/nlohmann/detail/meta/detected.hpp | 58 + .../include/nlohmann/detail/meta/is_sax.hpp | 142 + .../nlohmann/detail/meta/type_traits.hpp | 361 + .../include/nlohmann/detail/meta/void_t.hpp | 13 + .../nlohmann/detail/output/binary_writer.hpp | 1335 + .../detail/output/output_adapters.hpp | 123 + .../nlohmann/detail/output/serializer.hpp | 866 + .../json/include/nlohmann/detail/value_t.hpp | 77 + .../json/include/nlohmann/json.hpp | 8134 ++++++ .../json/include/nlohmann/json_fwd.hpp | 64 + .../nlohmann/thirdparty/hedley/hedley.hpp | 1595 ++ .../thirdparty/hedley/hedley_undef.hpp | 128 + libraries/cppdap/third_party/json/meson.build | 23 + .../third_party/json/nlohmann_json.natvis | 32 + .../json/single_include/nlohmann/json.hpp | 22815 ++++++++++++++++ .../json/third_party/amalgamate/CHANGES.md | 10 + .../json/third_party/amalgamate/LICENSE.md | 27 + .../json/third_party/amalgamate/README.md | 66 + .../json/third_party/amalgamate/amalgamate.py | 299 + .../json/third_party/amalgamate/config.json | 8 + .../json/third_party/cpplint/LICENSE | 27 + .../json/third_party/cpplint/README.rst | 80 + .../json/third_party/cpplint/cpplint.py | 6583 +++++ .../json/third_party/cpplint/update.sh | 5 + .../cppdap/tools/protocol_gen/protocol_gen.go | 982 + .../include/range_map/internal/rm_base.h | 160 + .../include/range_map/internal/rm_iter.h | 208 + .../include/range_map/internal/rm_node.h | 271 + .../range_map/include/range_map/range_map.h | 1342 + src/CMakeLists.txt | 37 +- src/common/console/c_console.cpp | 7 +- src/common/engine/i_net.cpp | 11 +- src/common/engine/printf.h | 1 + src/common/filesystem/include/fs_filesystem.h | 1 + src/common/filesystem/include/resourcefile.h | 5 + src/common/filesystem/source/filesystem.cpp | 18 + src/common/platform/win32/i_input.cpp | 84 +- src/common/platform/win32/i_input.h | 2 + src/common/scripting/backend/codegen.cpp | 5 + src/common/scripting/backend/vmbuilder.cpp | 13 + src/common/scripting/backend/vmbuilder.h | 8 +- src/common/scripting/core/vmdisasm.cpp | 8 +- .../scripting/dap/BreakpointManager.cpp | 492 + src/common/scripting/dap/BreakpointManager.h | 76 + .../scripting/dap/DebugExecutionManager.cpp | 388 + .../scripting/dap/DebugExecutionManager.h | 90 + src/common/scripting/dap/DebugServer.cpp | 161 + src/common/scripting/dap/DebugServer.h | 46 + src/common/scripting/dap/GameEventEmit.h | 15 + src/common/scripting/dap/GameInterfaces.h | 1159 + src/common/scripting/dap/IdHandleBase.cpp | 1 + src/common/scripting/dap/IdHandleBase.h | 20 + src/common/scripting/dap/IdMap.cpp | 1 + src/common/scripting/dap/IdMap.h | 101 + src/common/scripting/dap/IdProvider.cpp | 10 + src/common/scripting/dap/IdProvider.h | 13 + .../scripting/dap/Nodes/ArrayStateNode.cpp | 236 + .../scripting/dap/Nodes/ArrayStateNode.h | 28 + .../dap/Nodes/CVarScopeStateNode.cpp | 195 + .../scripting/dap/Nodes/CVarScopeStateNode.h | 48 + src/common/scripting/dap/Nodes/DummyNode.cpp | 49 + src/common/scripting/dap/Nodes/DummyNode.h | 30 + .../dap/Nodes/GlobalScopeStateNode.cpp | 141 + .../dap/Nodes/GlobalScopeStateNode.h | 20 + .../dap/Nodes/LocalScopeStateNode.cpp | 146 + .../scripting/dap/Nodes/LocalScopeStateNode.h | 27 + .../scripting/dap/Nodes/ObjectStateNode.cpp | 175 + .../scripting/dap/Nodes/ObjectStateNode.h | 31 + .../dap/Nodes/RegistersScopeStateNode.cpp | 261 + .../dap/Nodes/RegistersScopeStateNode.h | 135 + .../dap/Nodes/StackFrameStateNode.cpp | 139 + .../scripting/dap/Nodes/StackFrameStateNode.h | 35 + .../scripting/dap/Nodes/StackStateNode.cpp | 83 + .../scripting/dap/Nodes/StackStateNode.h | 22 + .../scripting/dap/Nodes/StateNodeBase.cpp | 43 + .../scripting/dap/Nodes/StateNodeBase.h | 67 + .../scripting/dap/Nodes/StatePointerNode.cpp | 260 + .../scripting/dap/Nodes/StatePointerNode.h | 21 + .../scripting/dap/Nodes/StructStateNode.cpp | 83 + .../scripting/dap/Nodes/StructStateNode.h | 25 + .../scripting/dap/Nodes/ValueStateNode.cpp | 243 + .../scripting/dap/Nodes/ValueStateNode.h | 20 + src/common/scripting/dap/PexCache.cpp | 949 + src/common/scripting/dap/PexCache.h | 135 + src/common/scripting/dap/Protocol/debugger.h | 77 + .../dap/Protocol/struct_extensions.cpp | 22 + .../dap/Protocol/struct_extensions.h | 33 + src/common/scripting/dap/RuntimeEvents.cpp | 86 + src/common/scripting/dap/RuntimeEvents.h | 38 + src/common/scripting/dap/RuntimeState.cpp | 302 + src/common/scripting/dap/RuntimeState.h | 35 + src/common/scripting/dap/Utilities.h | 275 + src/common/scripting/dap/ZScriptDebugger.cpp | 807 + src/common/scripting/dap/ZScriptDebugger.h | 104 + src/common/scripting/frontend/zcc_compile.cpp | 1 + src/common/scripting/vm/vmexec.cpp | 3 +- src/common/scripting/vm/vmexec.h | 13 +- src/common/scripting/vm/vmframe.cpp | 25 +- src/common/scripting/vm/vmintern.h | 22 +- src/common/utility/tarray.h | 111 +- src/d_main.cpp | 58 +- src/gamedata/d_dehacked.cpp | 32 +- vcpkg.json | 2 +- 235 files changed, 83166 insertions(+), 62 deletions(-) create mode 100644 libraries/cppdap/.gitattributes create mode 100644 libraries/cppdap/.github/workflows/main.yml create mode 100644 libraries/cppdap/.gitignore create mode 100644 libraries/cppdap/CMakeLists.txt create mode 100644 libraries/cppdap/CONTRIBUTING create mode 100644 libraries/cppdap/LICENSE create mode 100644 libraries/cppdap/README.md create mode 100755 libraries/cppdap/clang-format-all.sh create mode 100644 libraries/cppdap/cmake/Config.cmake.in create mode 100644 libraries/cppdap/examples/hello_debugger.cpp create mode 100644 libraries/cppdap/examples/simple_net_client_server.cpp create mode 100644 libraries/cppdap/examples/vscode/package.json create mode 100644 libraries/cppdap/fuzz/dictionary.txt create mode 100644 libraries/cppdap/fuzz/fuzz.cpp create mode 100644 libraries/cppdap/fuzz/fuzz.h create mode 100755 libraries/cppdap/fuzz/run.sh create mode 100644 libraries/cppdap/fuzz/seed/empty_json create mode 100644 libraries/cppdap/fuzz/seed/request create mode 100644 libraries/cppdap/include/dap/any.h create mode 100644 libraries/cppdap/include/dap/dap.h create mode 100644 libraries/cppdap/include/dap/future.h create mode 100644 libraries/cppdap/include/dap/io.h create mode 100644 libraries/cppdap/include/dap/network.h create mode 100644 libraries/cppdap/include/dap/optional.h create mode 100644 libraries/cppdap/include/dap/protocol.h create mode 100644 libraries/cppdap/include/dap/serialization.h create mode 100644 libraries/cppdap/include/dap/session.h create mode 100644 libraries/cppdap/include/dap/traits.h create mode 100644 libraries/cppdap/include/dap/typeinfo.h create mode 100644 libraries/cppdap/include/dap/typeof.h create mode 100644 libraries/cppdap/include/dap/types.h create mode 100644 libraries/cppdap/include/dap/variant.h create mode 100755 libraries/cppdap/kokoro/license-check/build.sh create mode 100644 libraries/cppdap/kokoro/license-check/presubmit.cfg create mode 100644 libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg create mode 100755 libraries/cppdap/kokoro/macos/presubmit.sh create mode 100644 libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/asan/presubmit.cfg create mode 100644 libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg create mode 100644 libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/tsan/presubmit.cfg create mode 100755 libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh create mode 100755 libraries/cppdap/kokoro/ubuntu/presubmit.sh create mode 100644 libraries/cppdap/kokoro/windows/presubmit.bat create mode 100644 libraries/cppdap/kokoro/windows/vs2022-amd64/cmake/presubmit.cfg create mode 100644 libraries/cppdap/kokoro/windows/vs2022-x86/cmake/presubmit.cfg create mode 100644 libraries/cppdap/license-checker.cfg create mode 100644 libraries/cppdap/src/any_test.cpp create mode 100644 libraries/cppdap/src/chan.h create mode 100644 libraries/cppdap/src/chan_test.cpp create mode 100644 libraries/cppdap/src/content_stream.cpp create mode 100644 libraries/cppdap/src/content_stream.h create mode 100644 libraries/cppdap/src/content_stream_test.cpp create mode 100644 libraries/cppdap/src/dap_test.cpp create mode 100644 libraries/cppdap/src/io.cpp create mode 100644 libraries/cppdap/src/json_serializer.h create mode 100644 libraries/cppdap/src/json_serializer_test.cpp create mode 100644 libraries/cppdap/src/jsoncpp_json_serializer.cpp create mode 100644 libraries/cppdap/src/jsoncpp_json_serializer.h create mode 100644 libraries/cppdap/src/network.cpp create mode 100644 libraries/cppdap/src/network_test.cpp create mode 100644 libraries/cppdap/src/nlohmann_json_serializer.cpp create mode 100644 libraries/cppdap/src/nlohmann_json_serializer.h create mode 100644 libraries/cppdap/src/null_json_serializer.cpp create mode 100644 libraries/cppdap/src/null_json_serializer.h create mode 100644 libraries/cppdap/src/optional_test.cpp create mode 100644 libraries/cppdap/src/protocol_events.cpp create mode 100644 libraries/cppdap/src/protocol_requests.cpp create mode 100644 libraries/cppdap/src/protocol_response.cpp create mode 100644 libraries/cppdap/src/protocol_types.cpp create mode 100644 libraries/cppdap/src/rapid_json_serializer.cpp create mode 100644 libraries/cppdap/src/rapid_json_serializer.h create mode 100644 libraries/cppdap/src/rwmutex.h create mode 100644 libraries/cppdap/src/rwmutex_test.cpp create mode 100644 libraries/cppdap/src/session.cpp create mode 100644 libraries/cppdap/src/session_test.cpp create mode 100644 libraries/cppdap/src/socket.cpp create mode 100644 libraries/cppdap/src/socket.h create mode 100644 libraries/cppdap/src/socket_test.cpp create mode 100644 libraries/cppdap/src/string_buffer.h create mode 100644 libraries/cppdap/src/traits_test.cpp create mode 100644 libraries/cppdap/src/typeinfo.cpp create mode 100644 libraries/cppdap/src/typeinfo_test.cpp create mode 100644 libraries/cppdap/src/typeof.cpp create mode 100644 libraries/cppdap/src/variant_test.cpp create mode 100644 libraries/cppdap/third_party/json/.circleci/config.yml create mode 100644 libraries/cppdap/third_party/json/.clang-tidy create mode 100644 libraries/cppdap/third_party/json/.doozer.json create mode 100644 libraries/cppdap/third_party/json/.github/CODEOWNERS create mode 100644 libraries/cppdap/third_party/json/.github/CONTRIBUTING.md create mode 100644 libraries/cppdap/third_party/json/.github/FUNDING.yml create mode 100644 libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md create mode 100644 libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md create mode 100644 libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md create mode 100644 libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 libraries/cppdap/third_party/json/.github/SECURITY.md create mode 100644 libraries/cppdap/third_party/json/.github/config.yml create mode 100644 libraries/cppdap/third_party/json/.github/stale.yml create mode 100644 libraries/cppdap/third_party/json/.github/workflows/ccpp.yml create mode 100644 libraries/cppdap/third_party/json/.gitignore create mode 100644 libraries/cppdap/third_party/json/.travis.yml create mode 100644 libraries/cppdap/third_party/json/CMakeLists.txt create mode 100644 libraries/cppdap/third_party/json/CODE_OF_CONDUCT.md create mode 100644 libraries/cppdap/third_party/json/ChangeLog.md create mode 100644 libraries/cppdap/third_party/json/LICENSE.MIT create mode 100644 libraries/cppdap/third_party/json/Makefile create mode 100644 libraries/cppdap/third_party/json/README.md create mode 100644 libraries/cppdap/third_party/json/appveyor.yml create mode 100644 libraries/cppdap/third_party/json/cmake/config.cmake.in create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/adl_serializer.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/from_json.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_chars.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_json.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/exceptions.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/binary_reader.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/input_adapters.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/json_sax.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/lexer.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/parser.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/input/position_t.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/internal_iterator.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iter_impl.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iteration_proxy.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iterator_traits.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/primitive_iterator.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/json_pointer.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/json_ref.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/macro_scope.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/macro_unscope.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/meta/cpp_future.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/meta/detected.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/meta/is_sax.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/meta/type_traits.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/meta/void_t.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/output/binary_writer.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/output/output_adapters.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/output/serializer.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/detail/value_t.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/json.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/json_fwd.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/thirdparty/hedley/hedley.hpp create mode 100644 libraries/cppdap/third_party/json/include/nlohmann/thirdparty/hedley/hedley_undef.hpp create mode 100644 libraries/cppdap/third_party/json/meson.build create mode 100644 libraries/cppdap/third_party/json/nlohmann_json.natvis create mode 100644 libraries/cppdap/third_party/json/single_include/nlohmann/json.hpp create mode 100644 libraries/cppdap/third_party/json/third_party/amalgamate/CHANGES.md create mode 100644 libraries/cppdap/third_party/json/third_party/amalgamate/LICENSE.md create mode 100644 libraries/cppdap/third_party/json/third_party/amalgamate/README.md create mode 100755 libraries/cppdap/third_party/json/third_party/amalgamate/amalgamate.py create mode 100644 libraries/cppdap/third_party/json/third_party/amalgamate/config.json create mode 100644 libraries/cppdap/third_party/json/third_party/cpplint/LICENSE create mode 100644 libraries/cppdap/third_party/json/third_party/cpplint/README.rst create mode 100755 libraries/cppdap/third_party/json/third_party/cpplint/cpplint.py create mode 100755 libraries/cppdap/third_party/json/third_party/cpplint/update.sh create mode 100644 libraries/cppdap/tools/protocol_gen/protocol_gen.go create mode 100644 libraries/range_map/include/range_map/internal/rm_base.h create mode 100644 libraries/range_map/include/range_map/internal/rm_iter.h create mode 100644 libraries/range_map/include/range_map/internal/rm_node.h create mode 100644 libraries/range_map/include/range_map/range_map.h create mode 100644 src/common/scripting/dap/BreakpointManager.cpp create mode 100644 src/common/scripting/dap/BreakpointManager.h create mode 100644 src/common/scripting/dap/DebugExecutionManager.cpp create mode 100644 src/common/scripting/dap/DebugExecutionManager.h create mode 100644 src/common/scripting/dap/DebugServer.cpp create mode 100644 src/common/scripting/dap/DebugServer.h create mode 100644 src/common/scripting/dap/GameEventEmit.h create mode 100644 src/common/scripting/dap/GameInterfaces.h create mode 100644 src/common/scripting/dap/IdHandleBase.cpp create mode 100644 src/common/scripting/dap/IdHandleBase.h create mode 100644 src/common/scripting/dap/IdMap.cpp create mode 100644 src/common/scripting/dap/IdMap.h create mode 100644 src/common/scripting/dap/IdProvider.cpp create mode 100644 src/common/scripting/dap/IdProvider.h create mode 100644 src/common/scripting/dap/Nodes/ArrayStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/ArrayStateNode.h create mode 100644 src/common/scripting/dap/Nodes/CVarScopeStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/CVarScopeStateNode.h create mode 100644 src/common/scripting/dap/Nodes/DummyNode.cpp create mode 100644 src/common/scripting/dap/Nodes/DummyNode.h create mode 100644 src/common/scripting/dap/Nodes/GlobalScopeStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/GlobalScopeStateNode.h create mode 100644 src/common/scripting/dap/Nodes/LocalScopeStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/LocalScopeStateNode.h create mode 100644 src/common/scripting/dap/Nodes/ObjectStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/ObjectStateNode.h create mode 100644 src/common/scripting/dap/Nodes/RegistersScopeStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/RegistersScopeStateNode.h create mode 100644 src/common/scripting/dap/Nodes/StackFrameStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/StackFrameStateNode.h create mode 100644 src/common/scripting/dap/Nodes/StackStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/StackStateNode.h create mode 100644 src/common/scripting/dap/Nodes/StateNodeBase.cpp create mode 100644 src/common/scripting/dap/Nodes/StateNodeBase.h create mode 100644 src/common/scripting/dap/Nodes/StatePointerNode.cpp create mode 100644 src/common/scripting/dap/Nodes/StatePointerNode.h create mode 100644 src/common/scripting/dap/Nodes/StructStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/StructStateNode.h create mode 100644 src/common/scripting/dap/Nodes/ValueStateNode.cpp create mode 100644 src/common/scripting/dap/Nodes/ValueStateNode.h create mode 100644 src/common/scripting/dap/PexCache.cpp create mode 100644 src/common/scripting/dap/PexCache.h create mode 100644 src/common/scripting/dap/Protocol/debugger.h create mode 100644 src/common/scripting/dap/Protocol/struct_extensions.cpp create mode 100644 src/common/scripting/dap/Protocol/struct_extensions.h create mode 100644 src/common/scripting/dap/RuntimeEvents.cpp create mode 100644 src/common/scripting/dap/RuntimeEvents.h create mode 100644 src/common/scripting/dap/RuntimeState.cpp create mode 100644 src/common/scripting/dap/RuntimeState.h create mode 100644 src/common/scripting/dap/Utilities.h create mode 100644 src/common/scripting/dap/ZScriptDebugger.cpp create mode 100644 src/common/scripting/dap/ZScriptDebugger.h diff --git a/.gitignore b/.gitignore index 3a3b7cc3d..d8d361a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ fmodapi*linux/ /src/gl/unused /mapfiles_release/*.map /AppDir +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1088d2483..ba2d6f9ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,9 @@ option( NO_OPENAL "Disable OpenAL sound support" OFF ) find_package( BZip2 ) find_package( VPX ) +if (NOT FORCE_INTERNAL_CPPDAP) + find_package( cppdap CONFIG ) +endif() include( TargetArch ) @@ -334,6 +337,9 @@ set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) option(FORCE_INTERNAL_BZIP2 "Use internal bzip2") option(FORCE_INTERNAL_ASMJIT "Use internal asmjit" ON) +option(FORCE_INTERNAL_CPPDAP "Use internal cppdap" ON) + + mark_as_advanced( FORCE_INTERNAL_ASMJIT ) if (HAVE_VULKAN) @@ -385,6 +391,17 @@ else() set( BZIP2_LIBRARY bz2 ) endif() +if ( CPPDAP_FOUND AND NOT FORCE_INTERNAL_CPPDAP ) + message( STATUS "Using system cppdap library, includes found at ${CPPDAP_INCLUDE_DIR}" ) +else() + message( STATUS "Using internal cppdap library" ) + add_subdirectory( libraries/cppdap ) + set( CPPDAP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/cppdap" ) + set( CPPDAP_LIBRARIES cppdap ) + set( CPPDAP_LIBRARY cppdap ) + set( CPPDAP_FOUND TRUE ) +endif() + set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/lzma/C" ) if( NOT CMAKE_CROSSCOMPILING ) diff --git a/docs/console.html b/docs/console.html index 7fb5d4c8c..4dd6a0fe5 100644 --- a/docs/console.html +++ b/docs/console.html @@ -1814,6 +1814,14 @@ default: 1.0
Sends you to the specified coordinates immediately. This can be used with idmypos to debug problems in a specific area of a map.
+
vm_debug
+
boolean: false
+
When set to true, the game will enable the ZScript debug + server on the port specified in vm_debug_port. Note that enabling this + will disable JIT and requires restarting the game.
+
vm_debug_port
+
integer: 19021
+
This is the port that the ZScript debug server will listen on.

Effects

diff --git a/libraries/cppdap/.gitattributes b/libraries/cppdap/.gitattributes new file mode 100644 index 000000000..175297ae8 --- /dev/null +++ b/libraries/cppdap/.gitattributes @@ -0,0 +1,4 @@ +* text=auto + +*.sh eol=lf +*.bat eol=crlf diff --git a/libraries/cppdap/.github/workflows/main.yml b/libraries/cppdap/.github/workflows/main.yml new file mode 100644 index 000000000..72d2934e9 --- /dev/null +++ b/libraries/cppdap/.github/workflows/main.yml @@ -0,0 +1,48 @@ +on: + push: + pull_request: + +jobs: + linux: + name: ci + runs-on: ubuntu-latest + + container: + image: ubuntu:22.04 + + strategy: + matrix: + include: + - CC: gcc + CXX: g++ + - CC: clang + CXX: clang++ + + # don't run pull requests from local branches twice + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository + + env: + DEBIAN_FRONTEND: noninteractive + + steps: + - name: Install packages + run: | + apt-get -q -y update + apt-get -q -y install build-essential cmake git clang + + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Build source + run: | + mkdir -p build + cd build + CC=${{ matrix.CC }} CXX=${{ matrix.CXX }} cmake .. + cmake --build . + DESTDIR=../out cmake --install . + + - uses: actions/upload-artifact@v4 + with: + name: cppdap-${{ matrix.CC }} + path: out/usr/local/ diff --git a/libraries/cppdap/.gitignore b/libraries/cppdap/.gitignore new file mode 100644 index 000000000..af8d40aae --- /dev/null +++ b/libraries/cppdap/.gitignore @@ -0,0 +1,6 @@ +build/ +fuzz/corpus +fuzz/logs +.vs/ +.vscode/settings.json +CMakeSettings.json diff --git a/libraries/cppdap/CMakeLists.txt b/libraries/cppdap/CMakeLists.txt new file mode 100644 index 000000000..a20152d77 --- /dev/null +++ b/libraries/cppdap/CMakeLists.txt @@ -0,0 +1,394 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.22) + +project(cppdap VERSION 1.68.0 LANGUAGES CXX C) + +set (CMAKE_CXX_STANDARD 11) + +include(GNUInstallDirs) + +########################################################### +# Options +########################################################### +function (option_if_not_defined name description default) + if(NOT DEFINED ${name}) + option(${name} ${description} ${default}) + endif() +endfunction() + +option_if_not_defined(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) +option_if_not_defined(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF) +option_if_not_defined(CPPDAP_BUILD_TESTS "Build tests" OFF) +option_if_not_defined(CPPDAP_BUILD_FUZZER "Build fuzzer" OFF) +option_if_not_defined(CPPDAP_ASAN "Build dap with address sanitizer" OFF) +option_if_not_defined(CPPDAP_MSAN "Build dap with memory sanitizer" OFF) +option_if_not_defined(CPPDAP_TSAN "Build dap with thread sanitizer" OFF) +option_if_not_defined(CPPDAP_INSTALL_VSCODE_EXAMPLES "Build and install dap examples into vscode extensions directory" OFF) +option_if_not_defined(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE "Use nlohmann_json with find_package() instead of building internal submodule" OFF) +option_if_not_defined(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE "Use RapidJSON with find_package()" OFF) +option_if_not_defined(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE "Use JsonCpp with find_package()" OFF) +option_if_not_defined(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE "Use googletest with find_package()" OFF) + +########################################################### +# Directories +########################################################### +function (set_if_not_defined name value) + if(NOT DEFINED ${name}) + set(${name} ${value} PARENT_SCOPE) + endif() +endfunction() + +set(CPPDAP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(CPPDAP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set_if_not_defined(CPPDAP_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party) +set_if_not_defined(CPPDAP_JSON_DIR ${CPPDAP_THIRD_PARTY_DIR}/json) +set_if_not_defined(CPPDAP_GOOGLETEST_DIR ${CPPDAP_THIRD_PARTY_DIR}/googletest) + +########################################################### +# Submodules +########################################################### +if(CPPDAP_BUILD_TESTS AND NOT CPPDAP_USE_EXTERNAL_GTEST_PACKAGE) + if(NOT EXISTS ${CPPDAP_GOOGLETEST_DIR}/.git) + message(WARNING "third_party/googletest submodule missing.") + message(WARNING "Run: `git submodule update --init` to build tests.") + set(CPPDAP_BUILD_TESTS OFF) + endif() +endif() + +########################################################### +# JSON library +########################################################### + +if(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE) + list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE") +endif() +if(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE) + list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE") +endif() +if(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE) + list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE") +endif() +list(LENGTH CPPDAP_EXTERNAL_JSON_PACKAGES CPPDAP_EXTERNAL_JSON_PACKAGES_COUNT) + +if(CPPDAP_EXTERNAL_JSON_PACKAGES_COUNT GREATER 1) + message(FATAL_ERROR "At most one of CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE, \ +CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE, and CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE can \ +be set, but ${CPPDAP_EXTERNAL_JSON_PACKAGES} were all set.") +elseif(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE) + find_package(nlohmann_json CONFIG REQUIRED) + set(CPPDAP_JSON_LIBRARY "nlohmann") +elseif(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE) + find_package(RapidJSON CONFIG REQUIRED) + set(CPPDAP_JSON_LIBRARY "rapid") +elseif(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE) + find_package(jsoncpp CONFIG REQUIRED) + set(CPPDAP_JSON_LIBRARY "jsoncpp") +endif() + +if(NOT DEFINED CPPDAP_JSON_LIBRARY) + # Attempt to detect JSON library from CPPDAP_JSON_DIR + if(NOT EXISTS "${CPPDAP_JSON_DIR}") + message(FATAL_ERROR "CPPDAP_JSON_DIR '${CPPDAP_JSON_DIR}' does not exist") + endif() + + if(EXISTS "${CPPDAP_JSON_DIR}/include/nlohmann") + set(CPPDAP_JSON_LIBRARY "nlohmann") + elseif(EXISTS "${CPPDAP_JSON_DIR}/include/rapidjson") + set(CPPDAP_JSON_LIBRARY "rapid") + elseif(EXISTS "${CPPDAP_JSON_DIR}/include/json") + set(CPPDAP_JSON_LIBRARY "jsoncpp") + else() + message(FATAL_ERROR "Could not determine JSON library from ${CPPDAP_JSON_DIR}") + endif() +endif() +string(TOUPPER ${CPPDAP_JSON_LIBRARY} CPPDAP_JSON_LIBRARY_UPPER) + +########################################################### +# File lists +########################################################### +set(CPPDAP_LIST + ${CPPDAP_SRC_DIR}/content_stream.cpp + ${CPPDAP_SRC_DIR}/io.cpp + ${CPPDAP_SRC_DIR}/${CPPDAP_JSON_LIBRARY}_json_serializer.cpp + ${CPPDAP_SRC_DIR}/network.cpp + ${CPPDAP_SRC_DIR}/null_json_serializer.cpp + ${CPPDAP_SRC_DIR}/protocol_events.cpp + ${CPPDAP_SRC_DIR}/protocol_requests.cpp + ${CPPDAP_SRC_DIR}/protocol_response.cpp + ${CPPDAP_SRC_DIR}/protocol_types.cpp + ${CPPDAP_SRC_DIR}/session.cpp + ${CPPDAP_SRC_DIR}/socket.cpp + ${CPPDAP_SRC_DIR}/typeinfo.cpp + ${CPPDAP_SRC_DIR}/typeof.cpp +) + +########################################################### +# OS libraries +########################################################### +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + set(CPPDAP_OS_LIBS WS2_32) + set(CPPDAP_OS_EXE_EXT ".exe") +elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(CPPDAP_OS_LIBS pthread) + set(CPPDAP_OS_EXE_EXT "") +elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(CPPDAP_OS_LIBS) + set(CPPDAP_OS_EXE_EXT "") +endif() + +########################################################### +# Functions +########################################################### + +function(cppdap_set_json_links target) + if (CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE) + target_link_libraries(${target} PRIVATE "$") + elseif(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE) + target_link_libraries(${target} PRIVATE rapidjson) + elseif(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE) + target_link_libraries(${target} PRIVATE JsonCpp::JsonCpp) + else() + target_include_directories(${target} PRIVATE "${CPPDAP_JSON_DIR}/include/") + endif() +endfunction(cppdap_set_json_links) + +function(cppdap_set_target_options target) + # Enable all warnings + if(MSVC) + target_compile_options(${target} PRIVATE "-W4") + else() + target_compile_options(${target} PRIVATE "-Wall") + endif() + + # Disable specific, pedantic warnings + if(MSVC) + target_compile_options(${target} PRIVATE + "-D_CRT_SECURE_NO_WARNINGS" + + # Warnings from nlohmann/json headers. + "/wd4267" # 'argument': conversion from 'size_t' to 'int', possible loss of data + ) + endif() + + # Add define for JSON library in use + target_compile_definitions(${target} + PRIVATE "CPPDAP_JSON_${CPPDAP_JSON_LIBRARY_UPPER}=1" + ) + + # Treat all warnings as errors + if(CPPDAP_WARNINGS_AS_ERRORS) + if(MSVC) + target_compile_options(${target} PRIVATE "/WX") + else() + target_compile_options(${target} PRIVATE "-Werror") + endif() + endif(CPPDAP_WARNINGS_AS_ERRORS) + + if(CPPDAP_ASAN) + target_compile_options(${target} PUBLIC "-fsanitize=address") + target_link_options(${target} PUBLIC "-fsanitize=address") + elseif(CPPDAP_MSAN) + target_compile_options(${target} PUBLIC "-fsanitize=memory") + target_link_options(${target} PUBLIC "-fsanitize=memory") + elseif(CPPDAP_TSAN) + target_compile_options(${target} PUBLIC "-fsanitize=thread") + target_link_options(${target} PUBLIC "-fsanitize=thread") + endif() + + # Error on undefined symbols + # if(NOT MSVC) + # target_compile_options(${target} PRIVATE "-Wl,--no-undefined") + # endif() + target_include_directories( + "${target}" + PUBLIC + "$" + "$" + ) + cppdap_set_json_links(${target}) + target_link_libraries(${target} PRIVATE ${CPPDAP_OS_LIBS}) +endfunction(cppdap_set_target_options) + +########################################################### +# Targets +########################################################### + +# dap +add_library(cppdap ${CPPDAP_LIST}) +set_target_properties(cppdap PROPERTIES POSITION_INDEPENDENT_CODE 1) + +cppdap_set_target_options(cppdap) + +set(CPPDAP_TARGET_NAME ${PROJECT_NAME}) +set(CPPDAP_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "") +set(CPPDAP_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") +set(CPPDAP_TARGETS_EXPORT_NAME "${PROJECT_NAME}-targets") +set(CPPDAP_CMAKE_CONFIG_TEMPLATE "cmake/Config.cmake.in") +set(CPPDAP_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}") +set(CPPDAP_CMAKE_VERSION_CONFIG_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake") +set(CPPDAP_CMAKE_PROJECT_CONFIG_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake") +set(CPPDAP_CMAKE_PROJECT_TARGETS_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}-targets.cmake") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${CPPDAP_CMAKE_VERSION_CONFIG_FILE} COMPATIBILITY SameMinorVersion +) +configure_package_config_file( + ${CPPDAP_CMAKE_CONFIG_TEMPLATE} + "${CPPDAP_CMAKE_PROJECT_CONFIG_FILE}" + INSTALL_DESTINATION ${CPPDAP_CONFIG_INSTALL_DIR} +) + +install( + TARGETS "${CPPDAP_TARGET_NAME}" + EXPORT "${CPPDAP_TARGETS_EXPORT_NAME}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CPPDAP_INCLUDE_INSTALL_DIR}" +) + +install( + EXPORT "${CPPDAP_TARGETS_EXPORT_NAME}" + NAMESPACE "${CPPDAP_TARGET_NAME}::" + DESTINATION "${CPPDAP_CONFIG_INSTALL_DIR}" +) + +install( + DIRECTORY + "include/dap" + DESTINATION "${CPPDAP_INCLUDE_INSTALL_DIR}" +) +install(FILES ${CPPDAP_CMAKE_VERSION_CONFIG_FILE} ${CPPDAP_CMAKE_PROJECT_CONFIG_FILE} +DESTINATION ${CPPDAP_CONFIG_INSTALL_DIR}) + +# tests +if(CPPDAP_BUILD_TESTS) + enable_testing() + + set(DAP_TEST_LIST + ${CPPDAP_SRC_DIR}/any_test.cpp + ${CPPDAP_SRC_DIR}/chan_test.cpp + ${CPPDAP_SRC_DIR}/content_stream_test.cpp + ${CPPDAP_SRC_DIR}/dap_test.cpp + ${CPPDAP_SRC_DIR}/json_serializer_test.cpp + ${CPPDAP_SRC_DIR}/network_test.cpp + ${CPPDAP_SRC_DIR}/optional_test.cpp + ${CPPDAP_SRC_DIR}/rwmutex_test.cpp + ${CPPDAP_SRC_DIR}/session_test.cpp + ${CPPDAP_SRC_DIR}/socket_test.cpp + ${CPPDAP_SRC_DIR}/traits_test.cpp + ${CPPDAP_SRC_DIR}/typeinfo_test.cpp + ${CPPDAP_SRC_DIR}/variant_test.cpp + ) + + if(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE) + find_package(GTest REQUIRED) + else() + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + add_subdirectory(${CPPDAP_GOOGLETEST_DIR}) + # googletest has -Werror=maybe-uninitialized problems. + # Disable all warnings in googletest code. + target_compile_options(gtest PRIVATE -w) + # gmock has -Werror=deprecated-copy problems. + target_compile_options(gmock PRIVATE -w) + endif() + + add_executable(cppdap-unittests ${DAP_TEST_LIST}) + add_test(NAME cppdap-unittests COMMAND cppdap-unittests) + + set_target_properties(cppdap-unittests PROPERTIES + FOLDER "Tests" + ) + + if(MSVC) + # googletest emits warning C4244: 'initializing': conversion from 'double' to 'testing::internal::BiggestInt', possible loss of data + target_compile_options(cppdap-unittests PRIVATE "/wd4244") + endif() + + cppdap_set_target_options(cppdap-unittests) + if(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE) + target_link_libraries(cppdap-unittests PRIVATE cppdap GTest::gtest) + else() + target_link_libraries(cppdap-unittests PRIVATE cppdap gtest gmock) + endif() +endif(CPPDAP_BUILD_TESTS) + +# fuzzer +if(CPPDAP_BUILD_FUZZER) + if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + message(FATAL_ERROR "CPPDAP_BUILD_FUZZER can currently only be used with the clang toolchain") + endif() + set(DAP_FUZZER_LIST + ${CPPDAP_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/fuzz/fuzz.cpp + ) + add_executable(cppdap-fuzzer ${DAP_FUZZER_LIST}) + if(CPPDAP_ASAN) + target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address") + target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address") + elseif(CPPDAP_MSAN) + target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,memory") + target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,memory") + elseif(CPPDAP_TSAN) + target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,thread") + target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,thread") + else() + target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer") + target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer") + endif() + target_include_directories(cppdap-fuzzer PUBLIC + ${CPPDAP_INCLUDE_DIR} + ${CPPDAP_SRC_DIR} + ) + cppdap_set_json_links(cppdap-fuzzer) + target_link_libraries(cppdap-fuzzer PRIVATE cppdap "${CPPDAP_OS_LIBS}") + +endif(CPPDAP_BUILD_FUZZER) + +# examples +if(CPPDAP_BUILD_EXAMPLES) + function(build_example target) + add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp") + set_target_properties(${target} PROPERTIES + FOLDER "Examples" + ) + cppdap_set_target_options(${target}) + target_link_libraries(${target} PRIVATE cppdap) + + if(CPPDAP_INSTALL_VSCODE_EXAMPLES) + if(CMAKE_SYSTEM_NAME MATCHES "Windows") + set(extroot "$ENV{USERPROFILE}\\.vscode\\extensions") + else() + set(extroot "$ENV{HOME}/.vscode/extensions") + endif() + if(EXISTS ${extroot}) + set(extdir "${extroot}/google.cppdap-example-${target}-1.0.0") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examples/vscode/package.json ${extdir}/package.json) + add_custom_command(TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${extdir}) + else() + message(WARNING "Could not install vscode example extension as '${extroot}' does not exist") + endif() + endif(CPPDAP_INSTALL_VSCODE_EXAMPLES) + endfunction(build_example) + + build_example(hello_debugger) + build_example(simple_net_client_server) + +endif(CPPDAP_BUILD_EXAMPLES) diff --git a/libraries/cppdap/CONTRIBUTING b/libraries/cppdap/CONTRIBUTING new file mode 100644 index 000000000..9a86ba025 --- /dev/null +++ b/libraries/cppdap/CONTRIBUTING @@ -0,0 +1,28 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). diff --git a/libraries/cppdap/LICENSE b/libraries/cppdap/LICENSE new file mode 100644 index 000000000..7a4a3ea24 --- /dev/null +++ b/libraries/cppdap/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/libraries/cppdap/README.md b/libraries/cppdap/README.md new file mode 100644 index 000000000..cd45bed78 --- /dev/null +++ b/libraries/cppdap/README.md @@ -0,0 +1,79 @@ +# cppdap + +## About + +`cppdap` is a C++11 library (["SDK"](https://microsoft.github.io/debug-adapter-protocol/implementors/sdks/)) implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/), providing an API for implementing a DAP client or server. + +`cppdap` provides C++ type-safe structures for the full [DAP specification](https://microsoft.github.io/debug-adapter-protocol/specification), and provides a simple way to add custom protocol messages. + +## Fetching dependencies + +`cppdap` provides CMake build files to build the library, unit tests and examples. + +`cppdap` depends on the [`nlohmann/json` library](https://github.com/nlohmann/json), and the unit tests depend on the [`googletest` library](https://github.com/google/googletest). Both are referenced as a git submodules. + +Before building, fetch the git submodules with: + +```bash +cd +git submodule update --init +``` + +Alternatively, `cppdap` can use the [`RapidJSON` library](https://rapidjson.org/) or the [`JsonCpp` library](https://github.com/open-source-parsers/jsoncpp) for JSON serialization. Use the `CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE`, `CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE`, and `CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE` CMake cache variables to select which library to use. + +## Building + +### Linux and macOS + +Next, generate the build files: + +```bash +cd +mkdir build +cd build +cmake .. +``` + +You may wish to suffix the `cmake ..` line with any of the following flags: + +* `-DCPPDAP_BUILD_TESTS=1` - Builds the `cppdap` unit tests +* `-DCPPDAP_BUILD_EXAMPLES=1` - Builds the `cppdap` examples +* `-DCPPDAP_INSTALL_VSCODE_EXAMPLES=1` - Installs the `cppdap` examples as Visual Studio Code extensions +* `-DCPPDAP_WARNINGS_AS_ERRORS=1` - Treats all compiler warnings as errors. + +Finally, build the project: + +`make` + +### Windows + +`cppdap` can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019). + + +### Using `cppdap` in your CMake project + +You can build and link `cppdap` using `add_subdirectory()` in your project's `CMakeLists.txt` file: +```cmake +set(CPPDAP_DIR ) # example : "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cppdap" +add_subdirectory(${CPPDAP_DIR}) +``` + +This will define the `cppdap` library target, which you can pass to `target_link_libraries()`: + +```cmake +target_link_libraries( cppdap) # replace with the name of your project's target +``` + +You may also wish to specify your own paths to the third party libraries used by `cppdap`. +You can do this by setting any of the following variables before the call to `add_subdirectory()`: + +```cmake +set(CPPDAP_THIRD_PARTY_DIR ) # defaults to ${CPPDAP_DIR}/third_party +set(CPPDAP_JSON_DIR ) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/json +set(CPPDAP_GOOGLETEST_DIR ) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/googletest +add_subdirectory(${CPPDAP_DIR}) +``` + +--- + +Note: This is not an officially supported Google product diff --git a/libraries/cppdap/clang-format-all.sh b/libraries/cppdap/clang-format-all.sh new file mode 100755 index 000000000..8bfb593b9 --- /dev/null +++ b/libraries/cppdap/clang-format-all.sh @@ -0,0 +1,21 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +SRC_DIR=${ROOT_DIR}/src +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} + +# Double clang-format, as it seems that one pass isn't always enough +find ${SRC_DIR} -iname "*.h" -o -iname "*.cpp" | xargs ${CLANG_FORMAT} -i -style=file +find ${SRC_DIR} -iname "*.h" -o -iname "*.cpp" | xargs ${CLANG_FORMAT} -i -style=file diff --git a/libraries/cppdap/cmake/Config.cmake.in b/libraries/cppdap/cmake/Config.cmake.in new file mode 100644 index 000000000..384aa3f50 --- /dev/null +++ b/libraries/cppdap/cmake/Config.cmake.in @@ -0,0 +1,25 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@PACKAGE_INIT@ +include(CMakeFindDependencyMacro) + +include("${CMAKE_CURRENT_LIST_DIR}/@CPPDAP_TARGETS_EXPORT_NAME@.cmake") +check_required_components("@CPPDAP_TARGET_NAME@") + +if ( @CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE@ ) + find_dependency(nlohmann_json CONFIG) +elseif( @CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE@ ) + find_dependency(RapidJSON CONFIG) +endif() \ No newline at end of file diff --git a/libraries/cppdap/examples/hello_debugger.cpp b/libraries/cppdap/examples/hello_debugger.cpp new file mode 100644 index 000000000..62b619f9e --- /dev/null +++ b/libraries/cppdap/examples/hello_debugger.cpp @@ -0,0 +1,469 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// hello_debugger is an example DAP server that provides single line stepping +// through a synthetic file. + +#include "dap/io.h" +#include "dap/protocol.h" +#include "dap/session.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER +#define OS_WINDOWS 1 +#endif + +// Uncomment the line below and change to a file path to +// write all DAP communications to the given path. +// +// #define LOG_TO_FILE "" + +#ifdef OS_WINDOWS +#include // _O_BINARY +#include // _setmode +#endif // OS_WINDOWS + +namespace { + +// sourceContent holds the synthetic file source. +constexpr char sourceContent[] = R"(// Hello Debugger! + +This is a synthetic source file provided by the DAP debugger. + +You can set breakpoints, and single line step. + +You may also notice that the locals contains a single variable for the currently executing line number.)"; + +// Total number of newlines in source. +constexpr int64_t numSourceLines = 7; + +// Debugger holds the dummy debugger state and fires events to the EventHandler +// passed to the constructor. +class Debugger { + public: + enum class Event { BreakpointHit, Stepped, Paused }; + using EventHandler = std::function; + + Debugger(const EventHandler&); + + // run() instructs the debugger to continue execution. + void run(); + + // pause() instructs the debugger to pause execution. + void pause(); + + // currentLine() returns the currently executing line number. + int64_t currentLine(); + + // stepForward() instructs the debugger to step forward one line. + void stepForward(); + + // clearBreakpoints() clears all set breakpoints. + void clearBreakpoints(); + + // addBreakpoint() sets a new breakpoint on the given line. + void addBreakpoint(int64_t line); + + private: + EventHandler onEvent; + std::mutex mutex; + int64_t line = 1; + std::unordered_set breakpoints; +}; + +Debugger::Debugger(const EventHandler& onEvent) : onEvent(onEvent) {} + +void Debugger::run() { + std::unique_lock lock(mutex); + for (int64_t i = 0; i < numSourceLines; i++) { + int64_t l = ((line + i) % numSourceLines) + 1; + if (breakpoints.count(l)) { + line = l; + lock.unlock(); + onEvent(Event::BreakpointHit); + return; + } + } +} + +void Debugger::pause() { + onEvent(Event::Paused); +} + +int64_t Debugger::currentLine() { + std::unique_lock lock(mutex); + return line; +} + +void Debugger::stepForward() { + std::unique_lock lock(mutex); + line = (line % numSourceLines) + 1; + lock.unlock(); + onEvent(Event::Stepped); +} + +void Debugger::clearBreakpoints() { + std::unique_lock lock(mutex); + this->breakpoints.clear(); +} + +void Debugger::addBreakpoint(int64_t l) { + std::unique_lock lock(mutex); + this->breakpoints.emplace(l); +} + +// Event provides a basic wait and signal synchronization primitive. +class Event { + public: + // wait() blocks until the event is fired. + void wait(); + + // fire() sets signals the event, and unblocks any calls to wait(). + void fire(); + + private: + std::mutex mutex; + std::condition_variable cv; + bool fired = false; +}; + +void Event::wait() { + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return fired; }); +} + +void Event::fire() { + std::unique_lock lock(mutex); + fired = true; + cv.notify_all(); +} + +} // anonymous namespace + +// main() entry point to the DAP server. +int main(int, char*[]) { +#ifdef OS_WINDOWS + // Change stdin & stdout from text mode to binary mode. + // This ensures sequences of \r\n are not changed to \n. + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif // OS_WINDOWS + + std::shared_ptr log; +#ifdef LOG_TO_FILE + log = dap::file(LOG_TO_FILE); +#endif + + // Create the DAP session. + // This is used to implement the DAP server. + auto session = dap::Session::create(); + + // Hard-coded identifiers for the one thread, frame, variable and source. + // These numbers have no meaning, and just need to remain constant for the + // duration of the service. + const dap::integer threadId = 100; + const dap::integer frameId = 200; + const dap::integer variablesReferenceId = 300; + const dap::integer sourceReferenceId = 400; + + // Signal events + Event configured; + Event terminate; + + // Event handlers from the Debugger. + auto onDebuggerEvent = [&](Debugger::Event onEvent) { + switch (onEvent) { + case Debugger::Event::Stepped: { + // The debugger has single-line stepped. Inform the client. + dap::StoppedEvent event; + event.reason = "step"; + event.threadId = threadId; + session->send(event); + break; + } + case Debugger::Event::BreakpointHit: { + // The debugger has hit a breakpoint. Inform the client. + dap::StoppedEvent event; + event.reason = "breakpoint"; + event.threadId = threadId; + session->send(event); + break; + } + case Debugger::Event::Paused: { + // The debugger has been suspended. Inform the client. + dap::StoppedEvent event; + event.reason = "pause"; + event.threadId = threadId; + session->send(event); + break; + } + } + }; + + // Construct the debugger. + Debugger debugger(onDebuggerEvent); + + // Handle errors reported by the Session. These errors include protocol + // parsing errors and receiving messages with no handler. + session->onError([&](const char* msg) { + if (log) { + dap::writef(log, "dap::Session error: %s\n", msg); + log->close(); + } + terminate.fire(); + }); + + // The Initialize request is the first message sent from the client and + // the response reports debugger capabilities. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize + session->registerHandler([](const dap::InitializeRequest&) { + dap::InitializeResponse response; + response.supportsConfigurationDoneRequest = true; + return response; + }); + + // When the Initialize response has been sent, we need to send the initialized + // event. + // We use the registerSentHandler() to ensure the event is sent *after* the + // initialize response. + // https://microsoft.github.io/debug-adapter-protocol/specification#Events_Initialized + session->registerSentHandler( + [&](const dap::ResponseOrError&) { + session->send(dap::InitializedEvent()); + }); + + // The Threads request queries the debugger's list of active threads. + // This example debugger only exposes a single thread. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Threads + session->registerHandler([&](const dap::ThreadsRequest&) { + dap::ThreadsResponse response; + dap::Thread thread; + thread.id = threadId; + thread.name = "TheThread"; + response.threads.push_back(thread); + return response; + }); + + // The StackTrace request reports the stack frames (call stack) for a given + // thread. This example debugger only exposes a single stack frame for the + // single thread. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StackTrace + session->registerHandler( + [&](const dap::StackTraceRequest& request) + -> dap::ResponseOrError { + if (request.threadId != threadId) { + return dap::Error("Unknown threadId '%d'", int(request.threadId)); + } + + dap::Source source; + source.sourceReference = sourceReferenceId; + source.name = "HelloDebuggerSource"; + + dap::StackFrame frame; + frame.line = debugger.currentLine(); + frame.column = 1; + frame.name = "HelloDebugger"; + frame.id = frameId; + frame.source = source; + + dap::StackTraceResponse response; + response.stackFrames.push_back(frame); + return response; + }); + + // The Scopes request reports all the scopes of the given stack frame. + // This example debugger only exposes a single 'Locals' scope for the single + // frame. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Scopes + session->registerHandler([&](const dap::ScopesRequest& request) + -> dap::ResponseOrError { + if (request.frameId != frameId) { + return dap::Error("Unknown frameId '%d'", int(request.frameId)); + } + + dap::Scope scope; + scope.name = "Locals"; + scope.presentationHint = "locals"; + scope.variablesReference = variablesReferenceId; + + dap::ScopesResponse response; + response.scopes.push_back(scope); + return response; + }); + + // The Variables request reports all the variables for the given scope. + // This example debugger only exposes a single 'currentLine' variable for the + // single 'Locals' scope. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Variables + session->registerHandler([&](const dap::VariablesRequest& request) + -> dap::ResponseOrError { + if (request.variablesReference != variablesReferenceId) { + return dap::Error("Unknown variablesReference '%d'", + int(request.variablesReference)); + } + + dap::Variable currentLineVar; + currentLineVar.name = "currentLine"; + currentLineVar.value = std::to_string(debugger.currentLine()); + currentLineVar.type = "int"; + + dap::VariablesResponse response; + response.variables.push_back(currentLineVar); + return response; + }); + + // The Pause request instructs the debugger to pause execution of one or all + // threads. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Pause + session->registerHandler([&](const dap::PauseRequest&) { + debugger.pause(); + return dap::PauseResponse(); + }); + + // The Continue request instructs the debugger to resume execution of one or + // all threads. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Continue + session->registerHandler([&](const dap::ContinueRequest&) { + debugger.run(); + return dap::ContinueResponse(); + }); + + // The Next request instructs the debugger to single line step for a specific + // thread. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Next + session->registerHandler([&](const dap::NextRequest&) { + debugger.stepForward(); + return dap::NextResponse(); + }); + + // The StepIn request instructs the debugger to step-in for a specific thread. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepIn + session->registerHandler([&](const dap::StepInRequest&) { + // Step-in treated as step-over as there's only one stack frame. + debugger.stepForward(); + return dap::StepInResponse(); + }); + + // The StepOut request instructs the debugger to step-out for a specific + // thread. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepOut + session->registerHandler([&](const dap::StepOutRequest&) { + // Step-out is not supported as there's only one stack frame. + return dap::StepOutResponse(); + }); + + // The SetBreakpoints request instructs the debugger to clear and set a number + // of line breakpoints for a specific source file. + // This example debugger only exposes a single source file. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints + session->registerHandler([&](const dap::SetBreakpointsRequest& request) { + dap::SetBreakpointsResponse response; + + auto breakpoints = request.breakpoints.value({}); + if (request.source.sourceReference.value(0) == sourceReferenceId) { + debugger.clearBreakpoints(); + response.breakpoints.resize(breakpoints.size()); + for (size_t i = 0; i < breakpoints.size(); i++) { + debugger.addBreakpoint(breakpoints[i].line); + response.breakpoints[i].verified = breakpoints[i].line < numSourceLines; + } + } else { + response.breakpoints.resize(breakpoints.size()); + } + + return response; + }); + + // The SetExceptionBreakpoints request configures the debugger's handling of + // thrown exceptions. + // This example debugger does not use any exceptions, so this is a no-op. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetExceptionBreakpoints + session->registerHandler([&](const dap::SetExceptionBreakpointsRequest&) { + return dap::SetExceptionBreakpointsResponse(); + }); + + // The Source request retrieves the source code for a given source file. + // This example debugger only exposes one synthetic source file. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Source + session->registerHandler([&](const dap::SourceRequest& request) + -> dap::ResponseOrError { + if (request.sourceReference != sourceReferenceId) { + return dap::Error("Unknown source reference '%d'", + int(request.sourceReference)); + } + + dap::SourceResponse response; + response.content = sourceContent; + return response; + }); + + // The Launch request is made when the client instructs the debugger adapter + // to start the debuggee. This request contains the launch arguments. + // This example debugger does nothing with this request. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Launch + session->registerHandler( + [&](const dap::LaunchRequest&) { return dap::LaunchResponse(); }); + + // Handler for disconnect requests + session->registerHandler([&](const dap::DisconnectRequest& request) { + if (request.terminateDebuggee.value(false)) { + terminate.fire(); + } + return dap::DisconnectResponse(); + }); + + // The ConfigurationDone request is made by the client once all configuration + // requests have been made. + // This example debugger uses this request to 'start' the debugger. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_ConfigurationDone + session->registerHandler([&](const dap::ConfigurationDoneRequest&) { + configured.fire(); + return dap::ConfigurationDoneResponse(); + }); + + // All the handlers we care about have now been registered. + // We now bind the session to stdin and stdout to connect to the client. + // After the call to bind() we should start receiving requests, starting with + // the Initialize request. + std::shared_ptr in = dap::file(stdin, false); + std::shared_ptr out = dap::file(stdout, false); + if (log) { + session->bind(spy(in, log), spy(out, log)); + } else { + session->bind(in, out); + } + + // Wait for the ConfigurationDone request to be made. + configured.wait(); + + // Broadcast the existance of the single thread to the client. + dap::ThreadEvent threadStartedEvent; + threadStartedEvent.reason = "started"; + threadStartedEvent.threadId = threadId; + session->send(threadStartedEvent); + + // Start the debugger in a paused state. + // This sends a stopped event to the client. + debugger.pause(); + + // Block until we receive a 'terminateDebuggee' request or encounter a session + // error. + terminate.wait(); + + return 0; +} diff --git a/libraries/cppdap/examples/simple_net_client_server.cpp b/libraries/cppdap/examples/simple_net_client_server.cpp new file mode 100644 index 000000000..c93cc9c1d --- /dev/null +++ b/libraries/cppdap/examples/simple_net_client_server.cpp @@ -0,0 +1,109 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// simple_net_client_server demonstrates a minimal DAP network connection +// between a server and client. + +#include "dap/io.h" +#include "dap/network.h" +#include "dap/protocol.h" +#include "dap/session.h" + +#include +#include + +int main(int, char*[]) { + constexpr int kPort = 19021; + + // Callback handler for a socket connection to the server + auto onClientConnected = + [&](const std::shared_ptr& socket) { + auto session = dap::Session::create(); + + // Set the session to close on invalid data. This ensures that data received over the network + // receives a baseline level of validation before being processed. + session->setOnInvalidData(dap::kClose); + + session->bind(socket); + + // The Initialize request is the first message sent from the client and + // the response reports debugger capabilities. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize + session->registerHandler([&](const dap::InitializeRequest&) { + dap::InitializeResponse response; + printf("Server received initialize request from client\n"); + return response; + }); + + // Signal used to terminate the server session when a DisconnectRequest + // is made by the client. + bool terminate = false; + std::condition_variable cv; + std::mutex mutex; // guards 'terminate' + + // The Disconnect request is made by the client before it disconnects + // from the server. + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect + session->registerHandler([&](const dap::DisconnectRequest&) { + // Client wants to disconnect. Set terminate to true, and signal the + // condition variable to unblock the server thread. + std::unique_lock lock(mutex); + terminate = true; + cv.notify_one(); + return dap::DisconnectResponse{}; + }); + + // Wait for the client to disconnect (or reach a 5 second timeout) + // before releasing the session and disconnecting the socket to the + // client. + std::unique_lock lock(mutex); + cv.wait_for(lock, std::chrono::seconds(5), [&] { return terminate; }); + printf("Server closing connection\n"); + }; + + // Error handler + auto onError = [&](const char* msg) { printf("Server error: %s\n", msg); }; + + // Create the network server + auto server = dap::net::Server::create(); + // Start listening on kPort. + // onClientConnected will be called when a client wants to connect. + // onError will be called on any connection errors. + server->start(kPort, onClientConnected, onError); + + // Create a socket to the server. This will be used for the client side of the + // connection. + auto client = dap::net::connect("localhost", kPort); + if (!client) { + printf("Couldn't connect to server\n"); + return 1; + } + + // Attach a session to the client socket. + auto session = dap::Session::create(); + session->bind(client); + + // Set an initialize request to the server. + auto future = session->send(dap::InitializeRequest{}); + printf("Client sent initialize request to server\n"); + printf("Waiting for response from server...\n"); + // Wait on the response. + auto response = future.get(); + printf("Response received from server\n"); + printf("Disconnecting...\n"); + // Disconnect. + session->send(dap::DisconnectRequest{}); + + return 0; +} diff --git a/libraries/cppdap/examples/vscode/package.json b/libraries/cppdap/examples/vscode/package.json new file mode 100644 index 000000000..9a524131d --- /dev/null +++ b/libraries/cppdap/examples/vscode/package.json @@ -0,0 +1,28 @@ +{ + "name": "cppdap-example-@target@", + "displayName": "cppdap example: @target@", + "description": "cppdap example: @target@", + "version": "1.0.0", + "preview": false, + "publisher": "Google LLC", + "author": { + "name": "Google LLC" + }, + "license": "SEE LICENSE IN LICENSE.txt", + "engines": { + "vscode": "^1.32.0" + }, + "categories": [ + "Debuggers" + ], + "contributes": { + "debuggers": [ + { + "type": "@target@", + "program": "@target@@CPPDAP_OS_EXE_EXT@", + "label": "cppdap example: @target@", + "configurationAttributes": {} + } + ] + } +} \ No newline at end of file diff --git a/libraries/cppdap/fuzz/dictionary.txt b/libraries/cppdap/fuzz/dictionary.txt new file mode 100644 index 000000000..639a8b0e8 --- /dev/null +++ b/libraries/cppdap/fuzz/dictionary.txt @@ -0,0 +1,372 @@ +"MD5" +"SHA1" +"SHA256" +"__restart" +"accessType" +"accessTypes" +"adapterData" +"adapterID" +"additionalModuleColumns" +"address" +"addressRange" +"algorithm" +"all" +"allThreadsContinued" +"allThreadsStopped" +"allowPartial" +"always" +"appliesTo" +"areas" +"args" +"argsCanBeInterpretedByShell" +"arguments" +"asAddress" +"attach" +"attachForSuspendedLaunch" +"attributeName" +"attributes" +"baseClass" +"body" +"boolean" +"breakMode" +"breakpoint" +"breakpointLocations" +"breakpointModes" +"breakpoints" +"bytes" +"bytesWritten" +"canPersist" +"canRestart" +"cancel" +"cancellable" +"cancelled" +"capabilities" +"category" +"changed" +"checksum" +"checksums" +"class" +"clientID" +"clientName" +"clipboard" +"color" +"column" +"columnsStartAt1" +"command" +"completionTriggerCharacters" +"completions" +"condition" +"conditionDescription" +"configuration" +"configurationDone" +"console" +"constructor" +"content" +"context" +"continue" +"continued" +"count" +"customcolor" +"cwd" +"data" +"data breakpoint" +"dataBreakpoint" +"dataBreakpointInfo" +"dataId" +"dateTimeStamp" +"declarationLocationReference" +"deemphasize" +"default" +"description" +"detail" +"details" +"disassemble" +"disconnect" +"emphasize" +"end" +"endColumn" +"endLine" +"entry" +"enum" +"env" +"error" +"evaluate" +"evaluateName" +"event" +"exception" +"exceptionBreakpointFilters" +"exceptionId" +"exceptionInfo" +"exceptionOptions" +"exitCode" +"exited" +"expensive" +"expression" +"external" +"failed" +"field" +"file" +"filter" +"filterId" +"filterOptions" +"filters" +"final" +"format" +"frameId" +"fullTypeName" +"function" +"function breakpoint" +"goto" +"gotoTargets" +"granularity" +"group" +"hex" +"hitBreakpointIds" +"hitCondition" +"hover" +"id" +"important" +"includeAll" +"indexed" +"indexedVariables" +"initialize" +"initialized" +"innerClass" +"innerException" +"instruction" +"instruction breakpoint" +"instructionBytes" +"instructionCount" +"instructionOffset" +"instructionPointerReference" +"instructionReference" +"instructions" +"integrated" +"interface" +"internal" +"invalid" +"invalidated" +"isLocalProcess" +"isOptimized" +"isUserCode" +"keyword" +"kind" +"label" +"launch" +"lazy" +"length" +"levels" +"line" +"lines" +"linesStartAt1" +"loadedSource" +"loadedSources" +"locale" +"locals" +"location" +"locationReference" +"locations" +"logMessage" +"memory" +"memoryReference" +"message" +"method" +"mimeType" +"mode" +"module" +"moduleCount" +"moduleId" +"modules" +"mostDerivedClass" +"name" +"named" +"namedVariables" +"names" +"negate" +"never" +"new" +"next" +"noDebug" +"normal" +"notStopped" +"number" +"offset" +"origin" +"output" +"parameterNames" +"parameterTypes" +"parameterValues" +"parameters" +"path" +"pathFormat" +"pause" +"pending" +"percentage" +"pointerSize" +"presentationHint" +"preserveFocusHint" +"private" +"process" +"processId" +"progressEnd" +"progressId" +"progressStart" +"progressUpdate" +"property" +"protected" +"public" +"read" +"readMemory" +"readWrite" +"reason" +"reference" +"registers" +"removed" +"repl" +"request" +"requestId" +"request_seq" +"resolveSymbols" +"response" +"restart" +"restartFrame" +"result" +"returnValue" +"reverseContinue" +"runInTerminal" +"scopes" +"selectionLength" +"selectionStart" +"sendTelemetry" +"seq" +"setBreakpoints" +"setDataBreakpoints" +"setExceptionBreakpoints" +"setExpression" +"setFunctionBreakpoints" +"setInstructionBreakpoints" +"setVariable" +"shellProcessId" +"showUser" +"singleThread" +"snippet" +"sortText" +"source" +"sourceModified" +"sourceReference" +"sources" +"stackFrameId" +"stackFrames" +"stackTrace" +"stacks" +"start" +"startCollapsed" +"startDebugging" +"startFrame" +"startMethod" +"startModule" +"started" +"statement" +"stderr" +"stdout" +"step" +"stepBack" +"stepIn" +"stepInTargets" +"stepOut" +"stopped" +"string" +"subtle" +"success" +"supportSuspendDebuggee" +"supportTerminateDebuggee" +"supportedChecksumAlgorithms" +"supportsANSIStyling" +"supportsArgsCanBeInterpretedByShell" +"supportsBreakpointLocationsRequest" +"supportsCancelRequest" +"supportsClipboardContext" +"supportsCompletionsRequest" +"supportsCondition" +"supportsConditionalBreakpoints" +"supportsConfigurationDoneRequest" +"supportsDataBreakpointBytes" +"supportsDataBreakpoints" +"supportsDelayedStackTraceLoading" +"supportsDisassembleRequest" +"supportsEvaluateForHovers" +"supportsExceptionFilterOptions" +"supportsExceptionInfoRequest" +"supportsExceptionOptions" +"supportsFunctionBreakpoints" +"supportsGotoTargetsRequest" +"supportsHitConditionalBreakpoints" +"supportsInstructionBreakpoints" +"supportsInvalidatedEvent" +"supportsLoadedSourcesRequest" +"supportsLogPoints" +"supportsMemoryEvent" +"supportsMemoryReferences" +"supportsModulesRequest" +"supportsProgressReporting" +"supportsReadMemoryRequest" +"supportsRestartFrame" +"supportsRestartRequest" +"supportsRunInTerminalRequest" +"supportsSetExpression" +"supportsSetVariable" +"supportsSingleThreadExecutionRequests" +"supportsStartDebuggingRequest" +"supportsStepBack" +"supportsStepInTargetsRequest" +"supportsSteppingGranularity" +"supportsTerminateRequest" +"supportsTerminateThreadsRequest" +"supportsValueFormattingOptions" +"supportsVariablePaging" +"supportsVariableType" +"supportsWriteMemoryRequest" +"suspendDebuggee" +"symbol" +"symbolFilePath" +"symbolStatus" +"systemProcessId" +"targetId" +"targets" +"telemetry" +"terminate" +"terminateDebuggee" +"terminateThreads" +"terminated" +"text" +"thread" +"threadId" +"threadIds" +"threads" +"timestamp" +"title" +"totalFrames" +"totalModules" +"type" +"typeName" +"unhandled" +"unit" +"unixTimestampUTC" +"unreadableBytes" +"uri" +"url" +"urlLabel" +"userUnhandled" +"value" +"valueLocationReference" +"variable" +"variables" +"variablesReference" +"verified" +"version" +"virtual" +"visibility" +"watch" +"width" +"write" +"writeMemory" \ No newline at end of file diff --git a/libraries/cppdap/fuzz/fuzz.cpp b/libraries/cppdap/fuzz/fuzz.cpp new file mode 100644 index 000000000..02a21cc6f --- /dev/null +++ b/libraries/cppdap/fuzz/fuzz.cpp @@ -0,0 +1,128 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// cppdap fuzzer program. +// Run with: ${CPPDAP_PATH}/fuzz/run.sh +// Requires modern clang toolchain. + +#include "content_stream.h" +#include "string_buffer.h" + +#include "dap/protocol.h" +#include "dap/session.h" + +#include "fuzz.h" + +#include +#include + +namespace { + +// Event provides a basic wait and signal synchronization primitive. +class Event { + public: + // wait() blocks until the event is fired or the given timeout is reached. + template + inline void wait(const DURATION& duration) { + std::unique_lock lock(mutex); + cv.wait_for(lock, duration, [&] { return fired; }); + } + + // fire() sets signals the event, and unblocks any calls to wait(). + inline void fire() { + std::unique_lock lock(mutex); + fired = true; + cv.notify_all(); + } + + private: + std::mutex mutex; + std::condition_variable cv; + bool fired = false; +}; + +} // namespace + +// Fuzzing main function. +// See http://llvm.org/docs/LibFuzzer.html for details. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // The first byte can optionally control fuzzing mode. + enum class ControlMode { + // Don't wrap the input data with a stream writer. Allows testing for stream + // writing. + TestStreamWriter, + + // Don't append a 'done' request. This may cause the test to take longer to + // complete (it may have to block on a timeout), but exercises the + // unrecognised-message cases. + DontAppendDoneRequest, + + // Number of control modes in this enum. + Count, + }; + + // Scan first byte for control mode. + bool useContentStreamWriter = true; + bool appendDoneRequest = true; + if (size > 0 && data[0] < static_cast(ControlMode::Count)) { + useContentStreamWriter = + data[0] != static_cast(ControlMode::TestStreamWriter); + appendDoneRequest = + data[0] != static_cast(ControlMode::DontAppendDoneRequest); + data++; + size--; + } + + // in contains the input data + auto in = std::make_shared(); + + dap::ContentWriter writer(in); + if (useContentStreamWriter) { + writer.write(std::string(reinterpret_cast(data), size)); + } else { + in->write(data, size); + } + + if (appendDoneRequest) { + writer.write(R"( + { + "seq": 10, + "type": "request", + "command": "done", + } + )"); + } + + // Each test is done if we receive a request, or report an error. + Event requestOrError; + +#define DAP_REQUEST(REQUEST, RESPONSE) \ + session->registerHandler([&](const REQUEST&) { \ + requestOrError.fire(); \ + return RESPONSE{}; \ + }); + + auto session = dap::Session::create(); + DAP_REQUEST_LIST(); + + session->onError([&](const char*) { requestOrError.fire(); }); + + auto out = std::make_shared(); + session->bind(dap::ReaderWriter::create(in, out)); + + // Give up after a second if we don't get a request or error reported. + requestOrError.wait(std::chrono::seconds(1)); + + return 0; +} \ No newline at end of file diff --git a/libraries/cppdap/fuzz/fuzz.h b/libraries/cppdap/fuzz/fuzz.h new file mode 100644 index 000000000..995f54025 --- /dev/null +++ b/libraries/cppdap/fuzz/fuzz.h @@ -0,0 +1,76 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#ifndef dap_fuzzer_h +#define dap_fuzzer_h + +#include "dap/protocol.h" + +#define DAP_REQUEST_LIST() \ + DAP_REQUEST(dap::AttachRequest, dap::AttachResponse) \ + DAP_REQUEST(dap::BreakpointLocationsRequest, \ + dap::BreakpointLocationsResponse) \ + DAP_REQUEST(dap::CancelRequest, dap::CancelResponse) \ + DAP_REQUEST(dap::CompletionsRequest, dap::CompletionsResponse) \ + DAP_REQUEST(dap::ConfigurationDoneRequest, dap::ConfigurationDoneResponse) \ + DAP_REQUEST(dap::ContinueRequest, dap::ContinueResponse) \ + DAP_REQUEST(dap::DataBreakpointInfoRequest, dap::DataBreakpointInfoResponse) \ + DAP_REQUEST(dap::DisassembleRequest, dap::DisassembleResponse) \ + DAP_REQUEST(dap::DisconnectRequest, dap::DisconnectResponse) \ + DAP_REQUEST(dap::EvaluateRequest, dap::EvaluateResponse) \ + DAP_REQUEST(dap::ExceptionInfoRequest, dap::ExceptionInfoResponse) \ + DAP_REQUEST(dap::GotoRequest, dap::GotoResponse) \ + DAP_REQUEST(dap::GotoTargetsRequest, dap::GotoTargetsResponse) \ + DAP_REQUEST(dap::InitializeRequest, dap::InitializeResponse) \ + DAP_REQUEST(dap::LaunchRequest, dap::LaunchResponse) \ + DAP_REQUEST(dap::LoadedSourcesRequest, dap::LoadedSourcesResponse) \ + DAP_REQUEST(dap::LocationsRequest, dap::LocationsResponse) \ + DAP_REQUEST(dap::ModulesRequest, dap::ModulesResponse) \ + DAP_REQUEST(dap::NextRequest, dap::NextResponse) \ + DAP_REQUEST(dap::PauseRequest, dap::PauseResponse) \ + DAP_REQUEST(dap::ReadMemoryRequest, dap::ReadMemoryResponse) \ + DAP_REQUEST(dap::RestartFrameRequest, dap::RestartFrameResponse) \ + DAP_REQUEST(dap::RestartRequest, dap::RestartResponse) \ + DAP_REQUEST(dap::ReverseContinueRequest, dap::ReverseContinueResponse) \ + DAP_REQUEST(dap::RunInTerminalRequest, dap::RunInTerminalResponse) \ + DAP_REQUEST(dap::ScopesRequest, dap::ScopesResponse) \ + DAP_REQUEST(dap::SetBreakpointsRequest, dap::SetBreakpointsResponse) \ + DAP_REQUEST(dap::SetDataBreakpointsRequest, dap::SetDataBreakpointsResponse) \ + DAP_REQUEST(dap::SetExceptionBreakpointsRequest, \ + dap::SetExceptionBreakpointsResponse) \ + DAP_REQUEST(dap::SetExpressionRequest, dap::SetExpressionResponse) \ + DAP_REQUEST(dap::SetFunctionBreakpointsRequest, \ + dap::SetFunctionBreakpointsResponse) \ + DAP_REQUEST(dap::SetInstructionBreakpointsRequest, \ + dap::SetInstructionBreakpointsResponse) \ + DAP_REQUEST(dap::SetVariableRequest, dap::SetVariableResponse) \ + DAP_REQUEST(dap::SourceRequest, dap::SourceResponse) \ + DAP_REQUEST(dap::StackTraceRequest, dap::StackTraceResponse) \ + DAP_REQUEST(dap::StartDebuggingRequest, dap::StartDebuggingResponse) \ + DAP_REQUEST(dap::StepBackRequest, dap::StepBackResponse) \ + DAP_REQUEST(dap::StepInRequest, dap::StepInResponse) \ + DAP_REQUEST(dap::StepInTargetsRequest, dap::StepInTargetsResponse) \ + DAP_REQUEST(dap::StepOutRequest, dap::StepOutResponse) \ + DAP_REQUEST(dap::TerminateRequest, dap::TerminateResponse) \ + DAP_REQUEST(dap::TerminateThreadsRequest, dap::TerminateThreadsResponse) \ + DAP_REQUEST(dap::ThreadsRequest, dap::ThreadsResponse) \ + DAP_REQUEST(dap::VariablesRequest, dap::VariablesResponse) \ + DAP_REQUEST(dap::WriteMemoryRequest, dap::WriteMemoryResponse) + +#endif // dap_fuzzer_h diff --git a/libraries/cppdap/fuzz/run.sh b/libraries/cppdap/fuzz/run.sh new file mode 100755 index 000000000..2d5403909 --- /dev/null +++ b/libraries/cppdap/fuzz/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e # Fail on any error. + +FUZZ_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +cd ${FUZZ_DIR} + +# Ensure we're testing with latest build +[ ! -d "build" ] && mkdir "build" +cd "build" +cmake ../.. -GNinja -DCPPDAP_BUILD_FUZZER=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo +ninja + +cd ${FUZZ_DIR} +[ ! -d "corpus" ] && mkdir "corpus" +[ ! -d "logs" ] && mkdir "logs" +cd "logs" +rm crash-* fuzz-* || true +${FUZZ_DIR}/build/cppdap-fuzzer ${FUZZ_DIR}/corpus ${FUZZ_DIR}/seed -dict=${FUZZ_DIR}/dictionary.txt -jobs=128 diff --git a/libraries/cppdap/fuzz/seed/empty_json b/libraries/cppdap/fuzz/seed/empty_json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/libraries/cppdap/fuzz/seed/empty_json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/libraries/cppdap/fuzz/seed/request b/libraries/cppdap/fuzz/seed/request new file mode 100644 index 000000000..7a183aded --- /dev/null +++ b/libraries/cppdap/fuzz/seed/request @@ -0,0 +1,8 @@ +{ + "seq": 153, + "type": "request", + "command": "next", + "arguments": { + "threadId": 3 + } +} \ No newline at end of file diff --git a/libraries/cppdap/include/dap/any.h b/libraries/cppdap/include/dap/any.h new file mode 100644 index 000000000..e799c44cc --- /dev/null +++ b/libraries/cppdap/include/dap/any.h @@ -0,0 +1,211 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_any_h +#define dap_any_h + +#include "typeinfo.h" + +#include +#include + +namespace dap { + +template +struct TypeOf; +class Deserializer; +class Serializer; + +// any provides a type-safe container for values of any of dap type (boolean, +// integer, number, array, variant, any, null, dap-structs). +class any { + public: + // constructors + inline any() = default; + inline any(const any& other) noexcept; + inline any(any&& other) noexcept; + + template + inline any(const T& val); + + // destructors + inline ~any(); + + // replaces the contained value with a null. + inline void reset(); + + // assignment + inline any& operator=(const any& rhs); + inline any& operator=(any&& rhs) noexcept; + template + inline any& operator=(const T& val); + inline any& operator=(const std::nullptr_t& val); + + // get() returns the contained value of the type T. + // If the any does not contain a value of type T, then get() will assert. + template + inline T& get() const; + + // is() returns true iff the contained value is of type T. + template + inline bool is() const; + + private: + friend class Deserializer; + friend class Serializer; + + static inline void* alignUp(void* val, size_t alignment); + inline void alloc(size_t size, size_t align); + inline void freeValue(); + inline bool isInBuffer(void* ptr) const; + + void* value = nullptr; + const TypeInfo* type = nullptr; + void* heap = nullptr; // heap allocation + uint8_t buffer[32]; // or internal allocation +}; + +inline any::~any() { + reset(); +} + +template +inline any::any(const T& val) { + *this = val; +} + +any::any(const any& other) noexcept : type(other.type) { + if (other.value != nullptr) { + alloc(type->size(), type->alignment()); + type->copyConstruct(value, other.value); + } +} + +any::any(any&& other) noexcept : type(other.type) { + if (other.isInBuffer(other.value)) { + alloc(type->size(), type->alignment()); + type->copyConstruct(value, other.value); + } else { + value = other.value; + } + other.value = nullptr; + other.type = nullptr; +} + +void any::reset() { + if (value != nullptr) { + type->destruct(value); + freeValue(); + } + value = nullptr; + type = nullptr; +} + +any& any::operator=(const any& rhs) { + reset(); + type = rhs.type; + if (rhs.value != nullptr) { + alloc(type->size(), type->alignment()); + type->copyConstruct(value, rhs.value); + } + return *this; +} + +any& any::operator=(any&& rhs) noexcept { + reset(); + type = rhs.type; + if (rhs.isInBuffer(rhs.value)) { + alloc(type->size(), type->alignment()); + type->copyConstruct(value, rhs.value); + } else { + value = rhs.value; + } + rhs.value = nullptr; + rhs.type = nullptr; + return *this; +} + +template +any& any::operator=(const T& val) { + if (!is()) { + reset(); + type = TypeOf::type(); + alloc(type->size(), type->alignment()); + type->copyConstruct(value, &val); + } else { +#ifdef __clang_analyzer__ + assert(value != nullptr); +#endif + *reinterpret_cast(value) = val; + } + return *this; +} + +any& any::operator=(const std::nullptr_t&) { + reset(); + return *this; +} + +template +T& any::get() const { + static_assert(!std::is_same(), + "Cannot get nullptr from 'any'."); + assert(is()); + return *reinterpret_cast(value); +} + +template +bool any::is() const { + return type == TypeOf::type(); +} + +template <> +inline bool any::is() const { + return value == nullptr; +} + +void* any::alignUp(void* val, size_t alignment) { + auto ptr = reinterpret_cast(val); + return reinterpret_cast(alignment * + ((ptr + alignment - 1) / alignment)); +} + +void any::alloc(size_t size, size_t align) { + assert(value == nullptr); + value = alignUp(buffer, align); + if (isInBuffer(reinterpret_cast(value) + size - 1)) { + return; + } + heap = new uint8_t[size + align]; + value = alignUp(heap, align); +} + +void any::freeValue() { + assert(value != nullptr); + if (heap != nullptr) { + delete[] reinterpret_cast(heap); + heap = nullptr; + } + value = nullptr; +} + +bool any::isInBuffer(void* ptr) const { + auto addr = reinterpret_cast(ptr); + return addr >= reinterpret_cast(buffer) && + addr < reinterpret_cast(buffer + sizeof(buffer)); +} + +} // namespace dap + +#endif // dap_any_h diff --git a/libraries/cppdap/include/dap/dap.h b/libraries/cppdap/include/dap/dap.h new file mode 100644 index 000000000..587e80c35 --- /dev/null +++ b/libraries/cppdap/include/dap/dap.h @@ -0,0 +1,35 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_dap_h +#define dap_dap_h + +namespace dap { + +// Explicit library initialization and termination functions. +// +// cppdap automatically initializes and terminates its internal state using lazy +// static initialization, and so will usually work fine without explicit calls +// to these functions. +// However, if you use cppdap types in global state, you may need to call these +// functions to ensure that cppdap is not uninitialized before the last usage. +// +// Each call to initialize() must have a corresponding call to terminate(). +// It is undefined behaviour to call initialize() after terminate(). +void initialize(); +void terminate(); + +} // namespace dap + +#endif // dap_dap_h diff --git a/libraries/cppdap/include/dap/future.h b/libraries/cppdap/include/dap/future.h new file mode 100644 index 000000000..af103c33b --- /dev/null +++ b/libraries/cppdap/include/dap/future.h @@ -0,0 +1,179 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_future_h +#define dap_future_h + +#include +#include +#include + +namespace dap { + +// internal functionality +namespace detail { +template +struct promise_state { + T val; + std::mutex mutex; + std::condition_variable cv; + bool hasVal = false; +}; +} // namespace detail + +// forward declaration +template +class promise; + +// future_status is the enumeration returned by future::wait_for and +// future::wait_until. +enum class future_status { + ready, + timeout, +}; + +// future is a minimal reimplementation of std::future, that does not suffer +// from TSAN false positives. See: +// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 +template +class future { + public: + using State = detail::promise_state; + + // constructors + inline future() = default; + inline future(future&&) = default; + + // valid() returns true if the future has an internal state. + bool valid() const; + + // get() blocks until the future has a valid result, and returns it. + // The future must have a valid internal state to call this method. + inline T get(); + + // wait() blocks until the future has a valid result. + // The future must have a valid internal state to call this method. + void wait() const; + + // wait_for() blocks until the future has a valid result, or the timeout is + // reached. + // The future must have a valid internal state to call this method. + template + future_status wait_for( + const std::chrono::duration& timeout) const; + + // wait_until() blocks until the future has a valid result, or the timeout is + // reached. + // The future must have a valid internal state to call this method. + template + future_status wait_until( + const std::chrono::time_point& timeout) const; + + private: + friend promise; + future(const future&) = delete; + inline future(const std::shared_ptr& state); + + std::shared_ptr state = std::make_shared(); +}; + +template +future::future(const std::shared_ptr& s) : state(s) {} + +template +bool future::valid() const { + return static_cast(state); +} + +template +T future::get() { + std::unique_lock lock(state->mutex); + state->cv.wait(lock, [&] { return state->hasVal; }); + return state->val; +} + +template +void future::wait() const { + std::unique_lock lock(state->mutex); + state->cv.wait(lock, [&] { return state->hasVal; }); +} + +template +template +future_status future::wait_for( + const std::chrono::duration& timeout) const { + std::unique_lock lock(state->mutex); + return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; }) + ? future_status::ready + : future_status::timeout; +} + +template +template +future_status future::wait_until( + const std::chrono::time_point& timeout) const { + std::unique_lock lock(state->mutex); + return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; }) + ? future_status::ready + : future_status::timeout; +} + +// promise is a minimal reimplementation of std::promise, that does not suffer +// from TSAN false positives. See: +// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 +template +class promise { + public: + // constructors + inline promise() = default; + inline promise(promise&& other) = default; + inline promise(const promise& other) = default; + + // set_value() stores value to the shared state. + // set_value() must only be called once. + inline void set_value(const T& value) const; + inline void set_value(T&& value) const; + + // get_future() returns a future sharing this promise's state. + future get_future(); + + private: + using State = detail::promise_state; + std::shared_ptr state = std::make_shared(); +}; + +template +future promise::get_future() { + return future(state); +} + +template +void promise::set_value(const T& value) const { + std::unique_lock lock(state->mutex); + state->val = value; + state->hasVal = true; + state->cv.notify_all(); +} + +template +void promise::set_value(T&& value) const { + std::unique_lock lock(state->mutex); + state->val = std::move(value); + state->hasVal = true; + state->cv.notify_all(); +} + +} // namespace dap + +#endif // dap_future_h diff --git a/libraries/cppdap/include/dap/io.h b/libraries/cppdap/include/dap/io.h new file mode 100644 index 000000000..61681cc4a --- /dev/null +++ b/libraries/cppdap/include/dap/io.h @@ -0,0 +1,97 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_io_h +#define dap_io_h + +#include // size_t +#include // FILE +#include // std::unique_ptr +#include // std::pair + +namespace dap { + +class Closable { + public: + virtual ~Closable() = default; + + // isOpen() returns true if the stream has not been closed. + virtual bool isOpen() = 0; + + // close() closes the stream. + virtual void close() = 0; +}; + +// Reader is an interface for reading from a byte stream. +class Reader : virtual public Closable { + public: + // read() attempts to read at most n bytes into buffer, returning the number + // of bytes read. + // read() will block until the stream is closed or at least one byte is read. + virtual size_t read(void* buffer, size_t n) = 0; +}; + +// Writer is an interface for writing to a byte stream. +class Writer : virtual public Closable { + public: + // write() writes n bytes from buffer into the stream. + // Returns true on success, or false if there was an error or the stream was + // closed. + virtual bool write(const void* buffer, size_t n) = 0; +}; + +// ReaderWriter is an interface that combines the Reader and Writer interfaces. +class ReaderWriter : public Reader, public Writer { + public: + // create() returns a ReaderWriter that delegates the interface methods on to + // the provided Reader and Writer. + // isOpen() returns true if the Reader and Writer both return true for + // isOpen(). + // close() closes both the Reader and Writer. + static std::shared_ptr create(const std::shared_ptr&, + const std::shared_ptr&); +}; + +// pipe() returns a ReaderWriter where the Writer streams to the Reader. +// Writes are internally buffered. +// Calling close() on either the Reader or Writer will close both ends of the +// stream. +std::shared_ptr pipe(); + +// file() wraps file with a ReaderWriter. +// If closable is false, then a call to ReaderWriter::close() will not close the +// underlying file. +std::shared_ptr file(FILE* file, bool closable = true); + +// file() opens (or creates) the file with the given path. +std::shared_ptr file(const char* path); + +// spy() returns a Reader that copies all reads from the Reader r to the Writer +// s, using the given optional prefix. +std::shared_ptr spy(const std::shared_ptr& r, + const std::shared_ptr& s, + const char* prefix = "\n->"); + +// spy() returns a Writer that copies all writes to the Writer w to the Writer +// s, using the given optional prefix. +std::shared_ptr spy(const std::shared_ptr& w, + const std::shared_ptr& s, + const char* prefix = "\n<-"); + +// writef writes the printf style string to the writer w. +bool writef(const std::shared_ptr& w, const char* msg, ...); + +} // namespace dap + +#endif // dap_io_h diff --git a/libraries/cppdap/include/dap/network.h b/libraries/cppdap/include/dap/network.h new file mode 100644 index 000000000..03b940925 --- /dev/null +++ b/libraries/cppdap/include/dap/network.h @@ -0,0 +1,71 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_network_h +#define dap_network_h + +#include +#include +#include + +namespace dap { +class ReaderWriter; + +namespace net { + +// connect() connects to the given TCP address and port. +// If timeoutMillis is non-zero and no connection was made before timeoutMillis +// milliseconds, then nullptr is returned. +std::shared_ptr connect(const char* addr, + int port, + uint32_t timeoutMillis = 0); + +// Server implements a basic TCP server. +class Server { + // ignoreErrors() matches the OnError signature, and does nothing. + static inline void ignoreErrors(const char*) {} + + public: + using OnError = std::function; + using OnConnect = std::function&)>; + + virtual ~Server() = default; + + // create() constructs and returns a new Server. + static std::unique_ptr create(); + + // start() begins listening for connections on localhost and the given port. + // callback will be called for each connection. + // onError will be called for any connection errors. + virtual bool start(int port, + const OnConnect& callback, + const OnError& onError = ignoreErrors) = 0; + + // start() begins listening for connections on the given specific address and port. + // callback will be called for each connection. + // onError will be called for any connection errors. + virtual bool start(const char* address, + int port, + const OnConnect& callback, + const OnError& onError = ignoreErrors) = 0; + + // stop() stops listening for connections. + // stop() is implicitly called on destruction. + virtual void stop() = 0; +}; + +} // namespace net +} // namespace dap + +#endif // dap_network_h diff --git a/libraries/cppdap/include/dap/optional.h b/libraries/cppdap/include/dap/optional.h new file mode 100644 index 000000000..9a3d21667 --- /dev/null +++ b/libraries/cppdap/include/dap/optional.h @@ -0,0 +1,263 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_optional_h +#define dap_optional_h + +#include +#include +#include // std::move, std::forward + +namespace dap { + +// optional holds an 'optional' contained value. +// This is similar to C++17's std::optional. +template +class optional { + template + using IsConvertibleToT = + typename std::enable_if::value>::type; + + public: + using value_type = T; + + // constructors + inline optional() = default; + inline optional(const optional& other); + inline optional(optional&& other); + template + inline optional(const optional& other); + template + inline optional(optional&& other); + template > + inline optional(U&& value); + + // value() returns the contained value. + // If the optional does not contain a value, then value() will assert. + inline T& value(); + inline const T& value() const; + + // value() returns the contained value, or defaultValue if the optional does + // not contain a value. + inline const T& value(const T& defaultValue) const; + + // operator bool() returns true if the optional contains a value. + inline explicit operator bool() const noexcept; + + // has_value() returns true if the optional contains a value. + inline bool has_value() const; + + // assignment + inline optional& operator=(const optional& other); + inline optional& operator=(optional&& other) noexcept; + template > + inline optional& operator=(U&& value); + template + inline optional& operator=(const optional& other); + template + inline optional& operator=(optional&& other); + + // value access + inline const T* operator->() const; + inline T* operator->(); + inline const T& operator*() const; + inline T& operator*(); + + private: + T val{}; + bool set = false; +}; + +template +optional::optional(const optional& other) : val(other.val), set(other.set) {} + +template +optional::optional(optional&& other) + : val(std::move(other.val)), set(other.set) {} + +template +template +optional::optional(const optional& other) : set(other.has_value()) { + if (set) { + val = static_cast(other.value()); + } +} + +template +template +optional::optional(optional&& other) : set(other.has_value()) { + if (set) { + val = static_cast(std::move(other.value())); + } +} + +template +template +optional::optional(U&& value) : val(std::forward(value)), set(true) {} + +template +T& optional::value() { + assert(set); + return val; +} + +template +const T& optional::value() const { + assert(set); + return val; +} + +template +const T& optional::value(const T& defaultValue) const { + if (!has_value()) { + return defaultValue; + } + return val; +} + +template +optional::operator bool() const noexcept { + return set; +} + +template +bool optional::has_value() const { + return set; +} + +template +optional& optional::operator=(const optional& other) { + val = other.val; + set = other.set; + return *this; +} + +template +optional& optional::operator=(optional&& other) noexcept { + val = std::move(other.val); + set = other.set; + return *this; +} + +template +template +optional& optional::operator=(U&& value) { + val = std::forward(value); + set = true; + return *this; +} + +template +template +optional& optional::operator=(const optional& other) { + val = other.val; + set = other.set; + return *this; +} + +template +template +optional& optional::operator=(optional&& other) { + val = std::move(other.val); + set = other.set; + return *this; +} + +template +const T* optional::operator->() const { + assert(set); + return &val; +} + +template +T* optional::operator->() { + assert(set); + return &val; +} + +template +const T& optional::operator*() const { + assert(set); + return val; +} + +template +T& optional::operator*() { + assert(set); + return val; +} + +template +inline bool operator==(const optional& lhs, const optional& rhs) { + if (!lhs.has_value() && !rhs.has_value()) { + return true; + } + if (!lhs.has_value() || !rhs.has_value()) { + return false; + } + return lhs.value() == rhs.value(); +} + +template +inline bool operator!=(const optional& lhs, const optional& rhs) { + return !(lhs == rhs); +} + +template +inline bool operator<(const optional& lhs, const optional& rhs) { + if (!rhs.has_value()) { + return false; + } + if (!lhs.has_value()) { + return true; + } + return lhs.value() < rhs.value(); +} + +template +inline bool operator<=(const optional& lhs, const optional& rhs) { + if (!lhs.has_value()) { + return true; + } + if (!rhs.has_value()) { + return false; + } + return lhs.value() <= rhs.value(); +} + +template +inline bool operator>(const optional& lhs, const optional& rhs) { + if (!lhs.has_value()) { + return false; + } + if (!rhs.has_value()) { + return true; + } + return lhs.value() > rhs.value(); +} + +template +inline bool operator>=(const optional& lhs, const optional& rhs) { + if (!rhs.has_value()) { + return true; + } + if (!lhs.has_value()) { + return false; + } + return lhs.value() >= rhs.value(); +} + +} // namespace dap + +#endif // dap_optional_h diff --git a/libraries/cppdap/include/dap/protocol.h b/libraries/cppdap/include/dap/protocol.h new file mode 100644 index 000000000..db5a0ef9a --- /dev/null +++ b/libraries/cppdap/include/dap/protocol.h @@ -0,0 +1,2909 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#ifndef dap_protocol_h +#define dap_protocol_h + +#include "optional.h" +#include "typeinfo.h" +#include "typeof.h" +#include "variant.h" + +#include +#include +#include + +namespace dap { + +struct Request {}; +struct Response {}; +struct Event {}; + +// Response to `attach` request. This is just an acknowledgement, so no body +// field is required. +struct AttachResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(AttachResponse); + +// The `attach` request is sent from the client to the debug adapter to attach +// to a debuggee that is already running. Since attaching is debugger/runtime +// specific, the arguments for this request are not part of this specification. +struct AttachRequest : public Request { + using Response = AttachResponse; + // Arbitrary data from the previous, restarted session. + // The data is sent as the `restart` attribute of the `terminated` event. + // The client should leave the data intact. + optional, boolean, integer, null, number, object, string>> + restart; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(AttachRequest); + +// Names of checksum algorithms that may be supported by a debug adapter. +// +// Must be one of the following enumeration values: +// 'MD5', 'SHA1', 'SHA256', 'timestamp' +using ChecksumAlgorithm = string; + +// The checksum of an item calculated by the specified algorithm. +struct Checksum { + // The algorithm used to calculate this checksum. + ChecksumAlgorithm algorithm = "MD5"; + // Value of the checksum, encoded as a hexadecimal value. + string checksum; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Checksum); + +// A `Source` is a descriptor for source code. +// It is returned from the debug adapter as part of a `StackFrame` and it is +// used by clients when specifying breakpoints. +struct Source { + // Additional data that a debug adapter might want to loop through the client. + // The client should leave the data intact and persist it across sessions. The + // client should not interpret the data. + optional, boolean, integer, null, number, object, string>> + adapterData; + // The checksums associated with this file. + optional> checksums; + // The short name of the source. Every source returned from the debug adapter + // has a name. When sending a source to the debug adapter this name is + // optional. + optional name; + // The origin of this source. For example, 'internal module', 'inlined content + // from source map', etc. + optional origin; + // The path of the source to be shown in the UI. + // It is only used to locate and load the content of the source if no + // `sourceReference` is specified (or its value is 0). + optional path; + // A hint for how to present the source in the UI. + // A value of `deemphasize` can be used to indicate that the source is not + // available or that it is skipped on stepping. + // + // Must be one of the following enumeration values: + // 'normal', 'emphasize', 'deemphasize' + optional presentationHint; + // If the value > 0 the contents of the source must be retrieved through the + // `source` request (even if a path is specified). Since a `sourceReference` + // is only valid for a session, it can not be used to persist a source. The + // value should be less than or equal to 2147483647 (2^31-1). + optional sourceReference; + // A list of sources that are related to this source. These may be the source + // that generated this source. + optional> sources; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Source); + +// Information about a breakpoint created in `setBreakpoints`, +// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or +// `setDataBreakpoints` requests. +struct Breakpoint { + // Start position of the source range covered by the breakpoint. It is + // measured in UTF-16 code units and the client capability `columnsStartAt1` + // determines whether it is 0- or 1-based. + optional column; + // End position of the source range covered by the breakpoint. It is measured + // in UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. If no end line is given, then the end column + // is assumed to be in the start line. + optional endColumn; + // The end line of the actual range covered by the breakpoint. + optional endLine; + // The identifier for the breakpoint. It is needed if breakpoint events are + // used to update or remove breakpoints. + optional id; + // A memory reference to where the breakpoint is set. + optional instructionReference; + // The start line of the actual range covered by the breakpoint. + optional line; + // A message about the state of the breakpoint. + // This is shown to the user and can be used to explain why a breakpoint could + // not be verified. + optional message; + // The offset from the instruction reference. + // This can be negative. + optional offset; + // A machine-readable explanation of why a breakpoint may not be verified. If + // a breakpoint is verified or a specific reason is not known, the adapter + // should omit this property. Possible values include: + // + // - `pending`: Indicates a breakpoint might be verified in the future, but + // the adapter cannot verify it in the current state. + // - `failed`: Indicates a breakpoint was not able to be verified, and the + // adapter does not believe it can be verified without intervention. + // + // Must be one of the following enumeration values: + // 'pending', 'failed' + optional reason; + // The source where the breakpoint is located. + optional source; + // If true, the breakpoint could be set (but not necessarily at the desired + // location). + boolean verified; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Breakpoint); + +// The event indicates that some information about a breakpoint has changed. +struct BreakpointEvent : public Event { + // The `id` attribute is used to find the target breakpoint, the other + // attributes are used as the new values. + Breakpoint breakpoint; + // The reason for the event. + // + // May be one of the following enumeration values: + // 'changed', 'new', 'removed' + string reason; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointEvent); + +// Properties of a breakpoint location returned from the `breakpointLocations` +// request. +struct BreakpointLocation { + // The start position of a breakpoint location. Position is measured in UTF-16 + // code units and the client capability `columnsStartAt1` determines whether + // it is 0- or 1-based. + optional column; + // The end position of a breakpoint location (if the location covers a range). + // Position is measured in UTF-16 code units and the client capability + // `columnsStartAt1` determines whether it is 0- or 1-based. + optional endColumn; + // The end line of breakpoint location if the location covers a range. + optional endLine; + // Start line of breakpoint location. + integer line; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocation); + +// Response to `breakpointLocations` request. +// Contains possible locations for source breakpoints. +struct BreakpointLocationsResponse : public Response { + // Sorted set of possible breakpoint locations. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocationsResponse); + +// The `breakpointLocations` request returns all possible locations for source +// breakpoints in a given range. Clients should only call this request if the +// corresponding capability `supportsBreakpointLocationsRequest` is true. +struct BreakpointLocationsRequest : public Request { + using Response = BreakpointLocationsResponse; + // Start position within `line` to search possible breakpoint locations in. It + // is measured in UTF-16 code units and the client capability + // `columnsStartAt1` determines whether it is 0- or 1-based. If no column is + // given, the first position in the start line is assumed. + optional column; + // End position within `endLine` to search possible breakpoint locations in. + // It is measured in UTF-16 code units and the client capability + // `columnsStartAt1` determines whether it is 0- or 1-based. If no end column + // is given, the last position in the end line is assumed. + optional endColumn; + // End line of range to search possible breakpoint locations in. If no end + // line is given, then the end line is assumed to be the start line. + optional endLine; + // Start line of range to search possible breakpoint locations in. If only the + // line is specified, the request returns all possible locations in that line. + integer line; + // The source location of the breakpoints; either `source.path` or + // `source.sourceReference` must be specified. + Source source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocationsRequest); + +// Response to `cancel` request. This is just an acknowledgement, so no body +// field is required. +struct CancelResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(CancelResponse); + +// The `cancel` request is used by the client in two situations: +// - to indicate that it is no longer interested in the result produced by a +// specific request issued earlier +// - to cancel a progress sequence. +// Clients should only call this request if the corresponding capability +// `supportsCancelRequest` is true. This request has a hint characteristic: a +// debug adapter can only be expected to make a 'best effort' in honoring this +// request but there are no guarantees. The `cancel` request may return an error +// if it could not cancel an operation but a client should refrain from +// presenting this error to end users. The request that got cancelled still +// needs to send a response back. This can either be a normal result (`success` +// attribute true) or an error response (`success` attribute false and the +// `message` set to `cancelled`). Returning partial results from a cancelled +// request is possible but please note that a client has no generic way for +// detecting that a response is partial or not. The progress that got cancelled +// still needs to send a `progressEnd` event back. +// A client should not assume that progress just got cancelled after sending +// the `cancel` request. +struct CancelRequest : public Request { + using Response = CancelResponse; + // The ID (attribute `progressId`) of the progress to cancel. If missing no + // progress is cancelled. Both a `requestId` and a `progressId` can be + // specified in one request. + optional progressId; + // The ID (attribute `seq`) of the request to cancel. If missing no request is + // cancelled. Both a `requestId` and a `progressId` can be specified in one + // request. + optional requestId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(CancelRequest); + +// A `ColumnDescriptor` specifies what module attribute to show in a column of +// the modules view, how to format it, and what the column's label should be. It +// is only used if the underlying UI actually supports this level of +// customization. +struct ColumnDescriptor { + // Name of the attribute rendered in this column. + string attributeName; + // Format to use for the rendered values in this column. TBD how the format + // strings looks like. + optional format; + // Header UI label of column. + string label; + // Datatype of values in this column. Defaults to `string` if not specified. + // + // Must be one of the following enumeration values: + // 'string', 'number', 'boolean', 'unixTimestampUTC' + optional type; + // Width of this column in characters (hint only). + optional width; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ColumnDescriptor); + +// Describes one or more type of breakpoint a `BreakpointMode` applies to. This +// is a non-exhaustive enumeration and may expand as future breakpoint types are +// added. +using BreakpointModeApplicability = string; + +// A `BreakpointMode` is provided as a option when setting breakpoints on +// sources or instructions. +struct BreakpointMode { + // Describes one or more type of breakpoint this mode applies to. + array appliesTo; + // A help text providing additional information about the breakpoint mode. + // This string is typically shown as a hover and can be translated. + optional description; + // The name of the breakpoint mode. This is shown in the UI. + string label; + // The internal ID of the mode. This value is passed to the `setBreakpoints` + // request. + string mode; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointMode); + +// An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for +// configuring how exceptions are dealt with. +struct ExceptionBreakpointsFilter { + // A help text providing information about the condition. This string is shown + // as the placeholder text for a text box and can be translated. + optional conditionDescription; + // Initial value of the filter option. If not specified a value false is + // assumed. + optional def; + // A help text providing additional information about the exception filter. + // This string is typically shown as a hover and can be translated. + optional description; + // The internal ID of the filter option. This value is passed to the + // `setExceptionBreakpoints` request. + string filter; + // The name of the filter option. This is shown in the UI. + string label; + // Controls whether a condition can be specified for this filter option. If + // false or missing, a condition can not be set. + optional supportsCondition; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionBreakpointsFilter); + +// Information about the capabilities of a debug adapter. +struct Capabilities { + // The set of additional module information exposed by the debug adapter. + optional> additionalModuleColumns; + // Modes of breakpoints supported by the debug adapter, such as 'hardware' or + // 'software'. If present, the client may allow the user to select a mode and + // include it in its `setBreakpoints` request. + // + // Clients may present the first applicable mode in this array as the + // 'default' mode in gestures that set breakpoints. + optional> breakpointModes; + // The set of characters that should trigger completion in a REPL. If not + // specified, the UI should assume the `.` character. + optional> completionTriggerCharacters; + // Available exception filter options for the `setExceptionBreakpoints` + // request. + optional> exceptionBreakpointFilters; + // The debug adapter supports the `suspendDebuggee` attribute on the + // `disconnect` request. + optional supportSuspendDebuggee; + // The debug adapter supports the `terminateDebuggee` attribute on the + // `disconnect` request. + optional supportTerminateDebuggee; + // Checksum algorithms supported by the debug adapter. + optional> supportedChecksumAlgorithms; + // The debug adapter supports ANSI escape sequences in styling of + // `OutputEvent.output` and `Variable.value` fields. + optional supportsANSIStyling; + // The debug adapter supports the `breakpointLocations` request. + optional supportsBreakpointLocationsRequest; + // The debug adapter supports the `cancel` request. + optional supportsCancelRequest; + // The debug adapter supports the `clipboard` context value in the `evaluate` + // request. + optional supportsClipboardContext; + // The debug adapter supports the `completions` request. + optional supportsCompletionsRequest; + // The debug adapter supports conditional breakpoints. + optional supportsConditionalBreakpoints; + // The debug adapter supports the `configurationDone` request. + optional supportsConfigurationDoneRequest; + // The debug adapter supports the `asAddress` and `bytes` fields in the + // `dataBreakpointInfo` request. + optional supportsDataBreakpointBytes; + // The debug adapter supports data breakpoints. + optional supportsDataBreakpoints; + // The debug adapter supports the delayed loading of parts of the stack, which + // requires that both the `startFrame` and `levels` arguments and the + // `totalFrames` result of the `stackTrace` request are supported. + optional supportsDelayedStackTraceLoading; + // The debug adapter supports the `disassemble` request. + optional supportsDisassembleRequest; + // The debug adapter supports a (side effect free) `evaluate` request for data + // hovers. + optional supportsEvaluateForHovers; + // The debug adapter supports `filterOptions` as an argument on the + // `setExceptionBreakpoints` request. + optional supportsExceptionFilterOptions; + // The debug adapter supports the `exceptionInfo` request. + optional supportsExceptionInfoRequest; + // The debug adapter supports `exceptionOptions` on the + // `setExceptionBreakpoints` request. + optional supportsExceptionOptions; + // The debug adapter supports function breakpoints. + optional supportsFunctionBreakpoints; + // The debug adapter supports the `gotoTargets` request. + optional supportsGotoTargetsRequest; + // The debug adapter supports breakpoints that break execution after a + // specified number of hits. + optional supportsHitConditionalBreakpoints; + // The debug adapter supports adding breakpoints based on instruction + // references. + optional supportsInstructionBreakpoints; + // The debug adapter supports the `loadedSources` request. + optional supportsLoadedSourcesRequest; + // The debug adapter supports log points by interpreting the `logMessage` + // attribute of the `SourceBreakpoint`. + optional supportsLogPoints; + // The debug adapter supports the `modules` request. + optional supportsModulesRequest; + // The debug adapter supports the `readMemory` request. + optional supportsReadMemoryRequest; + // The debug adapter supports restarting a frame. + optional supportsRestartFrame; + // The debug adapter supports the `restart` request. In this case a client + // should not implement `restart` by terminating and relaunching the adapter + // but by calling the `restart` request. + optional supportsRestartRequest; + // The debug adapter supports the `setExpression` request. + optional supportsSetExpression; + // The debug adapter supports setting a variable to a value. + optional supportsSetVariable; + // The debug adapter supports the `singleThread` property on the execution + // requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, + // `stepBack`). + optional supportsSingleThreadExecutionRequests; + // The debug adapter supports stepping back via the `stepBack` and + // `reverseContinue` requests. + optional supportsStepBack; + // The debug adapter supports the `stepInTargets` request. + optional supportsStepInTargetsRequest; + // The debug adapter supports stepping granularities (argument `granularity`) + // for the stepping requests. + optional supportsSteppingGranularity; + // The debug adapter supports the `terminate` request. + optional supportsTerminateRequest; + // The debug adapter supports the `terminateThreads` request. + optional supportsTerminateThreadsRequest; + // The debug adapter supports a `format` attribute on the `stackTrace`, + // `variables`, and `evaluate` requests. + optional supportsValueFormattingOptions; + // The debug adapter supports the `writeMemory` request. + optional supportsWriteMemoryRequest; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Capabilities); + +// The event indicates that one or more capabilities have changed. +// Since the capabilities are dependent on the client and its UI, it might not +// be possible to change that at random times (or too late). Consequently this +// event has a hint characteristic: a client can only be expected to make a +// 'best effort' in honoring individual capabilities but there are no +// guarantees. Only changed capabilities need to be included, all other +// capabilities keep their values. +struct CapabilitiesEvent : public Event { + // The set of updated capabilities. + Capabilities capabilities; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(CapabilitiesEvent); + +// Some predefined types for the CompletionItem. Please note that not all +// clients have specific icons for all of them. +// +// Must be one of the following enumeration values: +// 'method', 'function', 'constructor', 'field', 'variable', 'class', +// 'interface', 'module', 'property', 'unit', 'value', 'enum', 'keyword', +// 'snippet', 'text', 'color', 'file', 'reference', 'customcolor' +using CompletionItemType = string; + +// `CompletionItems` are the suggestions returned from the `completions` +// request. +struct CompletionItem { + // A human-readable string with additional information about this item, like + // type or symbol information. + optional detail; + // The label of this completion item. By default this is also the text that is + // inserted when selecting this completion. + string label; + // Length determines how many characters are overwritten by the completion + // text and it is measured in UTF-16 code units. If missing the value 0 is + // assumed which results in the completion text being inserted. + optional length; + // Determines the length of the new selection after the text has been inserted + // (or replaced) and it is measured in UTF-16 code units. The selection can + // not extend beyond the bounds of the completion text. If omitted the length + // is assumed to be 0. + optional selectionLength; + // Determines the start of the new selection after the text has been inserted + // (or replaced). `selectionStart` is measured in UTF-16 code units and must + // be in the range 0 and length of the completion text. If omitted the + // selection starts at the end of the completion text. + optional selectionStart; + // A string that should be used when comparing this item with other items. If + // not returned or an empty string, the `label` is used instead. + optional sortText; + // Start position (within the `text` attribute of the `completions` request) + // where the completion text is added. The position is measured in UTF-16 code + // units and the client capability `columnsStartAt1` determines whether it is + // 0- or 1-based. If the start position is omitted the text is added at the + // location specified by the `column` attribute of the `completions` request. + optional start; + // If text is returned and not an empty string, then it is inserted instead of + // the label. + optional text; + // The item's type. Typically the client uses this information to render the + // item in the UI with an icon. + optional type; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(CompletionItem); + +// Response to `completions` request. +struct CompletionsResponse : public Response { + // The possible completions for . + array targets; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(CompletionsResponse); + +// Returns a list of possible completions for a given caret position and text. +// Clients should only call this request if the corresponding capability +// `supportsCompletionsRequest` is true. +struct CompletionsRequest : public Request { + using Response = CompletionsResponse; + // The position within `text` for which to determine the completion proposals. + // It is measured in UTF-16 code units and the client capability + // `columnsStartAt1` determines whether it is 0- or 1-based. + integer column; + // Returns completions in the scope of this stack frame. If not specified, the + // completions are returned for the global scope. + optional frameId; + // A line for which to determine the completion proposals. If missing the + // first line of the text is assumed. + optional line; + // One or more source lines. Typically this is the text users have typed into + // the debug console before they asked for completion. + string text; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(CompletionsRequest); + +// Response to `configurationDone` request. This is just an acknowledgement, so +// no body field is required. +struct ConfigurationDoneResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(ConfigurationDoneResponse); + +// This request indicates that the client has finished initialization of the +// debug adapter. So it is the last request in the sequence of configuration +// requests (which was started by the `initialized` event). Clients should only +// call this request if the corresponding capability +// `supportsConfigurationDoneRequest` is true. +struct ConfigurationDoneRequest : public Request { + using Response = ConfigurationDoneResponse; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ConfigurationDoneRequest); + +// Response to `continue` request. +struct ContinueResponse : public Response { + // The value true (or a missing property) signals to the client that all + // threads have been resumed. The value false indicates that not all threads + // were resumed. + optional allThreadsContinued; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ContinueResponse); + +// The request resumes execution of all threads. If the debug adapter supports +// single thread execution (see capability +// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument +// to true resumes only the specified thread. If not all threads were resumed, +// the `allThreadsContinued` attribute of the response should be set to false. +struct ContinueRequest : public Request { + using Response = ContinueResponse; + // If this flag is true, execution is resumed only for the thread with given + // `threadId`. + optional singleThread; + // Specifies the active thread. If the debug adapter supports single thread + // execution (see `supportsSingleThreadExecutionRequests`) and the argument + // `singleThread` is true, only the thread with this ID is resumed. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ContinueRequest); + +// The event indicates that the execution of the debuggee has continued. +// Please note: a debug adapter is not expected to send this event in response +// to a request that implies that execution continues, e.g. `launch` or +// `continue`. It is only necessary to send a `continued` event if there was no +// previous request that implied this. +struct ContinuedEvent : public Event { + // If `allThreadsContinued` is true, a debug adapter can announce that all + // threads have continued. + optional allThreadsContinued; + // The thread which was continued. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ContinuedEvent); + +// This enumeration defines all possible access types for data breakpoints. +// +// Must be one of the following enumeration values: +// 'read', 'write', 'readWrite' +using DataBreakpointAccessType = string; + +// Response to `dataBreakpointInfo` request. +struct DataBreakpointInfoResponse : public Response { + // Attribute lists the available access types for a potential data breakpoint. + // A UI client could surface this information. + optional> accessTypes; + // Attribute indicates that a potential data breakpoint could be persisted + // across sessions. + optional canPersist; + // An identifier for the data on which a data breakpoint can be registered + // with the `setDataBreakpoints` request or null if no data breakpoint is + // available. If a `variablesReference` or `frameId` is passed, the `dataId` + // is valid in the current suspended state, otherwise it's valid indefinitely. + // See 'Lifetime of Object References' in the Overview section for details. + // Breakpoints set using the `dataId` in the `setDataBreakpoints` request may + // outlive the lifetime of the associated `dataId`. + variant dataId; + // UI string that describes on what data the breakpoint is set on or why a + // data breakpoint is not available. + string description; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpointInfoResponse); + +// Obtains information on a possible data breakpoint that could be set on an +// expression or variable. Clients should only call this request if the +// corresponding capability `supportsDataBreakpoints` is true. +struct DataBreakpointInfoRequest : public Request { + using Response = DataBreakpointInfoResponse; + // If `true`, the `name` is a memory address and the debugger should interpret + // it as a decimal value, or hex value if it is prefixed with `0x`. + // + // Clients may set this property only if the `supportsDataBreakpointBytes` + // capability is true. + optional asAddress; + // If specified, a debug adapter should return information for the range of + // memory extending `bytes` number of bytes from the address or variable + // specified by `name`. Breakpoints set using the resulting data ID should + // pause on data access anywhere within that range. + // + // Clients may set this property only if the `supportsDataBreakpointBytes` + // capability is true. + optional bytes; + // When `name` is an expression, evaluate it in the scope of this stack frame. + // If not specified, the expression is evaluated in the global scope. When + // `variablesReference` is specified, this property has no effect. + optional frameId; + // The mode of the desired breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; + // The name of the variable's child to obtain data breakpoint information for. + // If `variablesReference` isn't specified, this can be an expression, or an + // address if `asAddress` is also true. + string name; + // Reference to the variable container if the data breakpoint is requested for + // a child of the container. The `variablesReference` must have been obtained + // in the current suspended state. See 'Lifetime of Object References' in the + // Overview section for details. + optional variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpointInfoRequest); + +// Represents a single disassembled instruction. +struct DisassembledInstruction { + // The address of the instruction. Treated as a hex value if prefixed with + // `0x`, or as a decimal value otherwise. + string address; + // The column within the line that corresponds to this instruction, if any. + optional column; + // The end column of the range that corresponds to this instruction, if any. + optional endColumn; + // The end line of the range that corresponds to this instruction, if any. + optional endLine; + // Text representing the instruction and its operands, in an + // implementation-defined format. + string instruction; + // Raw bytes representing the instruction and its operands, in an + // implementation-defined format. + optional instructionBytes; + // The line within the source location that corresponds to this instruction, + // if any. + optional line; + // Source location that corresponds to this instruction, if any. + // Should always be set (if available) on the first instruction returned, + // but can be omitted afterwards if this instruction maps to the same source + // file as the previous instruction. + optional location; + // A hint for how to present the instruction in the UI. + // + // A value of `invalid` may be used to indicate this instruction is 'filler' + // and cannot be reached by the program. For example, unreadable memory + // addresses may be presented is 'invalid.' + // + // Must be one of the following enumeration values: + // 'normal', 'invalid' + optional presentationHint; + // Name of the symbol that corresponds with the location of this instruction, + // if any. + optional symbol; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DisassembledInstruction); + +// Response to `disassemble` request. +struct DisassembleResponse : public Response { + // The list of disassembled instructions. + array instructions; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DisassembleResponse); + +// Disassembles code stored at the provided location. +// Clients should only call this request if the corresponding capability +// `supportsDisassembleRequest` is true. +struct DisassembleRequest : public Request { + using Response = DisassembleResponse; + // Number of instructions to disassemble starting at the specified location + // and offset. An adapter must return exactly this number of instructions - + // any unavailable instructions should be replaced with an + // implementation-defined 'invalid instruction' value. + integer instructionCount; + // Offset (in instructions) to be applied after the byte offset (if any) + // before disassembling. Can be negative. + optional instructionOffset; + // Memory reference to the base location containing the instructions to + // disassemble. + string memoryReference; + // Offset (in bytes) to be applied to the reference location before + // disassembling. Can be negative. + optional offset; + // If true, the adapter should attempt to resolve memory addresses and other + // values to symbolic names. + optional resolveSymbols; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DisassembleRequest); + +// Response to `disconnect` request. This is just an acknowledgement, so no body +// field is required. +struct DisconnectResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(DisconnectResponse); + +// The `disconnect` request asks the debug adapter to disconnect from the +// debuggee (thus ending the debug session) and then to shut down itself (the +// debug adapter). In addition, the debug adapter must terminate the debuggee if +// it was started with the `launch` request. If an `attach` request was used to +// connect to the debuggee, then the debug adapter must not terminate the +// debuggee. This implicit behavior of when to terminate the debuggee can be +// overridden with the `terminateDebuggee` argument (which is only supported by +// a debug adapter if the corresponding capability `supportTerminateDebuggee` is +// true). +struct DisconnectRequest : public Request { + using Response = DisconnectResponse; + // A value of true indicates that this `disconnect` request is part of a + // restart sequence. + optional restart; + // Indicates whether the debuggee should stay suspended when the debugger is + // disconnected. If unspecified, the debuggee should resume execution. The + // attribute is only honored by a debug adapter if the corresponding + // capability `supportSuspendDebuggee` is true. + optional suspendDebuggee; + // Indicates whether the debuggee should be terminated when the debugger is + // disconnected. If unspecified, the debug adapter is free to do whatever it + // thinks is best. The attribute is only honored by a debug adapter if the + // corresponding capability `supportTerminateDebuggee` is true. + optional terminateDebuggee; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DisconnectRequest); + +// A structured message object. Used to return errors from requests. +struct Message { + // A format string for the message. Embedded variables have the form `{name}`. + // If variable name starts with an underscore character, the variable does not + // contain user data (PII) and can be safely used for telemetry purposes. + string format; + // Unique (within a debug adapter implementation) identifier for the message. + // The purpose of these error IDs is to help extension authors that have the + // requirement that every user visible error message needs a corresponding + // error number, so that users or customer support can find information about + // the specific error more easily. + integer id; + // If true send to telemetry. + optional sendTelemetry; + // If true show user. + optional showUser; + // A url where additional information about this message can be found. + optional url; + // A label that is presented to the user as the UI for opening the url. + optional urlLabel; + // An object used as a dictionary for looking up the variables in the format + // string. + optional variables; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Message); + +// On error (whenever `success` is false), the body can provide more details. +struct ErrorResponse : public Response { + // A structured error message. + optional error; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ErrorResponse); + +// Properties of a variable that can be used to determine how to render the +// variable in the UI. +struct VariablePresentationHint { + // Set of attributes represented as an array of strings. Before introducing + // additional values, try to use the listed values. + optional> attributes; + // The kind of variable. Before introducing additional values, try to use the + // listed values. + // + // May be one of the following enumeration values: + // 'property', 'method', 'class', 'data', 'event', 'baseClass', 'innerClass', + // 'interface', 'mostDerivedClass', 'virtual', 'dataBreakpoint' + optional kind; + // If true, clients can present the variable with a UI that supports a + // specific gesture to trigger its evaluation. This mechanism can be used for + // properties that require executing code when retrieving their value and + // where the code execution can be expensive and/or produce side-effects. A + // typical example are properties based on a getter function. Please note that + // in addition to the `lazy` flag, the variable's `variablesReference` is + // expected to refer to a variable that will provide the value through another + // `variable` request. + optional lazy; + // Visibility of variable. Before introducing additional values, try to use + // the listed values. + // + // May be one of the following enumeration values: + // 'public', 'private', 'protected', 'internal', 'final' + optional visibility; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(VariablePresentationHint); + +// Response to `evaluate` request. +struct EvaluateResponse : public Response { + // The number of indexed child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional indexedVariables; + // A memory reference to a location appropriate for this result. + // For pointer type eval results, this is generally a reference to the memory + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. + optional memoryReference; + // The number of named child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional namedVariables; + // Properties of an evaluate result that can be used to determine how to + // render the result in the UI. + optional presentationHint; + // The result of the evaluate request. + string result; + // The type of the evaluate result. + // This attribute should only be returned by a debug adapter if the + // corresponding capability `supportsVariableType` is true. + optional type; + // A reference that allows the client to request the location where the + // returned value is declared. For example, if a function pointer is returned, + // the adapter may be able to look up the function's location. This should be + // present only if the adapter is likely to be able to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional valueLocationReference; + // If `variablesReference` is > 0, the evaluate result is structured and its + // children can be retrieved by passing `variablesReference` to the + // `variables` request as long as execution remains suspended. See 'Lifetime + // of Object References' in the Overview section for details. + integer variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(EvaluateResponse); + +// Provides formatting information for a value. +struct ValueFormat { + // Display the value in hex. + optional hex; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ValueFormat); + +// Evaluates the given expression in the context of a stack frame. +// The expression has access to any variables and arguments that are in scope. +struct EvaluateRequest : public Request { + using Response = EvaluateResponse; + // The contextual column where the expression should be evaluated. This may be + // provided if `line` is also provided. + // + // It is measured in UTF-16 code units and the client capability + // `columnsStartAt1` determines whether it is 0- or 1-based. + optional column; + // The context in which the evaluate request is used. + // + // May be one of the following enumeration values: + // 'watch', 'repl', 'hover', 'clipboard', 'variables' + optional context; + // The expression to evaluate. + string expression; + // Specifies details on how to format the result. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsValueFormattingOptions` is true. + optional format; + // Evaluate the expression in the scope of this stack frame. If not specified, + // the expression is evaluated in the global scope. + optional frameId; + // The contextual line where the expression should be evaluated. In the + // 'hover' context, this should be set to the start of the expression being + // hovered. + optional line; + // The contextual source in which the `line` is found. This must be provided + // if `line` is provided. + optional source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(EvaluateRequest); + +// This enumeration defines all possible conditions when a thrown exception +// should result in a break. never: never breaks, always: always breaks, +// unhandled: breaks when exception unhandled, +// userUnhandled: breaks if the exception is not handled by user code. +// +// Must be one of the following enumeration values: +// 'never', 'always', 'unhandled', 'userUnhandled' +using ExceptionBreakMode = string; + +// Detailed information about an exception that has occurred. +struct ExceptionDetails { + // An expression that can be evaluated in the current scope to obtain the + // exception object. + optional evaluateName; + // Fully-qualified type name of the exception object. + optional fullTypeName; + // Details of the exception contained by this exception, if any. + optional> innerException; + // Message contained in the exception. + optional message; + // Stack trace at the time the exception was thrown. + optional stackTrace; + // Short type name of the exception object. + optional typeName; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionDetails); + +// Response to `exceptionInfo` request. +struct ExceptionInfoResponse : public Response { + // Mode that caused the exception notification to be raised. + ExceptionBreakMode breakMode = "never"; + // Descriptive text for the exception. + optional description; + // Detailed information about the exception. + optional details; + // ID of the exception that was thrown. + string exceptionId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionInfoResponse); + +// Retrieves the details of the exception that caused this event to be raised. +// Clients should only call this request if the corresponding capability +// `supportsExceptionInfoRequest` is true. +struct ExceptionInfoRequest : public Request { + using Response = ExceptionInfoResponse; + // Thread for which exception information should be retrieved. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionInfoRequest); + +// The event indicates that the debuggee has exited and returns its exit code. +struct ExitedEvent : public Event { + // The exit code returned from the debuggee. + integer exitCode; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExitedEvent); + +// Response to `goto` request. This is just an acknowledgement, so no body field +// is required. +struct GotoResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(GotoResponse); + +// The request sets the location where the debuggee will continue to run. +// This makes it possible to skip the execution of code or to execute code +// again. The code between the current location and the goto target is not +// executed but skipped. The debug adapter first sends the response and then a +// `stopped` event with reason `goto`. Clients should only call this request if +// the corresponding capability `supportsGotoTargetsRequest` is true (because +// only then goto targets exist that can be passed as arguments). +struct GotoRequest : public Request { + using Response = GotoResponse; + // The location where the debuggee will continue to run. + integer targetId; + // Set the goto target for this thread. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(GotoRequest); + +// A `GotoTarget` describes a code location that can be used as a target in the +// `goto` request. The possible goto targets can be determined via the +// `gotoTargets` request. +struct GotoTarget { + // The column of the goto target. + optional column; + // The end column of the range covered by the goto target. + optional endColumn; + // The end line of the range covered by the goto target. + optional endLine; + // Unique identifier for a goto target. This is used in the `goto` request. + integer id; + // A memory reference for the instruction pointer value represented by this + // target. + optional instructionPointerReference; + // The name of the goto target (shown in the UI). + string label; + // The line of the goto target. + integer line; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(GotoTarget); + +// Response to `gotoTargets` request. +struct GotoTargetsResponse : public Response { + // The possible goto targets of the specified location. + array targets; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(GotoTargetsResponse); + +// This request retrieves the possible goto targets for the specified source +// location. These targets can be used in the `goto` request. Clients should +// only call this request if the corresponding capability +// `supportsGotoTargetsRequest` is true. +struct GotoTargetsRequest : public Request { + using Response = GotoTargetsResponse; + // The position within `line` for which the goto targets are determined. It is + // measured in UTF-16 code units and the client capability `columnsStartAt1` + // determines whether it is 0- or 1-based. + optional column; + // The line location for which the goto targets are determined. + integer line; + // The source location for which the goto targets are determined. + Source source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(GotoTargetsRequest); + +// Response to `initialize` request. +struct InitializeResponse : public Response { + // The set of additional module information exposed by the debug adapter. + optional> additionalModuleColumns; + // Modes of breakpoints supported by the debug adapter, such as 'hardware' or + // 'software'. If present, the client may allow the user to select a mode and + // include it in its `setBreakpoints` request. + // + // Clients may present the first applicable mode in this array as the + // 'default' mode in gestures that set breakpoints. + optional> breakpointModes; + // The set of characters that should trigger completion in a REPL. If not + // specified, the UI should assume the `.` character. + optional> completionTriggerCharacters; + // Available exception filter options for the `setExceptionBreakpoints` + // request. + optional> exceptionBreakpointFilters; + // The debug adapter supports the `suspendDebuggee` attribute on the + // `disconnect` request. + optional supportSuspendDebuggee; + // The debug adapter supports the `terminateDebuggee` attribute on the + // `disconnect` request. + optional supportTerminateDebuggee; + // Checksum algorithms supported by the debug adapter. + optional> supportedChecksumAlgorithms; + // The debug adapter supports ANSI escape sequences in styling of + // `OutputEvent.output` and `Variable.value` fields. + optional supportsANSIStyling; + // The debug adapter supports the `breakpointLocations` request. + optional supportsBreakpointLocationsRequest; + // The debug adapter supports the `cancel` request. + optional supportsCancelRequest; + // The debug adapter supports the `clipboard` context value in the `evaluate` + // request. + optional supportsClipboardContext; + // The debug adapter supports the `completions` request. + optional supportsCompletionsRequest; + // The debug adapter supports conditional breakpoints. + optional supportsConditionalBreakpoints; + // The debug adapter supports the `configurationDone` request. + optional supportsConfigurationDoneRequest; + // The debug adapter supports the `asAddress` and `bytes` fields in the + // `dataBreakpointInfo` request. + optional supportsDataBreakpointBytes; + // The debug adapter supports data breakpoints. + optional supportsDataBreakpoints; + // The debug adapter supports the delayed loading of parts of the stack, which + // requires that both the `startFrame` and `levels` arguments and the + // `totalFrames` result of the `stackTrace` request are supported. + optional supportsDelayedStackTraceLoading; + // The debug adapter supports the `disassemble` request. + optional supportsDisassembleRequest; + // The debug adapter supports a (side effect free) `evaluate` request for data + // hovers. + optional supportsEvaluateForHovers; + // The debug adapter supports `filterOptions` as an argument on the + // `setExceptionBreakpoints` request. + optional supportsExceptionFilterOptions; + // The debug adapter supports the `exceptionInfo` request. + optional supportsExceptionInfoRequest; + // The debug adapter supports `exceptionOptions` on the + // `setExceptionBreakpoints` request. + optional supportsExceptionOptions; + // The debug adapter supports function breakpoints. + optional supportsFunctionBreakpoints; + // The debug adapter supports the `gotoTargets` request. + optional supportsGotoTargetsRequest; + // The debug adapter supports breakpoints that break execution after a + // specified number of hits. + optional supportsHitConditionalBreakpoints; + // The debug adapter supports adding breakpoints based on instruction + // references. + optional supportsInstructionBreakpoints; + // The debug adapter supports the `loadedSources` request. + optional supportsLoadedSourcesRequest; + // The debug adapter supports log points by interpreting the `logMessage` + // attribute of the `SourceBreakpoint`. + optional supportsLogPoints; + // The debug adapter supports the `modules` request. + optional supportsModulesRequest; + // The debug adapter supports the `readMemory` request. + optional supportsReadMemoryRequest; + // The debug adapter supports restarting a frame. + optional supportsRestartFrame; + // The debug adapter supports the `restart` request. In this case a client + // should not implement `restart` by terminating and relaunching the adapter + // but by calling the `restart` request. + optional supportsRestartRequest; + // The debug adapter supports the `setExpression` request. + optional supportsSetExpression; + // The debug adapter supports setting a variable to a value. + optional supportsSetVariable; + // The debug adapter supports the `singleThread` property on the execution + // requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, + // `stepBack`). + optional supportsSingleThreadExecutionRequests; + // The debug adapter supports stepping back via the `stepBack` and + // `reverseContinue` requests. + optional supportsStepBack; + // The debug adapter supports the `stepInTargets` request. + optional supportsStepInTargetsRequest; + // The debug adapter supports stepping granularities (argument `granularity`) + // for the stepping requests. + optional supportsSteppingGranularity; + // The debug adapter supports the `terminate` request. + optional supportsTerminateRequest; + // The debug adapter supports the `terminateThreads` request. + optional supportsTerminateThreadsRequest; + // The debug adapter supports a `format` attribute on the `stackTrace`, + // `variables`, and `evaluate` requests. + optional supportsValueFormattingOptions; + // The debug adapter supports the `writeMemory` request. + optional supportsWriteMemoryRequest; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(InitializeResponse); + +// The `initialize` request is sent as the first request from the client to the +// debug adapter in order to configure it with client capabilities and to +// retrieve capabilities from the debug adapter. Until the debug adapter has +// responded with an `initialize` response, the client must not send any +// additional requests or events to the debug adapter. In addition the debug +// adapter is not allowed to send any requests or events to the client until it +// has responded with an `initialize` response. The `initialize` request may +// only be sent once. +struct InitializeRequest : public Request { + using Response = InitializeResponse; + // The ID of the debug adapter. + string adapterID; + // The ID of the client using this adapter. + optional clientID; + // The human-readable name of the client using this adapter. + optional clientName; + // If true all column numbers are 1-based (default). + optional columnsStartAt1; + // If true all line numbers are 1-based (default). + optional linesStartAt1; + // The ISO-639 locale of the client using this adapter, e.g. en-US or de-CH. + optional locale; + // Determines in what format paths are specified. The default is `path`, which + // is the native format. + // + // May be one of the following enumeration values: + // 'path', 'uri' + optional pathFormat; + // The client will interpret ANSI escape sequences in the display of + // `OutputEvent.output` and `Variable.value` fields when + // `Capabilities.supportsANSIStyling` is also enabled. + optional supportsANSIStyling; + // Client supports the `argsCanBeInterpretedByShell` attribute on the + // `runInTerminal` request. + optional supportsArgsCanBeInterpretedByShell; + // Client supports the `invalidated` event. + optional supportsInvalidatedEvent; + // Client supports the `memory` event. + optional supportsMemoryEvent; + // Client supports memory references. + optional supportsMemoryReferences; + // Client supports progress reporting. + optional supportsProgressReporting; + // Client supports the `runInTerminal` request. + optional supportsRunInTerminalRequest; + // Client supports the `startDebugging` request. + optional supportsStartDebuggingRequest; + // Client supports the paging of variables. + optional supportsVariablePaging; + // Client supports the `type` attribute for variables. + optional supportsVariableType; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(InitializeRequest); + +// This event indicates that the debug adapter is ready to accept configuration +// requests (e.g. `setBreakpoints`, `setExceptionBreakpoints`). A debug adapter +// is expected to send this event when it is ready to accept configuration +// requests (but not before the `initialize` request has finished). The sequence +// of events/requests is as follows: +// - adapters sends `initialized` event (after the `initialize` request has +// returned) +// - client sends zero or more `setBreakpoints` requests +// - client sends one `setFunctionBreakpoints` request (if corresponding +// capability `supportsFunctionBreakpoints` is true) +// - client sends a `setExceptionBreakpoints` request if one or more +// `exceptionBreakpointFilters` have been defined (or if +// `supportsConfigurationDoneRequest` is not true) +// - client sends other future configuration requests +// - client sends one `configurationDone` request to indicate the end of the +// configuration. +struct InitializedEvent : public Event {}; + +DAP_DECLARE_STRUCT_TYPEINFO(InitializedEvent); + +// Logical areas that can be invalidated by the `invalidated` event. +using InvalidatedAreas = string; + +// This event signals that some state in the debug adapter has changed and +// requires that the client needs to re-render the data snapshot previously +// requested. Debug adapters do not have to emit this event for runtime changes +// like stopped or thread events because in that case the client refetches the +// new state anyway. But the event can be used for example to refresh the UI +// after rendering formatting has changed in the debug adapter. This event +// should only be sent if the corresponding capability +// `supportsInvalidatedEvent` is true. +struct InvalidatedEvent : public Event { + // Set of logical areas that got invalidated. This property has a hint + // characteristic: a client can only be expected to make a 'best effort' in + // honoring the areas but there are no guarantees. If this property is + // missing, empty, or if values are not understood, the client should assume a + // single value `all`. + optional> areas; + // If specified, the client only needs to refetch data related to this stack + // frame (and the `threadId` is ignored). + optional stackFrameId; + // If specified, the client only needs to refetch data related to this thread. + optional threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(InvalidatedEvent); + +// Response to `launch` request. This is just an acknowledgement, so no body +// field is required. +struct LaunchResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(LaunchResponse); + +// This launch request is sent from the client to the debug adapter to start the +// debuggee with or without debugging (if `noDebug` is true). Since launching is +// debugger/runtime specific, the arguments for this request are not part of +// this specification. +struct LaunchRequest : public Request { + using Response = LaunchResponse; + // Arbitrary data from the previous, restarted session. + // The data is sent as the `restart` attribute of the `terminated` event. + // The client should leave the data intact. + optional, boolean, integer, null, number, object, string>> + restart; + // If true, the launch request should launch the program without enabling + // debugging. + optional noDebug; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LaunchRequest); + +// The event indicates that some source has been added, changed, or removed from +// the set of all loaded sources. +struct LoadedSourceEvent : public Event { + // The reason for the event. + // + // Must be one of the following enumeration values: + // 'new', 'changed', 'removed' + string reason = "new"; + // The new, changed, or removed source. + Source source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourceEvent); + +// Response to `loadedSources` request. +struct LoadedSourcesResponse : public Response { + // Set of loaded sources. + array sources; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourcesResponse); + +// Retrieves the set of all sources currently loaded by the debugged process. +// Clients should only call this request if the corresponding capability +// `supportsLoadedSourcesRequest` is true. +struct LoadedSourcesRequest : public Request { + using Response = LoadedSourcesResponse; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourcesRequest); + +// Response to `locations` request. +struct LocationsResponse : public Response { + // Position of the location within the `line`. It is measured in UTF-16 code + // units and the client capability `columnsStartAt1` determines whether it is + // 0- or 1-based. If no column is given, the first position in the start line + // is assumed. + optional column; + // End position of the location within `endLine`, present if the location + // refers to a range. It is measured in UTF-16 code units and the client + // capability `columnsStartAt1` determines whether it is 0- or 1-based. + optional endColumn; + // End line of the location, present if the location refers to a range. The + // client capability `linesStartAt1` determines whether it is 0- or 1-based. + optional endLine; + // The line number of the location. The client capability `linesStartAt1` + // determines whether it is 0- or 1-based. + integer line; + // The source containing the location; either `source.path` or + // `source.sourceReference` must be specified. + Source source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LocationsResponse); + +// Looks up information about a location reference previously returned by the +// debug adapter. +struct LocationsRequest : public Request { + using Response = LocationsResponse; + // Location reference to resolve. + integer locationReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(LocationsRequest); + +// This event indicates that some memory range has been updated. It should only +// be sent if the corresponding capability `supportsMemoryEvent` is true. +// Clients typically react to the event by re-issuing a `readMemory` request if +// they show the memory identified by the `memoryReference` and if the updated +// memory range overlaps the displayed range. Clients should not make +// assumptions how individual memory references relate to each other, so they +// should not assume that they are part of a single continuous address range and +// might overlap. Debug adapters can use this event to indicate that the +// contents of a memory range has changed due to some other request like +// `setVariable` or `setExpression`. Debug adapters are not expected to emit +// this event for each and every memory change of a running program, because +// that information is typically not available from debuggers and it would flood +// clients with too many events. +struct MemoryEvent : public Event { + // Number of bytes updated. + integer count; + // Memory reference of a memory range that has been updated. + string memoryReference; + // Starting offset in bytes where memory has been updated. Can be negative. + integer offset; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(MemoryEvent); + +// A Module object represents a row in the modules view. +// The `id` attribute identifies a module in the modules view and is used in a +// `module` event for identifying a module for adding, updating or deleting. The +// `name` attribute is used to minimally render the module in the UI. +// +// Additional attributes can be added to the module. They show up in the module +// view if they have a corresponding `ColumnDescriptor`. +// +// To avoid an unnecessary proliferation of additional attributes with similar +// semantics but different names, we recommend to re-use attributes from the +// 'recommended' list below first, and only introduce new attributes if nothing +// appropriate could be found. +struct Module { + // Address range covered by this module. + optional addressRange; + // Module created or modified, encoded as a RFC 3339 timestamp. + optional dateTimeStamp; + // Unique identifier for the module. + variant id; + // True if the module is optimized. + optional isOptimized; + // True if the module is considered 'user code' by a debugger that supports + // 'Just My Code'. + optional isUserCode; + // A name of the module. + string name; + // Logical full path to the module. The exact definition is implementation + // defined, but usually this would be a full path to the on-disk file for the + // module. + optional path; + // Logical full path to the symbol file. The exact definition is + // implementation defined. + optional symbolFilePath; + // User-understandable description of if symbols were found for the module + // (ex: 'Symbols Loaded', 'Symbols not found', etc.) + optional symbolStatus; + // Version of Module. + optional version; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Module); + +// The event indicates that some information about a module has changed. +struct ModuleEvent : public Event { + // The new, changed, or removed module. In case of `removed` only the module + // id is used. + Module module; + // The reason for the event. + // + // Must be one of the following enumeration values: + // 'new', 'changed', 'removed' + string reason = "new"; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ModuleEvent); + +// Response to `modules` request. +struct ModulesResponse : public Response { + // All modules or range of modules. + array modules; + // The total number of modules available. + optional totalModules; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ModulesResponse); + +// Modules can be retrieved from the debug adapter with this request which can +// either return all modules or a range of modules to support paging. Clients +// should only call this request if the corresponding capability +// `supportsModulesRequest` is true. +struct ModulesRequest : public Request { + using Response = ModulesResponse; + // The number of modules to return. If `moduleCount` is not specified or 0, + // all modules are returned. + optional moduleCount; + // The index of the first module to return; if omitted modules start at 0. + optional startModule; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ModulesRequest); + +// Response to `next` request. This is just an acknowledgement, so no body field +// is required. +struct NextResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(NextResponse); + +// The granularity of one 'step' in the stepping requests `next`, `stepIn`, +// `stepOut`, and `stepBack`. +// +// Must be one of the following enumeration values: +// 'statement', 'line', 'instruction' +using SteppingGranularity = string; + +// The request executes one step (in the given granularity) for the specified +// thread and allows all other threads to run freely by resuming them. If the +// debug adapter supports single thread execution (see capability +// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument +// to true prevents other suspended threads from resuming. The debug adapter +// first sends the response and then a `stopped` event (with reason `step`) +// after the step has completed. +struct NextRequest : public Request { + using Response = NextResponse; + // Stepping granularity. If no granularity is specified, a granularity of + // `statement` is assumed. + optional granularity; + // If this flag is true, all other suspended threads are not resumed. + optional singleThread; + // Specifies the thread for which to resume execution for one step (of the + // given granularity). + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(NextRequest); + +// The event indicates that the target has produced some output. +struct OutputEvent : public Event { + // The output category. If not specified or if the category is not understood + // by the client, `console` is assumed. + // + // May be one of the following enumeration values: + // 'console', 'important', 'stdout', 'stderr', 'telemetry' + optional category; + // The position in `line` where the output was produced. It is measured in + // UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. + optional column; + // Additional data to report. For the `telemetry` category the data is sent to + // telemetry, for the other categories the data is shown in JSON format. + optional, boolean, integer, null, number, object, string>> + data; + // Support for keeping an output log organized by grouping related messages. + // + // Must be one of the following enumeration values: + // 'start', 'startCollapsed', 'end' + optional group; + // The source location's line where the output was produced. + optional line; + // A reference that allows the client to request the location where the new + // value is declared. For example, if the logged value is function pointer, + // the adapter may be able to look up the function's location. This should be + // present only if the adapter is likely to be able to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional locationReference; + // The output to report. + // + // ANSI escape sequences may be used to influence text color and styling if + // `supportsANSIStyling` is present in both the adapter's `Capabilities` and + // the client's `InitializeRequestArguments`. A client may strip any + // unrecognized ANSI sequences. + // + // If the `supportsANSIStyling` capabilities are not both true, then the + // client should display the output literally. + string output; + // The source location where the output was produced. + optional source; + // If an attribute `variablesReference` exists and its value is > 0, the + // output contains objects which can be retrieved by passing + // `variablesReference` to the `variables` request as long as execution + // remains suspended. See 'Lifetime of Object References' in the Overview + // section for details. + optional variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(OutputEvent); + +// Response to `pause` request. This is just an acknowledgement, so no body +// field is required. +struct PauseResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(PauseResponse); + +// The request suspends the debuggee. +// The debug adapter first sends the response and then a `stopped` event (with +// reason `pause`) after the thread has been paused successfully. +struct PauseRequest : public Request { + using Response = PauseResponse; + // Pause execution for this thread. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(PauseRequest); + +// The event indicates that the debugger has begun debugging a new process. +// Either one that it has launched, or one that it has attached to. +struct ProcessEvent : public Event { + // If true, the process is running on the same computer as the debug adapter. + optional isLocalProcess; + // The logical name of the process. This is usually the full path to process's + // executable file. Example: /home/example/myproj/program.js. + string name; + // The size of a pointer or address for this process, in bits. This value may + // be used by clients when formatting addresses for display. + optional pointerSize; + // Describes how the debug engine started debugging this process. + // + // Must be one of the following enumeration values: + // 'launch', 'attach', 'attachForSuspendedLaunch' + optional startMethod; + // The process ID of the debugged process, as assigned by the operating + // system. This property should be omitted for logical processes that do not + // map to operating system processes on the machine. + optional systemProcessId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ProcessEvent); + +// The event signals the end of the progress reporting with a final message. +// This event should only be sent if the corresponding capability +// `supportsProgressReporting` is true. +struct ProgressEndEvent : public Event { + // More detailed progress message. If omitted, the previous message (if any) + // is used. + optional message; + // The ID that was introduced in the initial `ProgressStartEvent`. + string progressId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ProgressEndEvent); + +// The event signals that a long running operation is about to start and +// provides additional information for the client to set up a corresponding +// progress and cancellation UI. The client is free to delay the showing of the +// UI in order to reduce flicker. This event should only be sent if the +// corresponding capability `supportsProgressReporting` is true. +struct ProgressStartEvent : public Event { + // If true, the request that reports progress may be cancelled with a `cancel` + // request. So this property basically controls whether the client should use + // UX that supports cancellation. Clients that don't support cancellation are + // allowed to ignore the setting. + optional cancellable; + // More detailed progress message. + optional message; + // Progress percentage to display (value range: 0 to 100). If omitted no + // percentage is shown. + optional percentage; + // An ID that can be used in subsequent `progressUpdate` and `progressEnd` + // events to make them refer to the same progress reporting. IDs must be + // unique within a debug session. + string progressId; + // The request ID that this progress report is related to. If specified a + // debug adapter is expected to emit progress events for the long running + // request until the request has been either completed or cancelled. If the + // request ID is omitted, the progress report is assumed to be related to some + // general activity of the debug adapter. + optional requestId; + // Short title of the progress reporting. Shown in the UI to describe the long + // running operation. + string title; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ProgressStartEvent); + +// The event signals that the progress reporting needs to be updated with a new +// message and/or percentage. The client does not have to update the UI +// immediately, but the clients needs to keep track of the message and/or +// percentage values. This event should only be sent if the corresponding +// capability `supportsProgressReporting` is true. +struct ProgressUpdateEvent : public Event { + // More detailed progress message. If omitted, the previous message (if any) + // is used. + optional message; + // Progress percentage to display (value range: 0 to 100). If omitted no + // percentage is shown. + optional percentage; + // The ID that was introduced in the initial `progressStart` event. + string progressId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ProgressUpdateEvent); + +// Response to `readMemory` request. +struct ReadMemoryResponse : public Response { + // The address of the first byte of data returned. + // Treated as a hex value if prefixed with `0x`, or as a decimal value + // otherwise. + string address; + // The bytes read from memory, encoded using base64. If the decoded length of + // `data` is less than the requested `count` in the original `readMemory` + // request, and `unreadableBytes` is zero or omitted, then the client should + // assume it's reached the end of readable memory. + optional data; + // The number of unreadable bytes encountered after the last successfully read + // byte. This can be used to determine the number of bytes that should be + // skipped before a subsequent `readMemory` request succeeds. + optional unreadableBytes; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ReadMemoryResponse); + +// Reads bytes from memory at the provided location. +// Clients should only call this request if the corresponding capability +// `supportsReadMemoryRequest` is true. +struct ReadMemoryRequest : public Request { + using Response = ReadMemoryResponse; + // Number of bytes to read at the specified location and offset. + integer count; + // Memory reference to the base location from which data should be read. + string memoryReference; + // Offset (in bytes) to be applied to the reference location before reading + // data. Can be negative. + optional offset; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ReadMemoryRequest); + +// Response to `restartFrame` request. This is just an acknowledgement, so no +// body field is required. +struct RestartFrameResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(RestartFrameResponse); + +// The request restarts execution of the specified stack frame. +// The debug adapter first sends the response and then a `stopped` event (with +// reason `restart`) after the restart has completed. Clients should only call +// this request if the corresponding capability `supportsRestartFrame` is true. +struct RestartFrameRequest : public Request { + using Response = RestartFrameResponse; + // Restart the stack frame identified by `frameId`. The `frameId` must have + // been obtained in the current suspended state. See 'Lifetime of Object + // References' in the Overview section for details. + integer frameId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(RestartFrameRequest); + +// Response to `restart` request. This is just an acknowledgement, so no body +// field is required. +struct RestartResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(RestartResponse); + +// Restarts a debug session. Clients should only call this request if the +// corresponding capability `supportsRestartRequest` is true. If the capability +// is missing or has the value false, a typical client emulates `restart` by +// terminating the debug adapter first and then launching it anew. +struct RestartRequest : public Request { + using Response = RestartResponse; + // The latest version of the `launch` or `attach` configuration. + optional arguments; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(RestartRequest); + +// Response to `reverseContinue` request. This is just an acknowledgement, so no +// body field is required. +struct ReverseContinueResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(ReverseContinueResponse); + +// The request resumes backward execution of all threads. If the debug adapter +// supports single thread execution (see capability +// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument +// to true resumes only the specified thread. If not all threads were resumed, +// the `allThreadsContinued` attribute of the response should be set to false. +// Clients should only call this request if the corresponding capability +// `supportsStepBack` is true. +struct ReverseContinueRequest : public Request { + using Response = ReverseContinueResponse; + // If this flag is true, backward execution is resumed only for the thread + // with given `threadId`. + optional singleThread; + // Specifies the active thread. If the debug adapter supports single thread + // execution (see `supportsSingleThreadExecutionRequests`) and the + // `singleThread` argument is true, only the thread with this ID is resumed. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ReverseContinueRequest); + +// Response to `runInTerminal` request. +struct RunInTerminalResponse : public Response { + // The process ID. The value should be less than or equal to 2147483647 + // (2^31-1). + optional processId; + // The process ID of the terminal shell. The value should be less than or + // equal to 2147483647 (2^31-1). + optional shellProcessId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(RunInTerminalResponse); + +// This request is sent from the debug adapter to the client to run a command in +// a terminal. This is typically used to launch the debuggee in a terminal +// provided by the client. This request should only be called if the +// corresponding client capability `supportsRunInTerminalRequest` is true. +// Client implementations of `runInTerminal` are free to run the command however +// they choose including issuing the command to a command line interpreter (aka +// 'shell'). Argument strings passed to the `runInTerminal` request must arrive +// verbatim in the command to be run. As a consequence, clients which use a +// shell are responsible for escaping any special shell characters in the +// argument strings to prevent them from being interpreted (and modified) by the +// shell. Some users may wish to take advantage of shell processing in the +// argument strings. For clients which implement `runInTerminal` using an +// intermediary shell, the `argsCanBeInterpretedByShell` property can be set to +// true. In this case the client is requested not to escape any special shell +// characters in the argument strings. +struct RunInTerminalRequest : public Request { + using Response = RunInTerminalResponse; + // List of arguments. The first argument is the command to run. + array args; + // This property should only be set if the corresponding capability + // `supportsArgsCanBeInterpretedByShell` is true. If the client uses an + // intermediary shell to launch the application, then the client must not + // attempt to escape characters with special meanings for the shell. The user + // is fully responsible for escaping as needed and that arguments using + // special characters may not be portable across shells. + optional argsCanBeInterpretedByShell; + // Working directory for the command. For non-empty, valid paths this + // typically results in execution of a change directory command. + string cwd; + // Environment key-value pairs that are added to or removed from the default + // environment. + optional env; + // What kind of terminal to launch. Defaults to `integrated` if not specified. + // + // Must be one of the following enumeration values: + // 'integrated', 'external' + optional kind; + // Title of the terminal. + optional title; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(RunInTerminalRequest); + +// A `Scope` is a named container for variables. Optionally a scope can map to a +// source or a range within a source. +struct Scope { + // Start position of the range covered by the scope. It is measured in UTF-16 + // code units and the client capability `columnsStartAt1` determines whether + // it is 0- or 1-based. + optional column; + // End position of the range covered by the scope. It is measured in UTF-16 + // code units and the client capability `columnsStartAt1` determines whether + // it is 0- or 1-based. + optional endColumn; + // The end line of the range covered by this scope. + optional endLine; + // If true, the number of variables in this scope is large or expensive to + // retrieve. + boolean expensive; + // The number of indexed variables in this scope. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. + optional indexedVariables; + // The start line of the range covered by this scope. + optional line; + // Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This + // string is shown in the UI as is and can be translated. + string name; + // The number of named variables in this scope. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. + optional namedVariables; + // A hint for how to present this scope in the UI. If this attribute is + // missing, the scope is shown with a generic UI. + // + // May be one of the following enumeration values: + // 'arguments', 'locals', 'registers', 'returnValue' + optional presentationHint; + // The source for this scope. + optional source; + // The variables of this scope can be retrieved by passing the value of + // `variablesReference` to the `variables` request as long as execution + // remains suspended. See 'Lifetime of Object References' in the Overview + // section for details. + integer variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Scope); + +// Response to `scopes` request. +struct ScopesResponse : public Response { + // The scopes of the stack frame. If the array has length zero, there are no + // scopes available. + array scopes; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ScopesResponse); + +// The request returns the variable scopes for a given stack frame ID. +struct ScopesRequest : public Request { + using Response = ScopesResponse; + // Retrieve the scopes for the stack frame identified by `frameId`. The + // `frameId` must have been obtained in the current suspended state. See + // 'Lifetime of Object References' in the Overview section for details. + integer frameId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ScopesRequest); + +// Response to `setBreakpoints` request. +// Returned is information about each breakpoint created by this request. +// This includes the actual code location and whether the breakpoint could be +// verified. The breakpoints returned are in the same order as the elements of +// the `breakpoints` (or the deprecated `lines`) array in the arguments. +struct SetBreakpointsResponse : public Response { + // Information about the breakpoints. + // The array elements are in the same order as the elements of the + // `breakpoints` (or the deprecated `lines`) array in the arguments. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetBreakpointsResponse); + +// Properties of a breakpoint or logpoint passed to the `setBreakpoints` +// request. +struct SourceBreakpoint { + // Start position within source line of the breakpoint or logpoint. It is + // measured in UTF-16 code units and the client capability `columnsStartAt1` + // determines whether it is 0- or 1-based. + optional column; + // The expression for conditional breakpoints. + // It is only honored by a debug adapter if the corresponding capability + // `supportsConditionalBreakpoints` is true. + optional condition; + // The expression that controls how many hits of the breakpoint are ignored. + // The debug adapter is expected to interpret the expression as needed. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsHitConditionalBreakpoints` is true. If both this + // property and `condition` are specified, `hitCondition` should be evaluated + // only if the `condition` is met, and the debug adapter should stop only if + // both conditions are met. + optional hitCondition; + // The source line of the breakpoint or logpoint. + integer line; + // If this attribute exists and is non-empty, the debug adapter must not + // 'break' (stop) but log the message instead. Expressions within `{}` are + // interpolated. The attribute is only honored by a debug adapter if the + // corresponding capability `supportsLogPoints` is true. If either + // `hitCondition` or `condition` is specified, then the message should only be + // logged if those conditions are met. + optional logMessage; + // The mode of this breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SourceBreakpoint); + +// Sets multiple breakpoints for a single source and clears all previous +// breakpoints in that source. To clear all breakpoint for a source, specify an +// empty array. When a breakpoint is hit, a `stopped` event (with reason +// `breakpoint`) is generated. +struct SetBreakpointsRequest : public Request { + using Response = SetBreakpointsResponse; + // The code locations of the breakpoints. + optional> breakpoints; + // Deprecated: The code locations of the breakpoints. + optional> lines; + // The source location of the breakpoints; either `source.path` or + // `source.sourceReference` must be specified. + Source source; + // A value of true indicates that the underlying source has been modified + // which results in new breakpoint locations. + optional sourceModified; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetBreakpointsRequest); + +// Response to `setDataBreakpoints` request. +// Returned is information about each breakpoint created by this request. +struct SetDataBreakpointsResponse : public Response { + // Information about the data breakpoints. The array elements correspond to + // the elements of the input argument `breakpoints` array. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetDataBreakpointsResponse); + +// Properties of a data breakpoint passed to the `setDataBreakpoints` request. +struct DataBreakpoint { + // The access type of the data. + optional accessType; + // An expression for conditional breakpoints. + optional condition; + // An id representing the data. This id is returned from the + // `dataBreakpointInfo` request. + string dataId; + // An expression that controls how many hits of the breakpoint are ignored. + // The debug adapter is expected to interpret the expression as needed. + optional hitCondition; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpoint); + +// Replaces all existing data breakpoints with new data breakpoints. +// To clear all data breakpoints, specify an empty array. +// When a data breakpoint is hit, a `stopped` event (with reason `data +// breakpoint`) is generated. Clients should only call this request if the +// corresponding capability `supportsDataBreakpoints` is true. +struct SetDataBreakpointsRequest : public Request { + using Response = SetDataBreakpointsResponse; + // The contents of this array replaces all existing data breakpoints. An empty + // array clears all data breakpoints. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetDataBreakpointsRequest); + +// Response to `setExceptionBreakpoints` request. +// The response contains an array of `Breakpoint` objects with information about +// each exception breakpoint or filter. The `Breakpoint` objects are in the same +// order as the elements of the `filters`, `filterOptions`, `exceptionOptions` +// arrays given as arguments. If both `filters` and `filterOptions` are given, +// the returned array must start with `filters` information first, followed by +// `filterOptions` information. The `verified` property of a `Breakpoint` object +// signals whether the exception breakpoint or filter could be successfully +// created and whether the condition is valid. In case of an error the `message` +// property explains the problem. The `id` property can be used to introduce a +// unique ID for the exception breakpoint or filter so that it can be updated +// subsequently by sending breakpoint events. For backward compatibility both +// the `breakpoints` array and the enclosing `body` are optional. If these +// elements are missing a client is not able to show problems for individual +// exception breakpoints or filters. +struct SetExceptionBreakpointsResponse : public Response { + // Information about the exception breakpoints or filters. + // The breakpoints returned are in the same order as the elements of the + // `filters`, `filterOptions`, `exceptionOptions` arrays in the arguments. If + // both `filters` and `filterOptions` are given, the returned array must start + // with `filters` information first, followed by `filterOptions` information. + optional> breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse); + +// An `ExceptionPathSegment` represents a segment in a path that is used to +// match leafs or nodes in a tree of exceptions. If a segment consists of more +// than one name, it matches the names provided if `negate` is false or missing, +// or it matches anything except the names provided if `negate` is true. +struct ExceptionPathSegment { + // Depending on the value of `negate` the names that should match or not + // match. + array names; + // If false or missing this segment matches the names provided, otherwise it + // matches anything except the names provided. + optional negate; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionPathSegment); + +// An `ExceptionOptions` assigns configuration options to a set of exceptions. +struct ExceptionOptions { + // Condition when a thrown exception should result in a break. + ExceptionBreakMode breakMode = "never"; + // A path that selects a single or multiple exceptions in a tree. If `path` is + // missing, the whole tree is selected. By convention the first segment of the + // path is a category that is used to group exceptions in the UI. + optional> path; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionOptions); + +// An `ExceptionFilterOptions` is used to specify an exception filter together +// with a condition for the `setExceptionBreakpoints` request. +struct ExceptionFilterOptions { + // An expression for conditional exceptions. + // The exception breaks into the debugger if the result of the condition is + // true. + optional condition; + // ID of an exception filter returned by the `exceptionBreakpointFilters` + // capability. + string filterId; + // The mode of this exception breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ExceptionFilterOptions); + +// The request configures the debugger's response to thrown exceptions. Each of +// the `filters`, `filterOptions`, and `exceptionOptions` in the request are +// independent configurations to a debug adapter indicating a kind of exception +// to catch. An exception thrown in a program should result in a `stopped` event +// from the debug adapter (with reason `exception`) if any of the configured +// filters match. Clients should only call this request if the corresponding +// capability `exceptionBreakpointFilters` returns one or more filters. +struct SetExceptionBreakpointsRequest : public Request { + using Response = SetExceptionBreakpointsResponse; + // Configuration options for selected exceptions. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsExceptionOptions` is true. + optional> exceptionOptions; + // Set of exception filters and their options. The set of all possible + // exception filters is defined by the `exceptionBreakpointFilters` + // capability. This attribute is only honored by a debug adapter if the + // corresponding capability `supportsExceptionFilterOptions` is true. The + // `filter` and `filterOptions` sets are additive. + optional> filterOptions; + // Set of exception filters specified by their ID. The set of all possible + // exception filters is defined by the `exceptionBreakpointFilters` + // capability. The `filter` and `filterOptions` sets are additive. + array filters; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest); + +// Response to `setExpression` request. +struct SetExpressionResponse : public Response { + // The number of indexed child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional indexedVariables; + // A memory reference to a location appropriate for this result. + // For pointer type eval results, this is generally a reference to the memory + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. + optional memoryReference; + // The number of named child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional namedVariables; + // Properties of a value that can be used to determine how to render the + // result in the UI. + optional presentationHint; + // The type of the value. + // This attribute should only be returned by a debug adapter if the + // corresponding capability `supportsVariableType` is true. + optional type; + // The new value of the expression. + string value; + // A reference that allows the client to request the location where the new + // value is declared. For example, if the new value is function pointer, the + // adapter may be able to look up the function's location. This should be + // present only if the adapter is likely to be able to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional valueLocationReference; + // If `variablesReference` is > 0, the evaluate result is structured and its + // children can be retrieved by passing `variablesReference` to the + // `variables` request as long as execution remains suspended. See 'Lifetime + // of Object References' in the Overview section for details. + optional variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetExpressionResponse); + +// Evaluates the given `value` expression and assigns it to the `expression` +// which must be a modifiable l-value. The expressions have access to any +// variables and arguments that are in scope of the specified frame. Clients +// should only call this request if the corresponding capability +// `supportsSetExpression` is true. If a debug adapter implements both +// `setExpression` and `setVariable`, a client uses `setExpression` if the +// variable has an `evaluateName` property. +struct SetExpressionRequest : public Request { + using Response = SetExpressionResponse; + // The l-value expression to assign to. + string expression; + // Specifies how the resulting value should be formatted. + optional format; + // Evaluate the expressions in the scope of this stack frame. If not + // specified, the expressions are evaluated in the global scope. + optional frameId; + // The value expression to assign to the l-value expression. + string value; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetExpressionRequest); + +// Response to `setFunctionBreakpoints` request. +// Returned is information about each breakpoint created by this request. +struct SetFunctionBreakpointsResponse : public Response { + // Information about the breakpoints. The array elements correspond to the + // elements of the `breakpoints` array. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse); + +// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. +struct FunctionBreakpoint { + // An expression for conditional breakpoints. + // It is only honored by a debug adapter if the corresponding capability + // `supportsConditionalBreakpoints` is true. + optional condition; + // An expression that controls how many hits of the breakpoint are ignored. + // The debug adapter is expected to interpret the expression as needed. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsHitConditionalBreakpoints` is true. + optional hitCondition; + // The name of the function. + string name; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(FunctionBreakpoint); + +// Replaces all existing function breakpoints with new function breakpoints. +// To clear all function breakpoints, specify an empty array. +// When a function breakpoint is hit, a `stopped` event (with reason `function +// breakpoint`) is generated. Clients should only call this request if the +// corresponding capability `supportsFunctionBreakpoints` is true. +struct SetFunctionBreakpointsRequest : public Request { + using Response = SetFunctionBreakpointsResponse; + // The function names of the breakpoints. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest); + +// Response to `setInstructionBreakpoints` request +struct SetInstructionBreakpointsResponse : public Response { + // Information about the breakpoints. The array elements correspond to the + // elements of the `breakpoints` array. + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse); + +// Properties of a breakpoint passed to the `setInstructionBreakpoints` request +struct InstructionBreakpoint { + // An expression for conditional breakpoints. + // It is only honored by a debug adapter if the corresponding capability + // `supportsConditionalBreakpoints` is true. + optional condition; + // An expression that controls how many hits of the breakpoint are ignored. + // The debug adapter is expected to interpret the expression as needed. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsHitConditionalBreakpoints` is true. + optional hitCondition; + // The instruction reference of the breakpoint. + // This should be a memory or instruction pointer reference from an + // `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or + // `Breakpoint`. + string instructionReference; + // The mode of this breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; + // The offset from the instruction reference in bytes. + // This can be negative. + optional offset; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(InstructionBreakpoint); + +// Replaces all existing instruction breakpoints. Typically, instruction +// breakpoints would be set from a disassembly window. To clear all instruction +// breakpoints, specify an empty array. When an instruction breakpoint is hit, a +// `stopped` event (with reason `instruction breakpoint`) is generated. Clients +// should only call this request if the corresponding capability +// `supportsInstructionBreakpoints` is true. +struct SetInstructionBreakpointsRequest : public Request { + using Response = SetInstructionBreakpointsResponse; + // The instruction references of the breakpoints + array breakpoints; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest); + +// Response to `setVariable` request. +struct SetVariableResponse : public Response { + // The number of indexed child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional indexedVariables; + // A memory reference to a location appropriate for this result. + // For pointer type eval results, this is generally a reference to the memory + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. + optional memoryReference; + // The number of named child variables. + // The client can use this information to present the variables in a paged UI + // and fetch them in chunks. The value should be less than or equal to + // 2147483647 (2^31-1). + optional namedVariables; + // The type of the new value. Typically shown in the UI when hovering over the + // value. + optional type; + // The new value of the variable. + string value; + // A reference that allows the client to request the location where the new + // value is declared. For example, if the new value is function pointer, the + // adapter may be able to look up the function's location. This should be + // present only if the adapter is likely to be able to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional valueLocationReference; + // If `variablesReference` is > 0, the new value is structured and its + // children can be retrieved by passing `variablesReference` to the + // `variables` request as long as execution remains suspended. See 'Lifetime + // of Object References' in the Overview section for details. + // + // If this property is included in the response, any `variablesReference` + // previously associated with the updated variable, and those of its children, + // are no longer valid. + optional variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetVariableResponse); + +// Set the variable with the given name in the variable container to a new +// value. Clients should only call this request if the corresponding capability +// `supportsSetVariable` is true. If a debug adapter implements both +// `setVariable` and `setExpression`, a client will only use `setExpression` if +// the variable has an `evaluateName` property. +struct SetVariableRequest : public Request { + using Response = SetVariableResponse; + // Specifies details on how to format the response value. + optional format; + // The name of the variable in the container. + string name; + // The value of the variable. + string value; + // The reference of the variable container. The `variablesReference` must have + // been obtained in the current suspended state. See 'Lifetime of Object + // References' in the Overview section for details. + integer variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SetVariableRequest); + +// Response to `source` request. +struct SourceResponse : public Response { + // Content of the source reference. + string content; + // Content type (MIME type) of the source. + optional mimeType; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SourceResponse); + +// The request retrieves the source code for a given source reference. +struct SourceRequest : public Request { + using Response = SourceResponse; + // Specifies the source content to load. Either `source.path` or + // `source.sourceReference` must be specified. + optional source; + // The reference to the source. This is the same as `source.sourceReference`. + // This is provided for backward compatibility since old clients do not + // understand the `source` attribute. + integer sourceReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(SourceRequest); + +// A Stackframe contains the source location. +struct StackFrame { + // Indicates whether this frame can be restarted with the `restartFrame` + // request. Clients should only use this if the debug adapter supports the + // `restart` request and the corresponding capability `supportsRestartFrame` + // is true. If a debug adapter has this capability, then `canRestart` defaults + // to `true` if the property is absent. + optional canRestart; + // Start position of the range covered by the stack frame. It is measured in + // UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. If attribute `source` is missing or doesn't + // exist, `column` is 0 and should be ignored by the client. + integer column; + // End position of the range covered by the stack frame. It is measured in + // UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. + optional endColumn; + // The end line of the range covered by the stack frame. + optional endLine; + // An identifier for the stack frame. It must be unique across all threads. + // This id can be used to retrieve the scopes of the frame with the `scopes` + // request or to restart the execution of a stack frame. + integer id; + // A memory reference for the current instruction pointer in this frame. + optional instructionPointerReference; + // The line within the source of the frame. If the source attribute is missing + // or doesn't exist, `line` is 0 and should be ignored by the client. + integer line; + // The module associated with this frame, if any. + optional> moduleId; + // The name of the stack frame, typically a method name. + string name; + // A hint for how to present this frame in the UI. + // A value of `label` can be used to indicate that the frame is an artificial + // frame that is used as a visual label or separator. A value of `subtle` can + // be used to change the appearance of a frame in a 'subtle' way. + // + // Must be one of the following enumeration values: + // 'normal', 'label', 'subtle' + optional presentationHint; + // The source of the frame. + optional source; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StackFrame); + +// Response to `stackTrace` request. +struct StackTraceResponse : public Response { + // The frames of the stack frame. If the array has length zero, there are no + // stack frames available. This means that there is no location information + // available. + array stackFrames; + // The total number of frames available in the stack. If omitted or if + // `totalFrames` is larger than the available frames, a client is expected to + // request frames until a request returns less frames than requested (which + // indicates the end of the stack). Returning monotonically increasing + // `totalFrames` values for subsequent requests can be used to enforce paging + // in the client. + optional totalFrames; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StackTraceResponse); + +// Provides formatting information for a stack frame. +struct StackFrameFormat : public ValueFormat { + // Includes all stack frames, including those the debug adapter might + // otherwise hide. + optional includeAll; + // Displays the line number of the stack frame. + optional line; + // Displays the module of the stack frame. + optional module; + // Displays the names of parameters for the stack frame. + optional parameterNames; + // Displays the types of parameters for the stack frame. + optional parameterTypes; + // Displays the values of parameters for the stack frame. + optional parameterValues; + // Displays parameters for the stack frame. + optional parameters; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StackFrameFormat); + +// The request returns a stacktrace from the current execution state of a given +// thread. A client can request all stack frames by omitting the startFrame and +// levels arguments. For performance-conscious clients and if the corresponding +// capability `supportsDelayedStackTraceLoading` is true, stack frames can be +// retrieved in a piecemeal way with the `startFrame` and `levels` arguments. +// The response of the `stackTrace` request may contain a `totalFrames` property +// that hints at the total number of frames in the stack. If a client needs this +// total number upfront, it can issue a request for a single (first) frame and +// depending on the value of `totalFrames` decide how to proceed. In any case a +// client should be prepared to receive fewer frames than requested, which is an +// indication that the end of the stack has been reached. +struct StackTraceRequest : public Request { + using Response = StackTraceResponse; + // Specifies details on how to format the stack frames. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsValueFormattingOptions` is true. + optional format; + // The maximum number of frames to return. If levels is not specified or 0, + // all frames are returned. + optional levels; + // The index of the first frame to return; if omitted frames start at 0. + optional startFrame; + // Retrieve the stacktrace for this thread. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StackTraceRequest); + +// Response to `startDebugging` request. This is just an acknowledgement, so no +// body field is required. +struct StartDebuggingResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(StartDebuggingResponse); + +// This request is sent from the debug adapter to the client to start a new +// debug session of the same type as the caller. This request should only be +// sent if the corresponding client capability `supportsStartDebuggingRequest` +// is true. A client implementation of `startDebugging` should start a new debug +// session (of the same type as the caller) in the same way that the caller's +// session was started. If the client supports hierarchical debug sessions, the +// newly created session can be treated as a child of the caller session. +struct StartDebuggingRequest : public Request { + using Response = StartDebuggingResponse; + // Arguments passed to the new debug session. The arguments must only contain + // properties understood by the `launch` or `attach` requests of the debug + // adapter and they must not contain any client-specific properties (e.g. + // `type`) or client-specific features (e.g. substitutable 'variables'). + object configuration; + // Indicates whether the new debug session should be started with a `launch` + // or `attach` request. + // + // Must be one of the following enumeration values: + // 'launch', 'attach' + string request = "launch"; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StartDebuggingRequest); + +// Response to `stepBack` request. This is just an acknowledgement, so no body +// field is required. +struct StepBackResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepBackResponse); + +// The request executes one backward step (in the given granularity) for the +// specified thread and allows all other threads to run backward freely by +// resuming them. If the debug adapter supports single thread execution (see +// capability `supportsSingleThreadExecutionRequests`), setting the +// `singleThread` argument to true prevents other suspended threads from +// resuming. The debug adapter first sends the response and then a `stopped` +// event (with reason `step`) after the step has completed. Clients should only +// call this request if the corresponding capability `supportsStepBack` is true. +struct StepBackRequest : public Request { + using Response = StepBackResponse; + // Stepping granularity to step. If no granularity is specified, a granularity + // of `statement` is assumed. + optional granularity; + // If this flag is true, all other suspended threads are not resumed. + optional singleThread; + // Specifies the thread for which to resume execution for one step backwards + // (of the given granularity). + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepBackRequest); + +// Response to `stepIn` request. This is just an acknowledgement, so no body +// field is required. +struct StepInResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepInResponse); + +// The request resumes the given thread to step into a function/method and +// allows all other threads to run freely by resuming them. If the debug adapter +// supports single thread execution (see capability +// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument +// to true prevents other suspended threads from resuming. If the request cannot +// step into a target, `stepIn` behaves like the `next` request. The debug +// adapter first sends the response and then a `stopped` event (with reason +// `step`) after the step has completed. If there are multiple function/method +// calls (or other targets) on the source line, the argument `targetId` can be +// used to control into which target the `stepIn` should occur. The list of +// possible targets for a given source line can be retrieved via the +// `stepInTargets` request. +struct StepInRequest : public Request { + using Response = StepInResponse; + // Stepping granularity. If no granularity is specified, a granularity of + // `statement` is assumed. + optional granularity; + // If this flag is true, all other suspended threads are not resumed. + optional singleThread; + // Id of the target to step into. + optional targetId; + // Specifies the thread for which to resume execution for one step-into (of + // the given granularity). + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepInRequest); + +// A `StepInTarget` can be used in the `stepIn` request and determines into +// which single target the `stepIn` request should step. +struct StepInTarget { + // Start position of the range covered by the step in target. It is measured + // in UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. + optional column; + // End position of the range covered by the step in target. It is measured in + // UTF-16 code units and the client capability `columnsStartAt1` determines + // whether it is 0- or 1-based. + optional endColumn; + // The end line of the range covered by the step-in target. + optional endLine; + // Unique identifier for a step-in target. + integer id; + // The name of the step-in target (shown in the UI). + string label; + // The line of the step-in target. + optional line; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepInTarget); + +// Response to `stepInTargets` request. +struct StepInTargetsResponse : public Response { + // The possible step-in targets of the specified source location. + array targets; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepInTargetsResponse); + +// This request retrieves the possible step-in targets for the specified stack +// frame. These targets can be used in the `stepIn` request. Clients should only +// call this request if the corresponding capability +// `supportsStepInTargetsRequest` is true. +struct StepInTargetsRequest : public Request { + using Response = StepInTargetsResponse; + // The stack frame for which to retrieve the possible step-in targets. + integer frameId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepInTargetsRequest); + +// Response to `stepOut` request. This is just an acknowledgement, so no body +// field is required. +struct StepOutResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepOutResponse); + +// The request resumes the given thread to step out (return) from a +// function/method and allows all other threads to run freely by resuming them. +// If the debug adapter supports single thread execution (see capability +// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument +// to true prevents other suspended threads from resuming. The debug adapter +// first sends the response and then a `stopped` event (with reason `step`) +// after the step has completed. +struct StepOutRequest : public Request { + using Response = StepOutResponse; + // Stepping granularity. If no granularity is specified, a granularity of + // `statement` is assumed. + optional granularity; + // If this flag is true, all other suspended threads are not resumed. + optional singleThread; + // Specifies the thread for which to resume execution for one step-out (of the + // given granularity). + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StepOutRequest); + +// The event indicates that the execution of the debuggee has stopped due to +// some condition. This can be caused by a breakpoint previously set, a stepping +// request has completed, by executing a debugger statement etc. +struct StoppedEvent : public Event { + // If `allThreadsStopped` is true, a debug adapter can announce that all + // threads have stopped. + // - The client should use this information to enable that all threads can be + // expanded to access their stacktraces. + // - If the attribute is missing or false, only the thread with the given + // `threadId` can be expanded. + optional allThreadsStopped; + // The full reason for the event, e.g. 'Paused on exception'. This string is + // shown in the UI as is and can be translated. + optional description; + // Ids of the breakpoints that triggered the event. In most cases there is + // only a single breakpoint but here are some examples for multiple + // breakpoints: + // - Different types of breakpoints map to the same location. + // - Multiple source breakpoints get collapsed to the same instruction by the + // compiler/runtime. + // - Multiple function breakpoints with different function names map to the + // same location. + optional> hitBreakpointIds; + // A value of true hints to the client that this event should not change the + // focus. + optional preserveFocusHint; + // The reason for the event. + // For backward compatibility this string is shown in the UI if the + // `description` attribute is missing (but it must not be translated). + // + // May be one of the following enumeration values: + // 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function + // breakpoint', 'data breakpoint', 'instruction breakpoint' + string reason; + // Additional information. E.g. if reason is `exception`, text contains the + // exception name. This string is shown in the UI. + optional text; + // The thread which was stopped. + optional threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(StoppedEvent); + +// Response to `terminate` request. This is just an acknowledgement, so no body +// field is required. +struct TerminateResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(TerminateResponse); + +// The `terminate` request is sent from the client to the debug adapter in order +// to shut down the debuggee gracefully. Clients should only call this request +// if the capability `supportsTerminateRequest` is true. Typically a debug +// adapter implements `terminate` by sending a software signal which the +// debuggee intercepts in order to clean things up properly before terminating +// itself. Please note that this request does not directly affect the state of +// the debug session: if the debuggee decides to veto the graceful shutdown for +// any reason by not terminating itself, then the debug session just continues. +// Clients can surface the `terminate` request as an explicit command or they +// can integrate it into a two stage Stop command that first sends `terminate` +// to request a graceful shutdown, and if that fails uses `disconnect` for a +// forceful shutdown. +struct TerminateRequest : public Request { + using Response = TerminateResponse; + // A value of true indicates that this `terminate` request is part of a + // restart sequence. + optional restart; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(TerminateRequest); + +// Response to `terminateThreads` request. This is just an acknowledgement, no +// body field is required. +struct TerminateThreadsResponse : public Response {}; + +DAP_DECLARE_STRUCT_TYPEINFO(TerminateThreadsResponse); + +// The request terminates the threads with the given ids. +// Clients should only call this request if the corresponding capability +// `supportsTerminateThreadsRequest` is true. +struct TerminateThreadsRequest : public Request { + using Response = TerminateThreadsResponse; + // Ids of threads to be terminated. + optional> threadIds; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(TerminateThreadsRequest); + +// The event indicates that debugging of the debuggee has terminated. This does +// **not** mean that the debuggee itself has exited. +struct TerminatedEvent : public Event { + // A debug adapter may set `restart` to true (or to an arbitrary object) to + // request that the client restarts the session. The value is not interpreted + // by the client and passed unmodified as an attribute `__restart` to the + // `launch` and `attach` requests. + optional, boolean, integer, null, number, object, string>> + restart; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(TerminatedEvent); + +// The event indicates that a thread has started or exited. +struct ThreadEvent : public Event { + // The reason for the event. + // + // May be one of the following enumeration values: + // 'started', 'exited' + string reason; + // The identifier of the thread. + integer threadId; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ThreadEvent); + +// A Thread +struct Thread { + // Unique identifier for the thread. + integer id; + // The name of the thread. + string name; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Thread); + +// Response to `threads` request. +struct ThreadsResponse : public Response { + // All threads. + array threads; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ThreadsResponse); + +// The request retrieves a list of all threads. +struct ThreadsRequest : public Request { + using Response = ThreadsResponse; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(ThreadsRequest); + +// A Variable is a name/value pair. +// The `type` attribute is shown if space permits or when hovering over the +// variable's name. The `kind` attribute is used to render additional properties +// of the variable, e.g. different icons can be used to indicate that a variable +// is public or private. If the value is structured (has children), a handle is +// provided to retrieve the children with the `variables` request. If the number +// of named or indexed children is large, the numbers should be returned via the +// `namedVariables` and `indexedVariables` attributes. The client can use this +// information to present the children in a paged UI and fetch them in chunks. +struct Variable { + // A reference that allows the client to request the location where the + // variable is declared. This should be present only if the adapter is likely + // to be able to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional declarationLocationReference; + // The evaluatable name of this variable which can be passed to the `evaluate` + // request to fetch the variable's value. + optional evaluateName; + // The number of indexed child variables. + // The client can use this information to present the children in a paged UI + // and fetch them in chunks. + optional indexedVariables; + // A memory reference associated with this variable. + // For pointer type variables, this is generally a reference to the memory + // address contained in the pointer. For executable data, this reference may + // later be used in a `disassemble` request. This attribute may be returned by + // a debug adapter if corresponding capability `supportsMemoryReferences` is + // true. + optional memoryReference; + // The variable's name. + string name; + // The number of named child variables. + // The client can use this information to present the children in a paged UI + // and fetch them in chunks. + optional namedVariables; + // Properties of a variable that can be used to determine how to render the + // variable in the UI. + optional presentationHint; + // The type of the variable's value. Typically shown in the UI when hovering + // over the value. This attribute should only be returned by a debug adapter + // if the corresponding capability `supportsVariableType` is true. + optional type; + // The variable's value. + // This can be a multi-line text, e.g. for a function the body of a function. + // For structured variables (which do not have a simple value), it is + // recommended to provide a one-line representation of the structured object. + // This helps to identify the structured object in the collapsed state when + // its children are not yet visible. An empty string can be used if no value + // should be shown in the UI. + string value; + // A reference that allows the client to request the location where the + // variable's value is declared. For example, if the variable contains a + // function pointer, the adapter may be able to look up the function's + // location. This should be present only if the adapter is likely to be able + // to resolve the location. + // + // This reference shares the same lifetime as the `variablesReference`. See + // 'Lifetime of Object References' in the Overview section for details. + optional valueLocationReference; + // If `variablesReference` is > 0, the variable is structured and its children + // can be retrieved by passing `variablesReference` to the `variables` request + // as long as execution remains suspended. See 'Lifetime of Object References' + // in the Overview section for details. + integer variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(Variable); + +// Response to `variables` request. +struct VariablesResponse : public Response { + // All (or a range) of variables for the given variable reference. + array variables; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(VariablesResponse); + +// Retrieves all child variables for the given variable reference. +// A filter can be used to limit the fetched children to either named or indexed +// children. +struct VariablesRequest : public Request { + using Response = VariablesResponse; + // The number of variables to return. If count is missing or 0, all variables + // are returned. The attribute is only honored by a debug adapter if the + // corresponding capability `supportsVariablePaging` is true. + optional count; + // Filter to limit the child variables to either named or indexed. If omitted, + // both types are fetched. + // + // Must be one of the following enumeration values: + // 'indexed', 'named' + optional filter; + // Specifies details on how to format the Variable values. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsValueFormattingOptions` is true. + optional format; + // The index of the first variable to return; if omitted children start at 0. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsVariablePaging` is true. + optional start; + // The variable for which to retrieve its children. The `variablesReference` + // must have been obtained in the current suspended state. See 'Lifetime of + // Object References' in the Overview section for details. + integer variablesReference; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(VariablesRequest); + +// Response to `writeMemory` request. +struct WriteMemoryResponse : public Response { + // Property that should be returned when `allowPartial` is true to indicate + // the number of bytes starting from address that were successfully written. + optional bytesWritten; + // Property that should be returned when `allowPartial` is true to indicate + // the offset of the first byte of data successfully written. Can be negative. + optional offset; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(WriteMemoryResponse); + +// Writes bytes to memory at the provided location. +// Clients should only call this request if the corresponding capability +// `supportsWriteMemoryRequest` is true. +struct WriteMemoryRequest : public Request { + using Response = WriteMemoryResponse; + // Property to control partial writes. If true, the debug adapter should + // attempt to write memory even if the entire memory region is not writable. + // In such a case the debug adapter should stop after hitting the first byte + // of memory that cannot be written and return the number of bytes written in + // the response via the `offset` and `bytesWritten` properties. If false or + // missing, a debug adapter should attempt to verify the region is writable + // before writing, and fail the response if it is not. + optional allowPartial; + // Bytes to write, encoded using base64. + string data; + // Memory reference to the base location to which data should be written. + string memoryReference; + // Offset (in bytes) to be applied to the reference location before writing + // data. Can be negative. + optional offset; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(WriteMemoryRequest); + +} // namespace dap + +#endif // dap_protocol_h diff --git a/libraries/cppdap/include/dap/serialization.h b/libraries/cppdap/include/dap/serialization.h new file mode 100644 index 000000000..c7d4c5e07 --- /dev/null +++ b/libraries/cppdap/include/dap/serialization.h @@ -0,0 +1,253 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_serialization_h +#define dap_serialization_h + +#include "typeof.h" +#include "types.h" + +#include // ptrdiff_t +#include + +namespace dap { + +// Field describes a single field of a struct. +struct Field { + std::string name; // name of the field + ptrdiff_t offset; // offset of the field to the base of the struct + const TypeInfo* type; // type of the field +}; + +//////////////////////////////////////////////////////////////////////////////// +// Deserializer +//////////////////////////////////////////////////////////////////////////////// + +// Deserializer is the interface used to decode data from structured storage. +// Methods that return a bool use this to indicate success. +class Deserializer { + public: + virtual ~Deserializer() = default; + + // deserialization methods for simple data types. + // If the stored object is not of the correct type, then these function will + // return false. + virtual bool deserialize(boolean*) const = 0; + virtual bool deserialize(integer*) const = 0; + virtual bool deserialize(number*) const = 0; + virtual bool deserialize(string*) const = 0; + virtual bool deserialize(object*) const = 0; + virtual bool deserialize(any*) const = 0; + + // count() returns the number of elements in the array object referenced by + // this Deserializer. + virtual size_t count() const = 0; + + // array() calls the provided std::function for deserializing each array + // element in the array object referenced by this Deserializer. + virtual bool array(const std::function&) const = 0; + + // field() calls the provided std::function for deserializing the field with + // the given name from the struct object referenced by this Deserializer. + virtual bool field(const std::string& name, + const std::function&) const = 0; + + // deserialize() delegates to TypeOf::type()->deserialize(). + template ::has_custom_serialization>> + inline bool deserialize(T*) const; + + // deserialize() decodes an array. + template + inline bool deserialize(dap::array*) const; + + // deserialize() decodes an optional. + template + inline bool deserialize(dap::optional*) const; + + // deserialize() decodes an variant. + template + inline bool deserialize(dap::variant*) const; + + // deserialize() decodes the struct field f with the given name. + template + inline bool field(const std::string& name, T* f) const; +}; + +template +bool Deserializer::deserialize(T* ptr) const { + return TypeOf::type()->deserialize(this, ptr); +} + +template +bool Deserializer::deserialize(dap::array* vec) const { + auto n = count(); + vec->resize(n); + size_t i = 0; + if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) { + return false; + } + return true; +} + +template +bool Deserializer::deserialize(dap::optional* opt) const { + T v; + if (deserialize(&v)) { + *opt = v; + } + return true; +} + +template +bool Deserializer::deserialize(dap::variant* var) const { + return deserialize(&var->value); +} + +template +bool Deserializer::field(const std::string& name, T* v) const { + return this->field(name, + [&](const Deserializer* d) { return d->deserialize(v); }); +} + +//////////////////////////////////////////////////////////////////////////////// +// Serializer +//////////////////////////////////////////////////////////////////////////////// +class FieldSerializer; + +// Serializer is the interface used to encode data to structured storage. +// A Serializer is associated with a single storage object, whos type and value +// is assigned by a call to serialize(). +// If serialize() is called multiple times on the same Serializer instance, +// the last type and value is stored. +// Methods that return a bool use this to indicate success. +class Serializer { + public: + virtual ~Serializer() = default; + + // serialization methods for simple data types. + virtual bool serialize(boolean) = 0; + virtual bool serialize(integer) = 0; + virtual bool serialize(number) = 0; + virtual bool serialize(const string&) = 0; + virtual bool serialize(const dap::object&) = 0; + virtual bool serialize(const any&) = 0; + + // array() encodes count array elements to the array object referenced by this + // Serializer. The std::function will be called count times, each time with a + // Serializer that should be used to encode the n'th array element's data. + virtual bool array(size_t count, const std::function&) = 0; + + // object() begins encoding the object referenced by this Serializer. + // The std::function will be called with a FieldSerializer to serialize the + // object's fields. + virtual bool object(const std::function&) = 0; + + // remove() deletes the object referenced by this Serializer. + // remove() can be used to serialize optionals with no value assigned. + virtual void remove() = 0; + + // serialize() delegates to TypeOf::type()->serialize(). + template ::has_custom_serialization>> + inline bool serialize(const T&); + + // serialize() encodes the given array. + template + inline bool serialize(const dap::array&); + + // serialize() encodes the given optional. + template + inline bool serialize(const dap::optional& v); + + // serialize() encodes the given variant. + template + inline bool serialize(const dap::variant&); + + // deserialize() encodes the given string. + inline bool serialize(const char* v); + protected: + static inline const TypeInfo* get_any_type(const any&); + static inline const void* get_any_val(const any&); +}; + +inline const TypeInfo* Serializer::get_any_type(const any& a){ + return a.type; +} +const void* Serializer::get_any_val(const any& a) { + return a.value; +} + +template +bool Serializer::serialize(const T& object) { + return TypeOf::type()->serialize(this, &object); +} + +template +bool Serializer::serialize(const dap::array& vec) { + auto it = vec.begin(); + return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); }); +} + +template +bool Serializer::serialize(const dap::optional& opt) { + if (!opt.has_value()) { + remove(); + return true; + } + return serialize(opt.value()); +} + +template +bool Serializer::serialize(const dap::variant& var) { + return serialize(var.value); +} + +bool Serializer::serialize(const char* v) { + return serialize(std::string(v)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FieldSerializer +//////////////////////////////////////////////////////////////////////////////// + +// FieldSerializer is the interface used to serialize fields of an object. +class FieldSerializer { + public: + using SerializeFunc = std::function; + template + using IsSerializeFunc = std::is_convertible; + + virtual ~FieldSerializer() = default; + + // field() encodes a field to the struct object referenced by this Serializer. + // The SerializeFunc will be called with a Serializer used to encode the + // field's data. + virtual bool field(const std::string& name, const SerializeFunc&) = 0; + + // field() encodes the field with the given name and value. + template < + typename T, + typename = typename std::enable_if::value>::type> + inline bool field(const std::string& name, const T& v); +}; + +template +bool FieldSerializer::field(const std::string& name, const T& v) { + return this->field(name, [&](Serializer* s) { return s->serialize(v); }); +} + +} // namespace dap + +#endif // dap_serialization_h diff --git a/libraries/cppdap/include/dap/session.h b/libraries/cppdap/include/dap/session.h new file mode 100644 index 000000000..96db04b7d --- /dev/null +++ b/libraries/cppdap/include/dap/session.h @@ -0,0 +1,460 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_session_h +#define dap_session_h + +#include "future.h" +#include "io.h" +#include "traits.h" +#include "typeinfo.h" +#include "typeof.h" + +#include + +namespace dap { + +// Forward declarations +struct Request; +struct Response; +struct Event; + +//////////////////////////////////////////////////////////////////////////////// +// Error +//////////////////////////////////////////////////////////////////////////////// + +// Error represents an error message in response to a DAP request. +struct Error { + Error() = default; + Error(const std::string& error); + Error(const char* msg, ...); + + // operator bool() returns true if there is an error. + inline operator bool() const { return message.size() > 0; } + + std::string message; // empty represents success. +}; + +//////////////////////////////////////////////////////////////////////////////// +// ResponseOrError +//////////////////////////////////////////////////////////////////////////////// + +// ResponseOrError holds either the response to a DAP request or an error +// message. +template +struct ResponseOrError { + using Request = T; + + inline ResponseOrError() = default; + inline ResponseOrError(const T& response); + inline ResponseOrError(T&& response); + inline ResponseOrError(const Error& error); + inline ResponseOrError(Error&& error); + inline ResponseOrError(const ResponseOrError& other); + inline ResponseOrError(ResponseOrError&& other); + + inline ResponseOrError& operator=(const ResponseOrError& other); + inline ResponseOrError& operator=(ResponseOrError&& other); + + T response; + Error error; // empty represents success. +}; + +template +ResponseOrError::ResponseOrError(const T& resp) : response(resp) {} +template +ResponseOrError::ResponseOrError(T&& resp) : response(std::move(resp)) {} +template +ResponseOrError::ResponseOrError(const Error& err) : error(err) {} +template +ResponseOrError::ResponseOrError(Error&& err) : error(std::move(err)) {} +template +ResponseOrError::ResponseOrError(const ResponseOrError& other) + : response(other.response), error(other.error) {} +template +ResponseOrError::ResponseOrError(ResponseOrError&& other) + : response(std::move(other.response)), error(std::move(other.error)) {} +template +ResponseOrError& ResponseOrError::operator=( + const ResponseOrError& other) { + response = other.response; + error = other.error; + return *this; +} +template +ResponseOrError& ResponseOrError::operator=(ResponseOrError&& other) { + response = std::move(other.response); + error = std::move(other.error); + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Session +//////////////////////////////////////////////////////////////////////////////// + +// An enum flag that controls how the Session handles invalid data. +enum OnInvalidData { + // Ignore invalid data. + kIgnore, + // Close the underlying reader when invalid data is received. + kClose, +}; + +// Session implements a DAP client or server endpoint. +// The general usage is as follows: +// (1) Create a session with Session::create(). +// (2) Register request and event handlers with registerHandler(). +// (3) Optionally register a protocol error handler with onError(). +// (3) Bind the session to the remote endpoint with bind(). +// (4) Send requests or events with send(). +class Session { + template + using ParamType = traits::ParameterType; + + template + using IsRequest = traits::EnableIfIsType; + + template + using IsEvent = traits::EnableIfIsType; + + template + using IsRequestHandlerWithoutCallback = traits::EnableIf< + traits::CompatibleWith>::value>; + + template + using IsRequestHandlerWithCallback = traits::EnableIf)>>:: + value>; + + public: + virtual ~Session(); + + // ErrorHandler is the type of callback function used for reporting protocol + // errors. + using ErrorHandler = std::function; + + // ClosedHandler is the type of callback function used to signal that a + // connected endpoint has closed. + using ClosedHandler = std::function; + + // create() constructs and returns a new Session. + static std::unique_ptr create(); + + // Sets how the Session handles invalid data. + virtual void setOnInvalidData(OnInvalidData) = 0; + + // onError() registers a error handler that will be called whenever a protocol + // error is encountered. + // Only one error handler can be bound at any given time, and later calls + // will replace the existing error handler. + virtual void onError(const ErrorHandler&) = 0; + + // registerHandler() registers a request handler for a specific request type. + // The function F must have one of the following signatures: + // ResponseOrError(const RequestType&) + // ResponseType(const RequestType&) + // Error(const RequestType&) + template > + inline IsRequestHandlerWithoutCallback registerHandler(F&& handler); + + // registerHandler() registers a request handler for a specific request type. + // The handler has a response callback function for the second argument of the + // handler function. This callback may be called after the handler has + // returned. + // The function F must have the following signature: + // void(const RequestType& request, + // std::function response) + template , + typename ResponseType = typename RequestType::Response> + inline IsRequestHandlerWithCallback registerHandler( + F&& handler); + + // registerHandler() registers a request handler for a specific request type. + // The handler has a response callback function for the second argument of the + // handler function. This callback may be called after the handler has + // returned. + // The function F must have the following signature: + // void(const RequestType& request, + // std::function)> response) + template , + typename ResponseType = typename RequestType::Response> + inline IsRequestHandlerWithCallback> + registerHandler(F&& handler); + + // registerHandler() registers a event handler for a specific event type. + // The function F must have the following signature: + // void(const EventType&) + template > + inline IsEvent registerHandler(F&& handler); + + // registerSentHandler() registers the function F to be called when a response + // of the specific type has been sent. + // The function F must have the following signature: + // void(const ResponseOrError&) + template ::Request> + inline void registerSentHandler(F&& handler); + + // send() sends the request to the connected endpoint and returns a + // future that is assigned the request response or error. + template > + future> send(const T& request); + + // send() sends the event to the connected endpoint. + template > + void send(const T& event); + + // bind() connects this Session to an endpoint using connect(), and then + // starts processing incoming messages with startProcessingMessages(). + // onClose is the optional callback which will be called when the session + // endpoint has been closed. + inline void bind(const std::shared_ptr& reader, + const std::shared_ptr& writer, + const ClosedHandler& onClose); + inline void bind(const std::shared_ptr& readerWriter, + const ClosedHandler& onClose); + + ////////////////////////////////////////////////////////////////////////////// + // Note: + // Methods and members below this point are for advanced usage, and are more + // likely to change signature than the methods above. + // The methods above this point should be sufficient for most use cases. + ////////////////////////////////////////////////////////////////////////////// + + // connect() connects this Session to an endpoint. + // connect() can only be called once. Repeated calls will raise an error, but + // otherwise will do nothing. + // Note: This method is used for explicit control over message handling. + // Most users will use bind() instead of calling this method directly. + virtual void connect(const std::shared_ptr&, + const std::shared_ptr&) = 0; + inline void connect(const std::shared_ptr&); + + // startProcessingMessages() starts a new thread to receive and dispatch + // incoming messages. + // onClose is the optional callback which will be called when the session + // endpoint has been closed. + // Note: This method is used for explicit control over message handling. + // Most users will use bind() instead of calling this method directly. + virtual void startProcessingMessages(const ClosedHandler& onClose = {}) = 0; + + // getPayload() blocks until the next incoming message is received, returning + // the payload or an empty function if the connection was lost. The returned + // payload is function that can be called on any thread to dispatch the + // message to the Session handler. + // Note: This method is used for explicit control over message handling. + // Most users will use bind() instead of calling this method directly. + virtual std::function getPayload() = 0; + + // The callback function type called when a request handler is invoked, and + // the request returns a successful result. + // 'responseTypeInfo' is the type information of the response data structure. + // 'responseData' is a pointer to response payload data. + using RequestHandlerSuccessCallback = + std::function; + + // The callback function type used to notify when a DAP request fails. + // 'responseTypeInfo' is the type information of the response data structure. + // 'message' is the error message + using RequestHandlerErrorCallback = + std::function; + + // The callback function type used to invoke a request handler. + // 'request' is a pointer to the request data structure + // 'onSuccess' is the function to call if the request completed succesfully. + // 'onError' is the function to call if the request failed. + // For each call of the request handler, 'onSuccess' or 'onError' must be + // called exactly once. + using GenericRequestHandler = + std::function; + + // The callback function type used to handle a response to a request. + // 'response' is a pointer to the response data structure. May be nullptr. + // 'error' is a pointer to the reponse error message. May be nullptr. + // One of 'data' or 'error' will be nullptr. + using GenericResponseHandler = + std::function; + + // The callback function type used to handle an event. + // 'event' is a pointer to the event data structure. + using GenericEventHandler = std::function; + + // The callback function type used to notify when a response has been sent + // from this session endpoint. + // 'response' is a pointer to the response data structure. + // 'error' is a pointer to the reponse error message. May be nullptr. + using GenericResponseSentHandler = + std::function; + + // registerHandler() registers 'handler' as the request handler callback for + // requests of the type 'typeinfo'. + virtual void registerHandler(const TypeInfo* typeinfo, + const GenericRequestHandler& handler) = 0; + + // registerHandler() registers 'handler' as the event handler callback for + // events of the type 'typeinfo'. + virtual void registerHandler(const TypeInfo* typeinfo, + const GenericEventHandler& handler) = 0; + + // registerHandler() registers 'handler' as the response-sent handler function + // which is called whenever a response of the type 'typeinfo' is sent from + // this session endpoint. + virtual void registerHandler(const TypeInfo* typeinfo, + const GenericResponseSentHandler& handler) = 0; + + // send() sends a request to the remote endpoint. + // 'requestTypeInfo' is the type info of the request data structure. + // 'requestTypeInfo' is the type info of the response data structure. + // 'request' is a pointer to the request data structure. + // 'responseHandler' is the handler function for the response. + virtual bool send(const dap::TypeInfo* requestTypeInfo, + const dap::TypeInfo* responseTypeInfo, + const void* request, + const GenericResponseHandler& responseHandler) = 0; + + // send() sends an event to the remote endpoint. + // 'eventTypeInfo' is the type info for the event data structure. + // 'event' is a pointer to the event data structure. + virtual bool send(const TypeInfo* eventTypeInfo, const void* event) = 0; +}; + +template +Session::IsRequestHandlerWithoutCallback Session::registerHandler( + F&& handler) { + using ResponseType = typename RequestType::Response; + const TypeInfo* typeinfo = TypeOf::type(); + registerHandler(typeinfo, + [handler](const void* args, + const RequestHandlerSuccessCallback& onSuccess, + const RequestHandlerErrorCallback& onError) { + ResponseOrError res = + handler(*reinterpret_cast(args)); + if (res.error) { + onError(TypeOf::type(), res.error); + } else { + onSuccess(TypeOf::type(), &res.response); + } + }); +} + +template +Session::IsRequestHandlerWithCallback Session::registerHandler( + F&& handler) { + using CallbackType = ParamType; + registerHandler( + TypeOf::type(), + [handler](const void* args, + const RequestHandlerSuccessCallback& onSuccess, + const RequestHandlerErrorCallback&) { + CallbackType responseCallback = [onSuccess](const ResponseType& res) { + onSuccess(TypeOf::type(), &res); + }; + handler(*reinterpret_cast(args), responseCallback); + }); +} + +template +Session::IsRequestHandlerWithCallback> +Session::registerHandler(F&& handler) { + using CallbackType = ParamType; + registerHandler( + TypeOf::type(), + [handler](const void* args, + const RequestHandlerSuccessCallback& onSuccess, + const RequestHandlerErrorCallback& onError) { + CallbackType responseCallback = + [onError, onSuccess](const ResponseOrError& res) { + if (res.error) { + onError(TypeOf::type(), res.error); + } else { + onSuccess(TypeOf::type(), &res.response); + } + }; + handler(*reinterpret_cast(args), responseCallback); + }); +} + +template +Session::IsEvent Session::registerHandler(F&& handler) { + auto cb = [handler](const void* args) { + handler(*reinterpret_cast(args)); + }; + const TypeInfo* typeinfo = TypeOf::type(); + registerHandler(typeinfo, cb); +} + +template +void Session::registerSentHandler(F&& handler) { + auto cb = [handler](const void* response, const Error* error) { + if (error != nullptr) { + handler(ResponseOrError(*error)); + } else { + handler(ResponseOrError(*reinterpret_cast(response))); + } + }; + const TypeInfo* typeinfo = TypeOf::type(); + registerHandler(typeinfo, cb); +} + +template +future> Session::send(const T& request) { + using Response = typename T::Response; + promise> promise; + auto sent = send(TypeOf::type(), TypeOf::type(), &request, + [=](const void* result, const Error* error) { + if (error != nullptr) { + promise.set_value(ResponseOrError(*error)); + } else { + promise.set_value(ResponseOrError( + *reinterpret_cast(result))); + } + }); + if (!sent) { + promise.set_value(Error("Failed to send request")); + } + return promise.get_future(); +} + +template +void Session::send(const T& event) { + const TypeInfo* typeinfo = TypeOf::type(); + send(typeinfo, &event); +} + +void Session::connect(const std::shared_ptr& rw) { + connect(rw, rw); +} + +void Session::bind(const std::shared_ptr& r, + const std::shared_ptr& w, + const ClosedHandler& onClose = {}) { + connect(r, w); + startProcessingMessages(onClose); +} + +void Session::bind(const std::shared_ptr& rw, + const ClosedHandler& onClose = {}) { + bind(rw, rw, onClose); +} + +} // namespace dap + +#endif // dap_session_h diff --git a/libraries/cppdap/include/dap/traits.h b/libraries/cppdap/include/dap/traits.h new file mode 100644 index 000000000..6a0c20d07 --- /dev/null +++ b/libraries/cppdap/include/dap/traits.h @@ -0,0 +1,159 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_traits_h +#define dap_traits_h + +#include +#include + +namespace dap { +namespace traits { + +// NthTypeOf returns the `N`th type in `Types` +template +using NthTypeOf = typename std::tuple_element>::type; + +// `IsTypeOrDerived::value` is true iff `T` is of type `BASE`, or +// derives from `BASE`. +template +using IsTypeOrDerived = std::integral_constant< + bool, + std::is_base_of::type>::value || + std::is_same::type>::value>; + +// `EachIsTypeOrDerived::value` is true iff all of the types in +// the std::tuple `TYPES` is of, or derives from the corresponding indexed type +// in the std::tuple `BASES`. +// `N` must be equal to the number of types in both the std::tuple `BASES` and +// `TYPES`. +template +struct EachIsTypeOrDerived { + using base = typename std::tuple_element::type; + using type = typename std::tuple_element::type; + using last_matches = IsTypeOrDerived; + using others_match = EachIsTypeOrDerived; + static constexpr bool value = last_matches::value && others_match::value; +}; + +// EachIsTypeOrDerived specialization for N = 1 +template +struct EachIsTypeOrDerived<1, BASES, TYPES> { + using base = typename std::tuple_element<0, BASES>::type; + using type = typename std::tuple_element<0, TYPES>::type; + static constexpr bool value = IsTypeOrDerived::value; +}; + +// EachIsTypeOrDerived specialization for N = 0 +template +struct EachIsTypeOrDerived<0, BASES, TYPES> { + static constexpr bool value = true; +}; + +// Signature describes the signature of a function. +template +struct Signature { + // The return type of the function signature + using ret = RETURN; + // The parameters of the function signature held in a std::tuple + using parameters = std::tuple; + // The type of the Nth parameter of function signature + template + using parameter = NthTypeOf; + // The total number of parameters + static constexpr std::size_t parameter_count = sizeof...(PARAMETERS); +}; + +// SignatureOf is a traits helper that infers the signature of the function, +// method, static method, lambda, or function-like object `F`. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = typename SignatureOf::type; +}; + +// SignatureOf specialization for a regular function or static method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOf specialization for a non-static method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOf specialization for a non-static, const method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOfT is an alias to `typename SignatureOf::type`. +template +using SignatureOfT = typename SignatureOf::type; + +// ParameterType is an alias to `typename SignatureOf::type::parameter`. +template +using ParameterType = typename SignatureOfT::template parameter; + +// `HasSignature::value` is true iff the function-like `F` has a matching +// signature to the function-like `S`. +template +using HasSignature = std::integral_constant< + bool, + std::is_same, SignatureOfT>::value>; + +// `Min::value` resolves to the smaller value of A and B. +template +using Min = std::integral_constant; + +// `CompatibleWith::value` is true iff the function-like `F` +// can be called with the argument types of the function-like `S`. Return type +// of the two functions are not considered. +template +using CompatibleWith = std::integral_constant< + bool, + (SignatureOfT::parameter_count == SignatureOfT::parameter_count) && + EachIsTypeOrDerived::parameter_count, + SignatureOfT::parameter_count>::value, + typename SignatureOfT::parameters, + typename SignatureOfT::parameters>::value>; + +// If `CONDITION` is true then EnableIf resolves to type T, otherwise an +// invalid type. +template +using EnableIf = typename std::enable_if::type; + +// If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE_TY`, +// otherwise an invalid type. +template +using EnableIfIsType = EnableIf::value, TRUE_TY>; + +// If the function-like `F` has a matching signature to the function-like `S` +// then EnableIfHasSignature resolves to type `TRUE_TY`, otherwise an invalid type. +template +using EnableIfHasSignature = EnableIf::value, TRUE_TY>; + +} // namespace traits +} // namespace dap + +#endif // dap_traits_h diff --git a/libraries/cppdap/include/dap/typeinfo.h b/libraries/cppdap/include/dap/typeinfo.h new file mode 100644 index 000000000..d99f2777e --- /dev/null +++ b/libraries/cppdap/include/dap/typeinfo.h @@ -0,0 +1,59 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_typeinfo_h +#define dap_typeinfo_h + +#include +#include + +namespace dap { + +class any; +class Deserializer; +class Serializer; + +// The TypeInfo interface provides basic runtime type information about DAP +// types. TypeInfo is used by the serialization system to encode and decode DAP +// requests, responses, events and structs. +struct TypeInfo { + virtual ~TypeInfo(); + virtual std::string name() const = 0; + virtual size_t size() const = 0; + virtual size_t alignment() const = 0; + virtual void construct(void*) const = 0; + virtual void copyConstruct(void* dst, const void* src) const = 0; + virtual void destruct(void*) const = 0; + virtual bool deserialize(const Deserializer*, void*) const = 0; + virtual bool serialize(Serializer*, const void*) const = 0; + + // create() allocates and constructs the TypeInfo of type T, registers the + // pointer for deletion on cppdap library termination, and returns the pointer + // to T. + template + static T* create(ARGS&&... args) { + auto typeinfo = new T(std::forward(args)...); + deleteOnExit(typeinfo); + return typeinfo; + } + + private: + // deleteOnExit() ensures that the TypeInfo is destructed and deleted on + // library termination. + static void deleteOnExit(TypeInfo*); +}; + +} // namespace dap + +#endif // dap_typeinfo_h diff --git a/libraries/cppdap/include/dap/typeof.h b/libraries/cppdap/include/dap/typeof.h new file mode 100644 index 000000000..43c9289c9 --- /dev/null +++ b/libraries/cppdap/include/dap/typeof.h @@ -0,0 +1,266 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_typeof_h +#define dap_typeof_h + +#include "typeinfo.h" +#include "types.h" + +#include "serialization.h" + +namespace dap { + +// BasicTypeInfo is an implementation of the TypeInfo interface for the simple +// template type T. +template +struct BasicTypeInfo : public TypeInfo { + constexpr BasicTypeInfo(std::string&& name) : name_(std::move(name)) {} + + // TypeInfo compliance + inline std::string name() const override { return name_; } + inline size_t size() const override { return sizeof(T); } + inline size_t alignment() const override { return alignof(T); } + inline void construct(void* ptr) const override { new (ptr) T(); } + inline void copyConstruct(void* dst, const void* src) const override { + new (dst) T(*reinterpret_cast(src)); + } + inline void destruct(void* ptr) const override { + reinterpret_cast(ptr)->~T(); + } + inline bool deserialize(const Deserializer* d, void* ptr) const override { + return d->deserialize(reinterpret_cast(ptr)); + } + inline bool serialize(Serializer* s, const void* ptr) const override { + return s->serialize(*reinterpret_cast(ptr)); + } + + private: + std::string name_; +}; + +// TypeOf has a template specialization for each DAP type, each declaring a +// const TypeInfo* type() static member function that describes type T. +template +struct TypeOf {}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template <> +struct TypeOf { + static const TypeInfo* type(); +}; + +template +struct TypeOf> { + static inline const TypeInfo* type() { + static auto typeinfo = TypeInfo::create>>( + "array<" + TypeOf::type()->name() + ">"); + return typeinfo; + } +}; + +template +struct TypeOf> { + static inline const TypeInfo* type() { + static auto typeinfo = + TypeInfo::create>>("variant"); + return typeinfo; + } +}; + +template +struct TypeOf> { + static inline const TypeInfo* type() { + static auto typeinfo = TypeInfo::create>>( + "optional<" + TypeOf::type()->name() + ">"); + return typeinfo; + } +}; + +// DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in +// . It evaluates to the offset of the given field, with fewer +// restrictions than offsetof(). We cast the address '32' and subtract it again, +// because null-dereference is undefined behavior. +#define DAP_OFFSETOF(s, m) \ + ((int)(size_t) & reinterpret_cast((((s*)32)->m)) - 32) + +// internal functionality +namespace detail { +template +M member_type(M T::*); +} // namespace detail + +// DAP_TYPEOF() returns the type of the struct (s) member (m). +#define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m)) + +// DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO +// macro. +// FIELD is the name of the struct field. +// NAME is the serialized name of the field, as described by the DAP +// specification. +#define DAP_FIELD(FIELD, NAME) \ + ::dap::Field { \ + NAME, DAP_OFFSETOF(StructTy, FIELD), \ + TypeOf::type(), \ + } + +// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT. +// Must be used within the 'dap' namespace. +#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \ + template <> \ + struct TypeOf { \ + static constexpr bool has_custom_serialization = true; \ + static const TypeInfo* type(); \ + static bool deserializeFields(const Deserializer*, void* obj); \ + static bool serializeFields(FieldSerializer*, const void* obj); \ + } + +// DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() implements the deserializeFields() +// and serializeFields() static methods of a TypeOf<> specialization. Used +// internally by DAP_IMPLEMENT_STRUCT_TYPEINFO() and +// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(). +// You probably do not want to use this directly. +#define DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, ...) \ + bool TypeOf::deserializeFields(const Deserializer* fd, void* obj) { \ + using StructTy = STRUCT; \ + (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ + for (auto& field : std::initializer_list{__VA_ARGS__}) { \ + if (!fd->field(field.name, [&](Deserializer* d) { \ + auto ptr = reinterpret_cast(obj) + field.offset; \ + return field.type->deserialize(d, ptr); \ + })) { \ + return false; \ + } \ + } \ + return true; \ + } \ + bool TypeOf::serializeFields(FieldSerializer* fs, const void* obj) {\ + using StructTy = STRUCT; \ + (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ + for (auto& field : std::initializer_list{__VA_ARGS__}) { \ + if (!fs->field(field.name, [&](Serializer* s) { \ + auto ptr = reinterpret_cast(obj) + field.offset; \ + return field.type->serialize(s, ptr); \ + })) { \ + return false; \ + } \ + } \ + return true; \ + } + +// DAP_IMPLEMENT_STRUCT_TYPEINFO() implements the type() member function for the +// TypeOf<> specialization for STRUCT. +// STRUCT is the structure typename. +// NAME is the serialized name of the structure, as described by the DAP +// specification. The variadic (...) parameters should be a repeated list of +// DAP_FIELD()s, one for each field of the struct. +// Must be used within the 'dap' namespace. +#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \ + DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \ + const ::dap::TypeInfo* TypeOf::type() { \ + struct TI : BasicTypeInfo { \ + TI() : BasicTypeInfo(NAME) {} \ + bool deserialize(const Deserializer* d, void* obj) const override { \ + return deserializeFields(d, obj); \ + } \ + bool serialize(Serializer* s, const void* obj) const override { \ + return s->object( \ + [&](FieldSerializer* fs) { return serializeFields(fs, obj); }); \ + } \ + }; \ + static TI typeinfo; \ + return &typeinfo; \ + } + +// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<> +// specialization for STRUCT in a single statement. +// Must be used within the 'dap' namespace. +#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \ + DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \ + DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__) + +// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT() implements the type() member function for +// the TypeOf<> specialization for STRUCT that derives from BASE. +// STRUCT is the structure typename. +// BASE is the base structure typename. +// NAME is the serialized name of the structure, as described by the DAP +// specification. The variadic (...) parameters should be a repeated list of +// DAP_FIELD()s, one for each field of the struct. +// Must be used within the 'dap' namespace. +#define DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \ + static_assert(std::is_base_of::value, \ + #STRUCT " does not derive from " #BASE); \ + DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \ + const ::dap::TypeInfo* TypeOf::type() { \ + struct TI : BasicTypeInfo { \ + TI() : BasicTypeInfo(NAME) {} \ + bool deserialize(const Deserializer* d, void* obj) const override { \ + auto derived = static_cast(obj); \ + auto base = static_cast(obj); \ + return TypeOf::deserializeFields(d, base) && \ + deserializeFields(d, derived); \ + } \ + bool serialize(Serializer* s, const void* obj) const override { \ + return s->object([&](FieldSerializer* fs) { \ + auto derived = static_cast(obj); \ + auto base = static_cast(obj); \ + return TypeOf::serializeFields(fs, base) && \ + serializeFields(fs, derived); \ + }); \ + } \ + }; \ + static TI typeinfo; \ + return &typeinfo; \ + } + +// DAP_STRUCT_TYPEINFO_EXT() is a helper for declaring and implementing a +// TypeOf<> specialization for STRUCT that derives from BASE in a single +// statement. +// Must be used within the 'dap' namespace. +#define DAP_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \ + DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \ + DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, __VA_ARGS__) + +} // namespace dap + +#endif // dap_typeof_h diff --git a/libraries/cppdap/include/dap/types.h b/libraries/cppdap/include/dap/types.h new file mode 100644 index 000000000..7954e871c --- /dev/null +++ b/libraries/cppdap/include/dap/types.h @@ -0,0 +1,104 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file holds the basic serializable types used by the debug adapter +// protocol. + +#ifndef dap_types_h +#define dap_types_h + +#include "any.h" +#include "optional.h" +#include "variant.h" + +#include +#include + +#include + +namespace dap { + +// string is a sequence of characters. +// string defaults to an empty string. +using string = std::string; + +// boolean holds a true or false value. +// boolean defaults to false. +class boolean { + public: + inline boolean() : val(false) {} + inline boolean(bool i) : val(i) {} + inline operator bool() const { return val; } + inline boolean& operator=(bool i) { + val = i; + return *this; + } + + private: + bool val; +}; + +// integer holds a whole signed number. +// integer defaults to 0. +class integer { + public: + inline integer() : val(0) {} + inline integer(int64_t i) : val(i) {} + inline operator int64_t() const { return val; } + inline integer& operator=(int64_t i) { + val = i; + return *this; + } + inline integer operator++(int) { + auto copy = *this; + val++; + return copy; + } + + private: + int64_t val; +}; + +// number holds a 64-bit floating point number. +// number defaults to 0. +class number { + public: + inline number() : val(0.0) {} + inline number(double i) : val(i) {} + inline operator double() const { return val; } + inline number& operator=(double i) { + val = i; + return *this; + } + + private: + double val; +}; + +// array is a list of items of type T. +// array defaults to an empty list. +template +using array = std::vector; + +// object is a map of string to any. +// object defaults to an empty map. +using object = std::unordered_map; + +// null represents no value. +// null is used by any to check for no-value. +using null = std::nullptr_t; + +} // namespace dap + +#endif // dap_types_h diff --git a/libraries/cppdap/include/dap/variant.h b/libraries/cppdap/include/dap/variant.h new file mode 100644 index 000000000..96e57c230 --- /dev/null +++ b/libraries/cppdap/include/dap/variant.h @@ -0,0 +1,108 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_variant_h +#define dap_variant_h + +#include "any.h" + +namespace dap { + +// internal functionality +namespace detail { +template +struct TypeIsIn { + static constexpr bool value = false; +}; + +template +struct TypeIsIn { + static constexpr bool value = + std::is_same::value || TypeIsIn::value; +}; +} // namespace detail + +// variant represents a type-safe union of DAP types. +// variant can hold a value of any of the template argument types. +// variant defaults to a default-constructed T0. +template +class variant { + public: + // constructors + inline variant(); + template + inline variant(const T& val); + + // assignment + template + inline variant& operator=(const T& val); + + // get() returns the contained value of the type T. + // If the any does not contain a value of type T, then get() will assert. + template + inline T& get() const; + + // is() returns true iff the contained value is of type T. + template + inline bool is() const; + + // accepts() returns true iff the variant accepts values of type T. + template + static constexpr bool accepts(); + + private: + friend class Serializer; + friend class Deserializer; + any value; +}; + +template +variant::variant() : value(T0()) {} + +template +template +variant::variant(const T& v) : value(v) { + static_assert(accepts(), "variant does not accept template type T"); +} + +template +template +variant& variant::operator=(const T& v) { + static_assert(accepts(), "variant does not accept template type T"); + value = v; + return *this; +} + +template +template +T& variant::get() const { + static_assert(accepts(), "variant does not accept template type T"); + return value.get(); +} + +template +template +bool variant::is() const { + return value.is(); +} + +template +template +constexpr bool variant::accepts() { + return detail::TypeIsIn::value; +} + +} // namespace dap + +#endif // dap_variant_h diff --git a/libraries/cppdap/kokoro/license-check/build.sh b/libraries/cppdap/kokoro/license-check/build.sh new file mode 100755 index 000000000..8b109e56b --- /dev/null +++ b/libraries/cppdap/kokoro/license-check/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}:ro" \ + --workdir "${ROOT_DIR}" \ + us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/license-checker diff --git a/libraries/cppdap/kokoro/license-check/presubmit.cfg b/libraries/cppdap/kokoro/license-check/presubmit.cfg new file mode 100644 index 000000000..271ad2f6c --- /dev/null +++ b/libraries/cppdap/kokoro/license-check/presubmit.cfg @@ -0,0 +1,4 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "cppdap/kokoro/license-check/build.sh" + diff --git a/libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg b/libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg new file mode 100644 index 000000000..89357a7a3 --- /dev/null +++ b/libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg @@ -0,0 +1,14 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/macos/presubmit.sh" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "x64" +} diff --git a/libraries/cppdap/kokoro/macos/presubmit.sh b/libraries/cppdap/kokoro/macos/presubmit.sh new file mode 100755 index 000000000..d74e48595 --- /dev/null +++ b/libraries/cppdap/kokoro/macos/presubmit.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. +set -x # Display commands being run. + +BUILD_ROOT=$PWD + +cd github/cppdap + +git submodule update --init + +if [ "$BUILD_SYSTEM" == "cmake" ]; then + mkdir build + cd build + + cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1 + make -j$(sysctl -n hw.logicalcpu) + + ./cppdap-unittests +else + echo "Unknown build system: $BUILD_SYSTEM" + exit 1 +fi diff --git a/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/asan/presubmit.cfg b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/asan/presubmit.cfg new file mode 100644 index 000000000..f4657cc0b --- /dev/null +++ b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/asan/presubmit.cfg @@ -0,0 +1,19 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/ubuntu/presubmit.sh" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "x64" +} + +env_vars { + key: "BUILD_SANITIZER" + value: "asan" +} diff --git a/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg new file mode 100644 index 000000000..55afbc2a3 --- /dev/null +++ b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg @@ -0,0 +1,14 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/ubuntu/presubmit.sh" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "x64" +} diff --git a/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/tsan/presubmit.cfg b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/tsan/presubmit.cfg new file mode 100644 index 000000000..3899026ee --- /dev/null +++ b/libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/tsan/presubmit.cfg @@ -0,0 +1,19 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/ubuntu/presubmit.sh" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "x64" +} + +env_vars { + key: "BUILD_SANITIZER" + value: "tsan" +} diff --git a/libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh b/libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh new file mode 100755 index 000000000..b779160ce --- /dev/null +++ b/libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +. /bin/using.sh # Declare the bash `using` function for configuring toolchains. + +set -x # Display commands being run. + +cd github/cppdap + +# Silence "fatal: unsafe repository" errors +git config --global --add safe.directory '*' + +git submodule update --init + +if [ "$BUILD_SYSTEM" == "cmake" ]; then + using cmake-3.31.2 + using gcc-13 + + mkdir build + cd build + + build_and_run() { + cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1 $1 + make --jobs=$(nproc) + + ./cppdap-unittests + } + + if [ "$BUILD_SANITIZER" == "asan" ]; then + build_and_run "-DCPPDAP_ASAN=1" + elif [ "$BUILD_SANITIZER" == "msan" ]; then + build_and_run "-DCPPDAP_MSAN=1" + elif [ "$BUILD_SANITIZER" == "tsan" ]; then + build_and_run "-DCPPDAP_TSAN=1" + else + build_and_run + fi +else + echo "Unknown build system: $BUILD_SYSTEM" + exit 1 +fi diff --git a/libraries/cppdap/kokoro/ubuntu/presubmit.sh b/libraries/cppdap/kokoro/ubuntu/presubmit.sh new file mode 100755 index 000000000..b2c5a4395 --- /dev/null +++ b/libraries/cppdap/kokoro/ubuntu/presubmit.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +ROOT_DIR=`pwd` +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" + +# --privileged is required for some sanitizer builds, as they seem to require PTRACE privileges +docker run --rm -i \ + --privileged \ + --volume "${ROOT_DIR}:${ROOT_DIR}" \ + --volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \ + --workdir "${ROOT_DIR}" \ + --env BUILD_SYSTEM=$BUILD_SYSTEM \ + --env BUILD_TARGET_ARCH=$BUILD_TARGET_ARCH \ + --env BUILD_SANITIZER=$BUILD_SANITIZER \ + --entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \ + us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/cpp-builder diff --git a/libraries/cppdap/kokoro/windows/presubmit.bat b/libraries/cppdap/kokoro/windows/presubmit.bat new file mode 100644 index 000000000..6e6f9bfaa --- /dev/null +++ b/libraries/cppdap/kokoro/windows/presubmit.bat @@ -0,0 +1,45 @@ +REM Copyright 2020 Google LLC +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM You may obtain a copy of the License at +REM +REM https://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +@echo on + +SETLOCAL ENABLEDELAYEDEXPANSION + +SET BUILD_ROOT=%cd% +SET PATH=C:\python312;C:\cmake-3.31.2\bin;%PATH% +SET SRC=%cd%\github\cppdap + +cd %SRC% +if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! + +git submodule update --init +if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! + +SET CONFIG=Release + +mkdir %SRC%\build +cd %SRC%\build +if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! + +IF /I "%BUILD_SYSTEM%"=="cmake" ( + cmake .. -G "%BUILD_GENERATOR%" -A %BUILD_TARGET_ARCH% "-DCPPDAP_BUILD_TESTS=1" "-DCPPDAP_BUILD_EXAMPLES=1" "-DCPPDAP_WARNINGS_AS_ERRORS=1" + if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! + cmake --build . --config %CONFIG% + if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! + %CONFIG%\cppdap-unittests.exe + if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! +) ELSE ( + echo "Unknown build system: %BUILD_SYSTEM%" + exit /b 1 +) diff --git a/libraries/cppdap/kokoro/windows/vs2022-amd64/cmake/presubmit.cfg b/libraries/cppdap/kokoro/windows/vs2022-amd64/cmake/presubmit.cfg new file mode 100644 index 000000000..5800334c2 --- /dev/null +++ b/libraries/cppdap/kokoro/windows/vs2022-amd64/cmake/presubmit.cfg @@ -0,0 +1,19 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/windows/presubmit.bat" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_GENERATOR" + value: "Visual Studio 17 2022" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "x64" +} diff --git a/libraries/cppdap/kokoro/windows/vs2022-x86/cmake/presubmit.cfg b/libraries/cppdap/kokoro/windows/vs2022-x86/cmake/presubmit.cfg new file mode 100644 index 000000000..bf3d29f14 --- /dev/null +++ b/libraries/cppdap/kokoro/windows/vs2022-x86/cmake/presubmit.cfg @@ -0,0 +1,19 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Location of the continuous bash script in Git. +build_file: "cppdap/kokoro/windows/presubmit.bat" + +env_vars { + key: "BUILD_SYSTEM" + value: "cmake" +} + +env_vars { + key: "BUILD_GENERATOR" + value: "Visual Studio 17 2022" +} + +env_vars { + key: "BUILD_TARGET_ARCH" + value: "Win32" +} diff --git a/libraries/cppdap/license-checker.cfg b/libraries/cppdap/license-checker.cfg new file mode 100644 index 000000000..163aec746 --- /dev/null +++ b/libraries/cppdap/license-checker.cfg @@ -0,0 +1,28 @@ +{ + "licenses": [ + "Apache-2.0", + "Apache-2.0-Header" + ], + "paths": [ + { + "exclude": [ + ".clang-format", + ".gitattributes", + ".github/workflows/main.yml", + ".gitignore", + ".gitmodules", + ".vscode/*.json", + "**.md", + "CONTRIBUTING", + "LICENSE", + "build/**", + "examples/vscode/package.json", + "fuzz/**", + "kokoro/**.cfg", + "third_party/googletest/**", + "third_party/json/**", + "third_party/rapidjson/**" + ] + } + ] +} diff --git a/libraries/cppdap/src/any_test.cpp b/libraries/cppdap/src/any_test.cpp new file mode 100644 index 000000000..7dfb73ce8 --- /dev/null +++ b/libraries/cppdap/src/any_test.cpp @@ -0,0 +1,262 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/any.h" +#include "dap/typeof.h" +#include "dap/types.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace dap { + +struct AnyTestObject { + dap::integer i; + dap::number n; +}; + +DAP_STRUCT_TYPEINFO(AnyTestObject, + "AnyTestObject", + DAP_FIELD(i, "i"), + DAP_FIELD(n, "n")); + +inline bool operator==(const AnyTestObject& a, const AnyTestObject& b) { + return a.i == b.i && a.n == b.n; +} + +} // namespace dap + +namespace { + +template +struct TestValue {}; + +template <> +struct TestValue { + static const dap::null value; +}; +template <> +struct TestValue { + static const dap::integer value; +}; +template <> +struct TestValue { + static const dap::boolean value; +}; +template <> +struct TestValue { + static const dap::number value; +}; +template <> +struct TestValue { + static const dap::string value; +}; +template <> +struct TestValue> { + static const dap::array value; +}; +template <> +struct TestValue { + static const dap::AnyTestObject value; +}; + +const dap::null TestValue::value = nullptr; +const dap::integer TestValue::value = 20; +const dap::boolean TestValue::value = true; +const dap::number TestValue::value = 123.45; +const dap::string TestValue::value = "hello world"; +const dap::array TestValue>::value = { + "one", "two", "three"}; +const dap::AnyTestObject TestValue::value = {10, 20.30}; + +} // namespace + +TEST(Any, EmptyConstruct) { + dap::any any; + ASSERT_TRUE(any.is()); + ASSERT_FALSE(any.is()); + ASSERT_FALSE(any.is()); + ASSERT_FALSE(any.is()); + ASSERT_FALSE(any.is()); + ASSERT_FALSE(any.is()); + ASSERT_FALSE(any.is>()); + ASSERT_FALSE(any.is()); +} + +TEST(Any, Boolean) { + dap::any any(dap::boolean(true)); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get(), dap::boolean(true)); +} + +TEST(Any, Integer) { + dap::any any(dap::integer(10)); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get(), dap::integer(10)); +} + +TEST(Any, Number) { + dap::any any(dap::number(123.0f)); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get(), dap::number(123.0f)); +} + +TEST(Any, String) { + dap::any any(dap::string("hello world")); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get(), dap::string("hello world")); +} + +TEST(Any, Array) { + using array = dap::array; + dap::any any(array({10, 20, 30})); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get(), array({10, 20, 30})); +} + +TEST(Any, Object) { + dap::object o; + o["one"] = dap::integer(1); + o["two"] = dap::integer(2); + o["three"] = dap::integer(3); + dap::any any(o); + ASSERT_TRUE(any.is()); + if (any.is()) { + auto got = any.get(); + ASSERT_EQ(got.size(), 3U); + ASSERT_EQ(got.count("one"), 1U); + ASSERT_EQ(got.count("two"), 1U); + ASSERT_EQ(got.count("three"), 1U); + ASSERT_TRUE(got["one"].is()); + ASSERT_TRUE(got["two"].is()); + ASSERT_TRUE(got["three"].is()); + ASSERT_EQ(got["one"].get(), dap::integer(1)); + ASSERT_EQ(got["two"].get(), dap::integer(2)); + ASSERT_EQ(got["three"].get(), dap::integer(3)); + } +} + +TEST(Any, TestObject) { + dap::any any(dap::AnyTestObject{5, 3.0}); + ASSERT_TRUE(any.is()); + ASSERT_EQ(any.get().i, 5); + ASSERT_EQ(any.get().n, 3.0); +} + +template +class AnyT : public ::testing::Test { + protected: + template ::value && + !std::is_same::value>> + void check_val(const dap::any& any, const T0& expect) { + ASSERT_EQ(any.is(), any.is()); + ASSERT_EQ(any.get(), expect); + } + + // Special case for Null assignment, as we can assign nullptr_t to any but + // can't `get()` it + template + void check_val(const dap::any& any, const dap::null& expect) { + ASSERT_EQ(nullptr, expect); + ASSERT_TRUE(any.is()); + } + + void check_type(const dap::any& any) { + ASSERT_EQ(any.is(), (std::is_same::value)); + ASSERT_EQ(any.is(), (std::is_same::value)); + ASSERT_EQ(any.is(), (std::is_same::value)); + ASSERT_EQ(any.is(), (std::is_same::value)); + ASSERT_EQ(any.is(), (std::is_same::value)); + ASSERT_EQ(any.is>(), + (std::is_same>::value)); + ASSERT_EQ(any.is(), + (std::is_same::value)); + } +}; +TYPED_TEST_SUITE_P(AnyT); + +TYPED_TEST_P(AnyT, CopyConstruct) { + auto val = TestValue::value; + dap::any any(val); + this->check_type(any); + this->check_val(any, val); +} + +TYPED_TEST_P(AnyT, MoveConstruct) { + auto val = TestValue::value; + dap::any any(std::move(val)); + this->check_type(any); + this->check_val(any, val); +} + +TYPED_TEST_P(AnyT, Assign) { + auto val = TestValue::value; + dap::any any; + any = val; + this->check_type(any); + this->check_val(any, val); +} + +TYPED_TEST_P(AnyT, MoveAssign) { + auto val = TestValue::value; + dap::any any; + any = std::move(val); + this->check_type(any); + this->check_val(any, val); +} + +TYPED_TEST_P(AnyT, RepeatedAssign) { + dap::string str = "hello world"; + auto val = TestValue::value; + dap::any any; + any = str; + any = val; + this->check_type(any); + this->check_val(any, val); +} + +TYPED_TEST_P(AnyT, RepeatedMoveAssign) { + dap::string str = "hello world"; + auto val = TestValue::value; + dap::any any; + any = std::move(str); + any = std::move(val); + this->check_type(any); + this->check_val(any, val); +} + +REGISTER_TYPED_TEST_SUITE_P(AnyT, + CopyConstruct, + MoveConstruct, + Assign, + MoveAssign, + RepeatedAssign, + RepeatedMoveAssign); + +using AnyTypes = ::testing::Types, + dap::AnyTestObject>; +INSTANTIATE_TYPED_TEST_SUITE_P(T, AnyT, AnyTypes); + +TEST(Any, Reset) { + dap::any any(dap::integer(10)); + ASSERT_TRUE(any.is()); + any.reset(); + ASSERT_FALSE(any.is()); +} diff --git a/libraries/cppdap/src/chan.h b/libraries/cppdap/src/chan.h new file mode 100644 index 000000000..f2345e968 --- /dev/null +++ b/libraries/cppdap/src/chan.h @@ -0,0 +1,90 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_chan_h +#define dap_chan_h + +#include "dap/optional.h" + +#include +#include +#include + +namespace dap { + +template +struct Chan { + public: + void reset(); + void close(); + optional take(); + void put(T&& in); + void put(const T& in); + + private: + bool closed = false; + std::queue queue; + std::condition_variable cv; + std::mutex mutex; +}; + +template +void Chan::reset() { + std::unique_lock lock(mutex); + queue = {}; + closed = false; +} + +template +void Chan::close() { + std::unique_lock lock(mutex); + closed = true; + cv.notify_all(); +} + +template +optional Chan::take() { + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return queue.size() > 0 || closed; }); + if (queue.size() == 0) { + return optional(); + } + auto out = std::move(queue.front()); + queue.pop(); + return optional(std::move(out)); +} + +template +void Chan::put(T&& in) { + std::unique_lock lock(mutex); + auto notify = queue.size() == 0 && !closed; + queue.push(std::move(in)); + if (notify) { + cv.notify_all(); + } +} + +template +void Chan::put(const T& in) { + std::unique_lock lock(mutex); + auto notify = queue.size() == 0 && !closed; + queue.push(in); + if (notify) { + cv.notify_all(); + } +} + +} // namespace dap + +#endif // dap_chan_h diff --git a/libraries/cppdap/src/chan_test.cpp b/libraries/cppdap/src/chan_test.cpp new file mode 100644 index 000000000..4d7e0a43a --- /dev/null +++ b/libraries/cppdap/src/chan_test.cpp @@ -0,0 +1,35 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "chan.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +TEST(ChanTest, PutTakeClose) { + dap::Chan chan; + auto thread = std::thread([&] { + chan.put(10); + chan.put(20); + chan.put(30); + chan.close(); + }); + EXPECT_EQ(chan.take(), dap::optional(10)); + EXPECT_EQ(chan.take(), dap::optional(20)); + EXPECT_EQ(chan.take(), dap::optional(30)); + EXPECT_EQ(chan.take(), dap::optional()); + thread.join(); +} diff --git a/libraries/cppdap/src/content_stream.cpp b/libraries/cppdap/src/content_stream.cpp new file mode 100644 index 000000000..c5264aad1 --- /dev/null +++ b/libraries/cppdap/src/content_stream.cpp @@ -0,0 +1,198 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "content_stream.h" + +#include "dap/io.h" + +#include // strlen +#include // std::min + +namespace dap { + +//////////////////////////////////////////////////////////////////////////////// +// ContentReader +//////////////////////////////////////////////////////////////////////////////// +ContentReader::ContentReader( + const std::shared_ptr& reader, + OnInvalidData on_invalid_data /* = OnInvalidData::kIgnore */) + : reader(reader), on_invalid_data(on_invalid_data) {} + +ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept { + buf = std::move(rhs.buf); + reader = std::move(rhs.reader); + on_invalid_data = std::move(rhs.on_invalid_data); + return *this; +} + +bool ContentReader::isOpen() { + return reader ? reader->isOpen() : false; +} + +void ContentReader::close() { + if (reader) { + reader->close(); + } +} + +std::string ContentReader::read() { + // Find Content-Length header prefix + if (on_invalid_data == kClose) { + if (!match("Content-Length:")) { + return badHeader(); + } + } else { + if (!scan("Content-Length:")) { + return ""; + } + } + // Skip whitespace and tabs + while (matchAny(" \t")) { + } + // Parse length + size_t len = 0; + while (true) { + auto c = matchAny("0123456789"); + if (c == 0) { + break; + } + len *= 10; + len += size_t(c) - size_t('0'); + } + if (len == 0) { + return ""; + } + + // Expect \r\n\r\n + if (!match("\r\n\r\n")) { + return badHeader(); + } + + // Read message + if (!buffer(len)) { + return ""; + } + std::string out; + out.reserve(len); + for (size_t i = 0; i < len; i++) { + out.push_back(static_cast(buf.front())); + buf.pop_front(); + } + return out; +} + +bool ContentReader::scan(const uint8_t* seq, size_t len) { + while (buffer(len)) { + if (match(seq, len)) { + return true; + } + buf.pop_front(); + } + return false; +} + +bool ContentReader::scan(const char* str) { + auto len = strlen(str); + return scan(reinterpret_cast(str), len); +} + +bool ContentReader::match(const uint8_t* seq, size_t len) { + if (!buffer(len)) { + return false; + } + auto it = buf.begin(); + for (size_t i = 0; i < len; i++, it++) { + if (*it != seq[i]) { + return false; + } + } + for (size_t i = 0; i < len; i++) { + buf.pop_front(); + } + return true; +} + +bool ContentReader::match(const char* str) { + auto len = strlen(str); + return match(reinterpret_cast(str), len); +} + +char ContentReader::matchAny(const char* chars) { + if (!buffer(1)) { + return false; + } + int c = buf.front(); + if (auto p = strchr(chars, c)) { + buf.pop_front(); + return *p; + } + return 0; +} + +bool ContentReader::buffer(size_t bytes) { + if (bytes < buf.size()) { + return true; + } + bytes -= buf.size(); + while (bytes > 0) { + uint8_t chunk[256]; + auto numWant = std::min(sizeof(chunk), bytes); + auto numGot = reader->read(chunk, numWant); + if (numGot == 0) { + return false; + } + for (size_t i = 0; i < numGot; i++) { + buf.push_back(chunk[i]); + } + bytes -= numGot; + } + return true; +} + +std::string ContentReader::badHeader() { + if (on_invalid_data == kClose) { + close(); + } + return ""; +} + +//////////////////////////////////////////////////////////////////////////////// +// ContentWriter +//////////////////////////////////////////////////////////////////////////////// +ContentWriter::ContentWriter(const std::shared_ptr& rhs) + : writer(rhs) {} + +ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept { + writer = std::move(rhs.writer); + return *this; +} + +bool ContentWriter::isOpen() { + return writer ? writer->isOpen() : false; +} + +void ContentWriter::close() { + if (writer) { + writer->close(); + } +} + +bool ContentWriter::write(const std::string& msg) const { + auto header = + std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n"; + return writer->write(header.data(), header.size()) && + writer->write(msg.data(), msg.size()); +} + +} // namespace dap \ No newline at end of file diff --git a/libraries/cppdap/src/content_stream.h b/libraries/cppdap/src/content_stream.h new file mode 100644 index 000000000..eee998ed9 --- /dev/null +++ b/libraries/cppdap/src/content_stream.h @@ -0,0 +1,73 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_content_stream_h +#define dap_content_stream_h + +#include +#include +#include + +#include + +#include "dap/session.h" + +namespace dap { + +// Forward declarations +class Reader; +class Writer; + +class ContentReader { + public: + ContentReader() = default; + ContentReader(const std::shared_ptr&, + const OnInvalidData on_invalid_data = kIgnore); + ContentReader& operator=(ContentReader&&) noexcept; + + bool isOpen(); + void close(); + std::string read(); + + private: + bool scan(const uint8_t* seq, size_t len); + bool scan(const char* str); + bool match(const uint8_t* seq, size_t len); + bool match(const char* str); + char matchAny(const char* chars); + bool buffer(size_t bytes); + std::string badHeader(); + + std::shared_ptr reader; + std::deque buf; + OnInvalidData on_invalid_data; +}; + +class ContentWriter { + public: + ContentWriter() = default; + ContentWriter(const std::shared_ptr&); + ContentWriter& operator=(ContentWriter&&) noexcept; + + bool isOpen(); + void close(); + bool write(const std::string&) const; + + private: + std::shared_ptr writer; +}; + +} // namespace dap + +#endif // dap_content_stream_h diff --git a/libraries/cppdap/src/content_stream_test.cpp b/libraries/cppdap/src/content_stream_test.cpp new file mode 100644 index 000000000..3f0047230 --- /dev/null +++ b/libraries/cppdap/src/content_stream_test.cpp @@ -0,0 +1,126 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "content_stream.h" + +#include "string_buffer.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +namespace { + +// SingleByteReader wraps a dap::Reader to only provide a single byte for each +// read() call, regardless of the number of bytes actually requested. +class SingleByteReader : public dap::Reader { + public: + SingleByteReader(std::unique_ptr&& inner) + : inner(std::move(inner)) {} + + bool isOpen() override { return inner->isOpen(); } + void close() override { return inner->close(); } + size_t read(void* buffer, size_t) override { return inner->read(buffer, 1); }; + + private: + std::unique_ptr inner; +}; + +} // namespace + +TEST(ContentStreamTest, Write) { + auto sb = dap::StringBuffer::create(); + auto ptr = sb.get(); + dap::ContentWriter cw(std::move(sb)); + cw.write("Content payload number one"); + cw.write("Content payload number two"); + cw.write("Content payload number three"); + ASSERT_EQ(ptr->string(), + "Content-Length: 26\r\n\r\nContent payload number one" + "Content-Length: 26\r\n\r\nContent payload number two" + "Content-Length: 28\r\n\r\nContent payload number three"); +} + +TEST(ContentStreamTest, Read) { + auto sb = dap::StringBuffer::create(); + sb->write("Content-Length: 26\r\n\r\nContent payload number one"); + sb->write("some unrecognised garbage"); + sb->write("Content-Length: 26\r\n\r\nContent payload number two"); + sb->write("some more unrecognised garbage"); + sb->write("Content-Length: 28\r\n\r\nContent payload number three"); + dap::ContentReader cs(std::move(sb)); + ASSERT_EQ(cs.read(), "Content payload number one"); + ASSERT_EQ(cs.read(), "Content payload number two"); + ASSERT_EQ(cs.read(), "Content payload number three"); + ASSERT_EQ(cs.read(), ""); +} + +TEST(ContentStreamTest, ShortRead) { + auto sb = dap::StringBuffer::create(); + sb->write("Content-Length: 26\r\n\r\nContent payload number one"); + sb->write("some unrecognised garbage"); + sb->write("Content-Length: 26\r\n\r\nContent payload number two"); + sb->write("some more unrecognised garbage"); + sb->write("Content-Length: 28\r\n\r\nContent payload number three"); + dap::ContentReader cs( + std::unique_ptr(new SingleByteReader(std::move(sb)))); + ASSERT_EQ(cs.read(), "Content payload number one"); + ASSERT_EQ(cs.read(), "Content payload number two"); + ASSERT_EQ(cs.read(), "Content payload number three"); + ASSERT_EQ(cs.read(), ""); +} + +TEST(ContentStreamTest, PartialReadAndParse) { + auto sb = std::make_shared(); + sb->write("Content"); + sb->write("-Length: "); + sb->write("26"); + sb->write("\r\n\r\n"); + sb->write("Content payload number one"); + + dap::ContentReader cs(sb); + ASSERT_EQ(cs.read(), "Content payload number one"); + ASSERT_EQ(cs.read(), ""); +} + +TEST(ContentStreamTest, HttpRequest) { + const char* const part1 = + "POST / HTTP/1.1\r\n" + "Host: localhost:8001\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 99\r\n"; + const char* const part2 = + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Content-Type: text/plain;charset=UTF-8\r\n" + "Accept: */*\r\n" + "Origin: null\r\n" + "Sec-Fetch-Site: cross-site\r\n" + "Sec-Fetch-Mode: cors\r\n" + "Sec-Fetch-Dest: empty\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "\r\n" + "{\"type\":\"request\",\"command\":\"launch\",\"arguments\":{\"cmd\":\"/" + "bin/sh -c 'echo remote code execution'\"}}"; + + auto sb = dap::StringBuffer::create(); + sb->write(part1); + sb->write(part2); + + dap::ContentReader cr(std::move(sb), dap::kClose); + ASSERT_EQ(cr.read(), ""); + ASSERT_FALSE(cr.isOpen()); +} diff --git a/libraries/cppdap/src/dap_test.cpp b/libraries/cppdap/src/dap_test.cpp new file mode 100644 index 000000000..f31be464e --- /dev/null +++ b/libraries/cppdap/src/dap_test.cpp @@ -0,0 +1,72 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/dap.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +TEST(DAP, PairedInitializeTerminate) { + dap::initialize(); + dap::terminate(); +} + +TEST(DAP, NestedInitializeTerminate) { + dap::initialize(); + dap::initialize(); + dap::initialize(); + dap::terminate(); + dap::terminate(); + dap::terminate(); +} + +TEST(DAP, MultiThreadedInitializeTerminate) { + const size_t numThreads = 64; + + std::mutex mutex; + std::condition_variable cv; + size_t numInits = 0; + + std::vector threads; + threads.reserve(numThreads); + for (size_t i = 0; i < numThreads; i++) { + threads.emplace_back([&] { + dap::initialize(); + { + std::unique_lock lock(mutex); + numInits++; + if (numInits == numThreads) { + cv.notify_all(); + } else { + cv.wait(lock, [&] { return numInits == numThreads; }); + } + } + dap::terminate(); + }); + } + + for (auto& thread : threads) { + thread.join(); + } +} diff --git a/libraries/cppdap/src/io.cpp b/libraries/cppdap/src/io.cpp new file mode 100644 index 000000000..b4133e50b --- /dev/null +++ b/libraries/cppdap/src/io.cpp @@ -0,0 +1,258 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/io.h" + +#include +#include +#include +#include +#include // strlen +#include +#include +#include + +namespace { + +class Pipe : public dap::ReaderWriter { + public: + // dap::ReaderWriter compliance + bool isOpen() override { + std::unique_lock lock(mutex); + return !closed; + } + + void close() override { + std::unique_lock lock(mutex); + closed = true; + cv.notify_all(); + } + + size_t read(void* buffer, size_t bytes) override { + std::unique_lock lock(mutex); + auto out = reinterpret_cast(buffer); + size_t n = 0; + while (true) { + cv.wait(lock, [&] { return closed || data.size() > 0; }); + if (closed) { + return n; + } + for (; n < bytes && data.size() > 0; n++) { + out[n] = data.front(); + data.pop_front(); + } + if (n == bytes) { + return n; + } + } + } + + bool write(const void* buffer, size_t bytes) override { + std::unique_lock lock(mutex); + if (closed) { + return false; + } + if (bytes == 0) { + return true; + } + auto notify = data.size() == 0; + auto src = reinterpret_cast(buffer); + for (size_t i = 0; i < bytes; i++) { + data.emplace_back(src[i]); + } + if (notify) { + cv.notify_all(); + } + return true; + } + + private: + std::mutex mutex; + std::condition_variable cv; + std::deque data; + bool closed = false; +}; + +class RW : public dap::ReaderWriter { + public: + RW(const std::shared_ptr& r, const std::shared_ptr& w) + : r(r), w(w) {} + + // dap::ReaderWriter compliance + bool isOpen() override { return r->isOpen() && w->isOpen(); } + void close() override { + r->close(); + w->close(); + } + size_t read(void* buffer, size_t n) override { return r->read(buffer, n); } + bool write(const void* buffer, size_t n) override { + return w->write(buffer, n); + } + + private: + const std::shared_ptr r; + const std::shared_ptr w; +}; + +class File : public dap::ReaderWriter { + public: + File(FILE* f, bool closable) : f(f), closable(closable) {} + + ~File() { close(); } + + // dap::ReaderWriter compliance + bool isOpen() override { return !closed; } + void close() override { + if (closable) { + if (!closed.exchange(true)) { + fclose(f); + } + } + } + size_t read(void* buffer, size_t n) override { + std::unique_lock lock(readMutex); + auto out = reinterpret_cast(buffer); + for (size_t i = 0; i < n; i++) { + int c = fgetc(f); + if (c == EOF) { + return i; + } + out[i] = char(c); + } + return n; + } + bool write(const void* buffer, size_t n) override { + std::unique_lock lock(writeMutex); + if (fwrite(buffer, 1, n, f) == n) { + fflush(f); + return true; + } + return false; + } + + private: + FILE* const f; + const bool closable; + std::mutex readMutex; + std::mutex writeMutex; + std::atomic closed = {false}; +}; + +class ReaderSpy : public dap::Reader { + public: + ReaderSpy(const std::shared_ptr& r, + const std::shared_ptr& s, + const std::string& prefix) + : r(r), s(s), prefix(prefix) {} + + // dap::Reader compliance + bool isOpen() override { return r->isOpen(); } + void close() override { r->close(); } + size_t read(void* buffer, size_t n) override { + auto c = r->read(buffer, n); + if (c > 0) { + auto chars = reinterpret_cast(buffer); + std::string buf = prefix; + buf.append(chars, chars + c); + s->write(buf.data(), buf.size()); + } + return c; + } + + private: + const std::shared_ptr r; + const std::shared_ptr s; + const std::string prefix; +}; + +class WriterSpy : public dap::Writer { + public: + WriterSpy(const std::shared_ptr& w, + const std::shared_ptr& s, + const std::string& prefix) + : w(w), s(s), prefix(prefix) {} + + // dap::Writer compliance + bool isOpen() override { return w->isOpen(); } + void close() override { w->close(); } + bool write(const void* buffer, size_t n) override { + if (!w->write(buffer, n)) { + return false; + } + auto chars = reinterpret_cast(buffer); + std::string buf = prefix; + buf.append(chars, chars + n); + s->write(buf.data(), buf.size()); + return true; + } + + private: + const std::shared_ptr w; + const std::shared_ptr s; + const std::string prefix; +}; + +} // anonymous namespace + +namespace dap { + +std::shared_ptr ReaderWriter::create( + const std::shared_ptr& r, + const std::shared_ptr& w) { + return std::make_shared(r, w); +} + +std::shared_ptr pipe() { + return std::make_shared(); +} + +std::shared_ptr file(FILE* f, bool closable /* = true */) { + return std::make_shared(f, closable); +} + +std::shared_ptr file(const char* path) { + if (auto f = fopen(path, "wb")) { + return std::make_shared(f, true); + } + return nullptr; +} + +// spy() returns a Reader that copies all reads from the Reader r to the Writer +// s, using the given optional prefix. +std::shared_ptr spy(const std::shared_ptr& r, + const std::shared_ptr& s, + const char* prefix /* = "\n<-" */) { + return std::make_shared(r, s, prefix); +} + +// spy() returns a Writer that copies all writes to the Writer w to the Writer +// s, using the given optional prefix. +std::shared_ptr spy(const std::shared_ptr& w, + const std::shared_ptr& s, + const char* prefix /* = "\n->" */) { + return std::make_shared(w, s, prefix); +} + +bool writef(const std::shared_ptr& w, const char* msg, ...) { + char buf[2048]; + + va_list vararg; + va_start(vararg, msg); + vsnprintf(buf, sizeof(buf), msg, vararg); + va_end(vararg); + + return w->write(buf, strlen(buf)); +} + +} // namespace dap diff --git a/libraries/cppdap/src/json_serializer.h b/libraries/cppdap/src/json_serializer.h new file mode 100644 index 000000000..32a7ce430 --- /dev/null +++ b/libraries/cppdap/src/json_serializer.h @@ -0,0 +1,47 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_json_serializer_h +#define dap_json_serializer_h + +#if defined(CPPDAP_JSON_NLOHMANN) +#include "nlohmann_json_serializer.h" +#elif defined(CPPDAP_JSON_RAPID) +#include "rapid_json_serializer.h" +#elif defined(CPPDAP_JSON_JSONCPP) +#include "jsoncpp_json_serializer.h" +#else +#error "Unrecognised cppdap JSON library" +#endif + +namespace dap { +namespace json { + +#if defined(CPPDAP_JSON_NLOHMANN) +using Deserializer = NlohmannDeserializer; +using Serializer = NlohmannSerializer; +#elif defined(CPPDAP_JSON_RAPID) +using Deserializer = RapidDeserializer; +using Serializer = RapidSerializer; +#elif defined(CPPDAP_JSON_JSONCPP) +using Deserializer = JsonCppDeserializer; +using Serializer = JsonCppSerializer; +#else +#error "Unrecognised cppdap JSON library" +#endif + +} // namespace json +} // namespace dap + +#endif // dap_json_serializer_h diff --git a/libraries/cppdap/src/json_serializer_test.cpp b/libraries/cppdap/src/json_serializer_test.cpp new file mode 100644 index 000000000..3416cd9e1 --- /dev/null +++ b/libraries/cppdap/src/json_serializer_test.cpp @@ -0,0 +1,266 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "json_serializer.h" + +#include "dap/typeinfo.h" +#include "dap/typeof.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace dap { + +struct JSONInnerTestObject { + integer i; +}; + +DAP_STRUCT_TYPEINFO(JSONInnerTestObject, + "json-inner-test-object", + DAP_FIELD(i, "i")); + +struct JSONTestObject { + boolean b; + integer i; + number n; + array a; + object o; + string s; + optional o1; + optional o2; + JSONInnerTestObject inner; +}; + +DAP_STRUCT_TYPEINFO(JSONTestObject, + "json-test-object", + DAP_FIELD(b, "b"), + DAP_FIELD(i, "i"), + DAP_FIELD(n, "n"), + DAP_FIELD(a, "a"), + DAP_FIELD(o, "o"), + DAP_FIELD(s, "s"), + DAP_FIELD(o1, "o1"), + DAP_FIELD(o2, "o2"), + DAP_FIELD(inner, "inner")); + +struct JSONObjectNoFields {}; + +DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields"); + +struct SimpleJSONTestObject { + boolean b; + integer i; +}; +DAP_STRUCT_TYPEINFO(SimpleJSONTestObject, + "simple-json-test-object", + DAP_FIELD(b, "b"), + DAP_FIELD(i, "i")); + +} // namespace dap + +class JSONSerializer : public testing::Test { + protected: + static dap::object GetSimpleObject() { + return dap::object({{"one", dap::integer(1)}, + {"two", dap::number(2)}, + {"three", dap::string("three")}, + {"four", dap::boolean(true)}}); + } + void TEST_SIMPLE_OBJECT(const dap::object& obj) { + NESTED_TEST_FAILED = true; + auto ref_obj = GetSimpleObject(); + ASSERT_EQ(obj.size(), ref_obj.size()); + ASSERT_TRUE(obj.at("one").is()); + ASSERT_TRUE(obj.at("two").is()); + ASSERT_TRUE(obj.at("three").is()); + ASSERT_TRUE(obj.at("four").is()); + + ASSERT_EQ(ref_obj.at("one").get(), + obj.at("one").get()); + ASSERT_EQ(ref_obj.at("two").get(), + obj.at("two").get()); + ASSERT_EQ(ref_obj.at("three").get(), + obj.at("three").get()); + ASSERT_EQ(ref_obj.at("four").get(), + obj.at("four").get()); + NESTED_TEST_FAILED = false; + } + template + void TEST_SERIALIZING_DESERIALIZING(const T& encoded, T& decoded) { + NESTED_TEST_FAILED = true; + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(encoded)); + dap::json::Deserializer d(s.dump()); + ASSERT_TRUE(d.deserialize(&decoded)); + NESTED_TEST_FAILED = false; + } + bool NESTED_TEST_FAILED = false; +#define _ASSERT_PASS(NESTED_TEST) \ + NESTED_TEST; \ + ASSERT_FALSE(NESTED_TEST_FAILED); +}; + +TEST_F(JSONSerializer, SerializeDeserialize) { + dap::JSONTestObject encoded; + encoded.b = true; + encoded.i = 32; + encoded.n = 123.456; + encoded.a = {2, 4, 6, 8, 0x100000000, -2, -4, -6, -8, -0x100000000}; + encoded.o["one"] = dap::integer(1); + encoded.o["two"] = dap::number(2); + encoded.s = "hello world"; + encoded.o2 = 42; + encoded.inner.i = 70; + + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(encoded)); + + dap::JSONTestObject decoded; + dap::json::Deserializer d(s.dump()); + ASSERT_TRUE(d.deserialize(&decoded)); + + ASSERT_EQ(encoded.b, decoded.b); + ASSERT_EQ(encoded.i, decoded.i); + ASSERT_EQ(encoded.n, decoded.n); + ASSERT_EQ(encoded.a, decoded.a); + ASSERT_EQ(encoded.o["one"].get(), + decoded.o["one"].get()); + ASSERT_EQ(encoded.o["two"].get(), + decoded.o["two"].get()); + ASSERT_EQ(encoded.s, decoded.s); + ASSERT_EQ(encoded.o2, decoded.o2); + ASSERT_EQ(encoded.inner.i, decoded.inner.i); +} + +TEST_F(JSONSerializer, SerializeObjectNoFields) { + dap::JSONObjectNoFields obj; + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(obj)); + ASSERT_EQ(s.dump(), "{}"); +} + +TEST_F(JSONSerializer, SerializeDeserializeObject) { + dap::object encoded = GetSimpleObject(); + dap::object decoded; + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded)); +} + +TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObject) { + dap::object encoded; + dap::object decoded; + // object nested inside object + dap::object encoded_embed_obj = GetSimpleObject(); + dap::object decoded_embed_obj; + encoded["embed_obj"] = encoded_embed_obj; + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + ASSERT_TRUE(decoded["embed_obj"].is()); + decoded_embed_obj = decoded["embed_obj"].get(); + _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_obj)); +} + +TEST_F(JSONSerializer, SerializeDeserializeEmbeddedStruct) { + dap::object encoded; + dap::object decoded; + // object nested inside object + dap::SimpleJSONTestObject encoded_embed_struct; + encoded_embed_struct.b = true; + encoded_embed_struct.i = 50; + encoded["embed_struct"] = encoded_embed_struct; + + dap::object decoded_embed_obj; + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + ASSERT_TRUE(decoded["embed_struct"].is()); + decoded_embed_obj = decoded["embed_struct"].get(); + ASSERT_TRUE(decoded_embed_obj.at("b").is()); + ASSERT_TRUE(decoded_embed_obj.at("i").is()); + + ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get()); + ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get()); +} + +TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) { + dap::object encoded; + dap::object decoded; + // array nested inside object + dap::array encoded_embed_arr = {1, 2, 3, 4}; + dap::array decoded_embed_arr; + + encoded["embed_arr"] = encoded_embed_arr; + + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + // TODO: Deserializing array should infer basic member types + ASSERT_TRUE(decoded["embed_arr"].is>()); + decoded_embed_arr = decoded["embed_arr"].get>(); + ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); + for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { + ASSERT_TRUE(decoded_embed_arr[i].is()); + ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get()); + } +} + +TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) { + dap::object encoded; + dap::object decoded; + + dap::array encoded_embed_arr = {GetSimpleObject(), + GetSimpleObject()}; + dap::array decoded_embed_arr; + + encoded["embed_arr"] = encoded_embed_arr; + + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + // TODO: Deserializing array should infer basic member types + ASSERT_TRUE(decoded["embed_arr"].is>()); + decoded_embed_arr = decoded["embed_arr"].get>(); + ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); + for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { + ASSERT_TRUE(decoded_embed_arr[i].is()); + _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get())); + } +} + +TEST_F(JSONSerializer, DeserializeSerializeEmptyObject) { + auto empty_obj = "{}"; + dap::object decoded; + dap::json::Deserializer d(empty_obj); + ASSERT_TRUE(d.deserialize(&decoded)); + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(decoded)); + ASSERT_EQ(s.dump(), empty_obj); +} + +TEST_F(JSONSerializer, SerializeDeserializeEmbeddedEmptyObject) { + dap::object encoded_empty_obj; + dap::object encoded = {{"empty_obj", encoded_empty_obj}}; + dap::object decoded; + + _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); + ASSERT_TRUE(decoded["empty_obj"].is()); + dap::object decoded_empty_obj = decoded["empty_obj"].get(); + ASSERT_EQ(encoded_empty_obj.size(), decoded_empty_obj.size()); +} + +TEST_F(JSONSerializer, SerializeDeserializeObjectWithNulledField) { + auto thing = dap::any(dap::null()); + dap::object encoded; + encoded["nulled_field"] = dap::null(); + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(encoded)); + dap::object decoded; + auto dump = s.dump(); + dap::json::Deserializer d(dump); + ASSERT_TRUE(d.deserialize(&decoded)); + ASSERT_TRUE(encoded["nulled_field"].is()); +} diff --git a/libraries/cppdap/src/jsoncpp_json_serializer.cpp b/libraries/cppdap/src/jsoncpp_json_serializer.cpp new file mode 100644 index 000000000..954b0e5a6 --- /dev/null +++ b/libraries/cppdap/src/jsoncpp_json_serializer.cpp @@ -0,0 +1,272 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "jsoncpp_json_serializer.h" + +#include "null_json_serializer.h" + +#include +#include +#include + +namespace dap { +namespace json { + +JsonCppDeserializer::JsonCppDeserializer(const std::string& str) + : json(new Json::Value(JsonCppDeserializer::parse(str))), ownsJson(true) {} + +JsonCppDeserializer::JsonCppDeserializer(const Json::Value* json) + : json(json), ownsJson(false) {} + +JsonCppDeserializer::~JsonCppDeserializer() { + if (ownsJson) { + delete json; + } +} + +bool JsonCppDeserializer::deserialize(dap::boolean* v) const { + if (!json->isBool()) { + return false; + } + *v = json->asBool(); + return true; +} + +bool JsonCppDeserializer::deserialize(dap::integer* v) const { + if (!json->isInt64()) { + return false; + } + *v = json->asInt64(); + return true; +} + +bool JsonCppDeserializer::deserialize(dap::number* v) const { + if (!json->isNumeric()) { + return false; + } + *v = json->asDouble(); + return true; +} + +bool JsonCppDeserializer::deserialize(dap::string* v) const { + if (!json->isString()) { + return false; + } + *v = json->asString(); + return true; +} + +bool JsonCppDeserializer::deserialize(dap::object* v) const { + v->reserve(json->size()); + for (auto i = json->begin(); i != json->end(); i++) { + JsonCppDeserializer d(&*i); + dap::any val; + if (!d.deserialize(&val)) { + return false; + } + (*v)[i.name()] = val; + } + return true; +} + +bool JsonCppDeserializer::deserialize(dap::any* v) const { + if (json->isBool()) { + *v = dap::boolean(json->asBool()); + } else if (json->type() == Json::ValueType::realValue) { + // json->isDouble() returns true for integers as well, so we need to + // explicitly look for the realValue type. + *v = dap::number(json->asDouble()); + } else if (json->isInt64()) { + *v = dap::integer(json->asInt64()); + } else if (json->isString()) { + *v = json->asString(); + } else if (json->isObject()) { + dap::object obj; + if (!deserialize(&obj)) { + return false; + } + *v = obj; + } else if (json->isArray()) { + dap::array arr; + if (!deserialize(&arr)) { + return false; + } + *v = arr; + } else if (json->isNull()) { + *v = null(); + } else { + return false; + } + return true; +} + +size_t JsonCppDeserializer::count() const { + return json->size(); +} + +bool JsonCppDeserializer::array( + const std::function& cb) const { + if (!json->isArray()) { + return false; + } + for (const auto& value : *json) { + JsonCppDeserializer d(&value); + if (!cb(&d)) { + return false; + } + } + return true; +} + +bool JsonCppDeserializer::field( + const std::string& name, + const std::function& cb) const { + if (!json->isObject()) { + return false; + } + auto value = json->find(name.data(), name.data() + name.size()); + if (value == nullptr) { + return cb(&NullDeserializer::instance); + } + JsonCppDeserializer d(value); + return cb(&d); +} + +Json::Value JsonCppDeserializer::parse(const std::string& text) { + Json::CharReaderBuilder builder; + auto jsonReader = std::unique_ptr(builder.newCharReader()); + Json::Value json; + std::string error; + if (!jsonReader->parse(text.data(), text.data() + text.size(), &json, + &error)) { + // cppdap expects that the JSON layer does not throw exceptions. + std::abort(); + } + return json; +} + +JsonCppSerializer::JsonCppSerializer() + : json(new Json::Value()), ownsJson(true) {} + +JsonCppSerializer::JsonCppSerializer(Json::Value* json) + : json(json), ownsJson(false) {} + +JsonCppSerializer::~JsonCppSerializer() { + if (ownsJson) { + delete json; + } +} + +std::string JsonCppSerializer::dump() const { + Json::StreamWriterBuilder writer; + return Json::writeString(writer, *json); +} + +bool JsonCppSerializer::serialize(dap::boolean v) { + *json = (bool)v; + return true; +} + +bool JsonCppSerializer::serialize(dap::integer v) { + *json = (Json::LargestInt)v; + return true; +} + +bool JsonCppSerializer::serialize(dap::number v) { + *json = (double)v; + return true; +} + +bool JsonCppSerializer::serialize(const dap::string& v) { + *json = v; + return true; +} + +bool JsonCppSerializer::serialize(const dap::object& v) { + if (!json->isObject()) { + *json = Json::Value(Json::objectValue); + } + for (auto& it : v) { + JsonCppSerializer s(&(*json)[it.first]); + if (!s.serialize(it.second)) { + return false; + } + } + return true; +} + +bool JsonCppSerializer::serialize(const dap::any& v) { + if (v.is()) { + *json = (bool)v.get(); + } else if (v.is()) { + *json = (Json::LargestInt)v.get(); + } else if (v.is()) { + *json = (double)v.get(); + } else if (v.is()) { + *json = v.get(); + } else if (v.is()) { + // reachable if dap::object nested is inside other dap::object + return serialize(v.get()); + } else if (v.is()) { + } else { + // reachable if array or custom serialized type is nested inside other + auto type = get_any_type(v); + auto value = get_any_val(v); + if (type && value) { + return type->serialize(this, value); + } + return false; + } + return true; +} + +bool JsonCppSerializer::array(size_t count, + const std::function& cb) { + *json = Json::Value(Json::arrayValue); + for (size_t i = 0; i < count; i++) { + JsonCppSerializer s(&(*json)[Json::Value::ArrayIndex(i)]); + if (!cb(&s)) { + return false; + } + } + return true; +} + +bool JsonCppSerializer::object( + const std::function& cb) { + struct FS : public FieldSerializer { + Json::Value* const json; + + FS(Json::Value* json) : json(json) {} + bool field(const std::string& name, const SerializeFunc& cb) override { + JsonCppSerializer s(&(*json)[name]); + auto res = cb(&s); + if (s.removed) { + json->removeMember(name); + } + return res; + } + }; + + *json = Json::Value(Json::objectValue); + FS fs{json}; + return cb(&fs); +} + +void JsonCppSerializer::remove() { + removed = true; +} + +} // namespace json +} // namespace dap diff --git a/libraries/cppdap/src/jsoncpp_json_serializer.h b/libraries/cppdap/src/jsoncpp_json_serializer.h new file mode 100644 index 000000000..6bdf6a451 --- /dev/null +++ b/libraries/cppdap/src/jsoncpp_json_serializer.h @@ -0,0 +1,134 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_jsoncpp_json_serializer_h +#define dap_jsoncpp_json_serializer_h + +#include "dap/protocol.h" +#include "dap/serialization.h" +#include "dap/types.h" + +#include + +namespace dap { +namespace json { + +struct JsonCppDeserializer : public dap::Deserializer { + explicit JsonCppDeserializer(const std::string&); + ~JsonCppDeserializer(); + + // dap::Deserializer compliance + bool deserialize(boolean* v) const override; + bool deserialize(integer* v) const override; + bool deserialize(number* v) const override; + bool deserialize(string* v) const override; + bool deserialize(object* v) const override; + bool deserialize(any* v) const override; + size_t count() const override; + bool array(const std::function&) const override; + bool field(const std::string& name, + const std::function&) const override; + + // Unhide base overloads + template + inline bool field(const std::string& name, T* v) { + return dap::Deserializer::field(name, v); + } + + template ::has_custom_serialization>> + inline bool deserialize(T* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::array* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::optional* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::variant* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool field(const std::string& name, T* v) const { + return dap::Deserializer::deserialize(name, v); + } + + private: + JsonCppDeserializer(const Json::Value*); + static Json::Value parse(const std::string& text); + const Json::Value* const json; + const bool ownsJson; +}; + +struct JsonCppSerializer : public dap::Serializer { + JsonCppSerializer(); + ~JsonCppSerializer(); + + std::string dump() const; + + // dap::Serializer compliance + bool serialize(boolean v) override; + bool serialize(integer v) override; + bool serialize(number v) override; + bool serialize(const string& v) override; + bool serialize(const dap::object& v) override; + bool serialize(const any& v) override; + bool array(size_t count, + const std::function&) override; + bool object(const std::function&) override; + void remove() override; + + // Unhide base overloads + template ::has_custom_serialization>> + inline bool serialize(const T& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::array& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::optional& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::variant& v) { + return dap::Serializer::serialize(v); + } + + inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } + + private: + JsonCppSerializer(Json::Value*); + Json::Value* const json; + const bool ownsJson; + bool removed = false; +}; + +} // namespace json +} // namespace dap + +#endif // dap_jsoncpp_json_serializer_h diff --git a/libraries/cppdap/src/network.cpp b/libraries/cppdap/src/network.cpp new file mode 100644 index 000000000..7d1da7a1c --- /dev/null +++ b/libraries/cppdap/src/network.cpp @@ -0,0 +1,107 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/network.h" + +#include "socket.h" + +#include +#include +#include +#include + +namespace { + +class Impl : public dap::net::Server { + public: + Impl() : stopped{true} {} + + ~Impl() { stop(); } + + bool start(int port, + const OnConnect& onConnect, + const OnError& onError) override { + return start("localhost", port, onConnect, onError); + } + + bool start(const char* address, + int port, + const OnConnect& onConnect, + const OnError& onError) override { + std::unique_lock lock(mutex); + stopWithLock(); + socket = std::unique_ptr( + new dap::Socket(address, std::to_string(port).c_str())); + + if (!socket->isOpen()) { + onError("Failed to open socket"); + return false; + } + + stopped = false; + thread = std::thread([=] { + while (true) { + if (auto rw = socket->accept()) { + onConnect(rw); + continue; + } + if (!stopped) { + onError("Failed to accept connection"); + } + break; + }; + }); + + return true; + } + + void stop() override { + std::unique_lock lock(mutex); + stopWithLock(); + } + + private: + bool isRunning() { return !stopped; } + + void stopWithLock() { + if (!stopped.exchange(true)) { + socket->close(); + thread.join(); + } + } + + std::mutex mutex; + std::thread thread; + std::unique_ptr socket; + std::atomic stopped; + OnError errorHandler; +}; + +} // anonymous namespace + +namespace dap { +namespace net { + +std::unique_ptr Server::create() { + return std::unique_ptr(new Impl()); +} + +std::shared_ptr connect(const char* addr, + int port, + uint32_t timeoutMillis) { + return Socket::connect(addr, std::to_string(port).c_str(), timeoutMillis); +} + +} // namespace net +} // namespace dap diff --git a/libraries/cppdap/src/network_test.cpp b/libraries/cppdap/src/network_test.cpp new file mode 100644 index 000000000..57bb0a903 --- /dev/null +++ b/libraries/cppdap/src/network_test.cpp @@ -0,0 +1,110 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/network.h" +#include "dap/io.h" + +#include "chan.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +namespace { + +constexpr int port = 19021; + +bool write(const std::shared_ptr& w, const std::string& s) { + return w->write(s.data(), s.size()) && w->write("\0", 1); +} + +std::string read(const std::shared_ptr& r) { + char c; + std::string s; + while (r->read(&c, sizeof(c)) > 0) { + if (c == '\0') { + return s; + } + s += c; + } + return r->isOpen() ? "" : ""; +} + +} // anonymous namespace + +TEST(Network, ClientServer) { + dap::Chan done; + auto server = dap::net::Server::create(); + if (!server->start( + port, + [&](const std::shared_ptr& rw) { + ASSERT_EQ(read(rw), "client to server"); + ASSERT_TRUE(write(rw, "server to client")); + done.put(true); + }, + [&](const char* err) { FAIL() << "Server error: " << err; })) { + FAIL() << "Couldn't start server"; + return; + } + + for (int i = 0; i < 5; i++) { + auto client = dap::net::connect("localhost", port); + ASSERT_NE(client, nullptr) << "Failed to connect client " << i; + ASSERT_TRUE(write(client, "client to server")); + ASSERT_EQ(read(client), "server to client"); + done.take(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + server.reset(); +} + +TEST(Network, ServerRepeatStopAndRestart) { + dap::Chan done; + auto onConnect = [&](const std::shared_ptr& rw) { + ASSERT_EQ(read(rw), "client to server"); + ASSERT_TRUE(write(rw, "server to client")); + done.put(true); + }; + auto onError = [&](const char* err) { FAIL() << "Server error: " << err; }; + + auto server = dap::net::Server::create(); + if (!server->start(port, onConnect, onError)) { + FAIL() << "Couldn't start server"; + return; + } + + server->stop(); + server->stop(); + server->stop(); + + if (!server->start(port, onConnect, onError)) { + FAIL() << "Couldn't restart server"; + return; + } + + auto client = dap::net::connect("localhost", port); + ASSERT_NE(client, nullptr) << "Failed to connect"; + ASSERT_TRUE(write(client, "client to server")); + ASSERT_EQ(read(client), "server to client"); + done.take(); + + server->stop(); + server->stop(); + server->stop(); + + server.reset(); +} diff --git a/libraries/cppdap/src/nlohmann_json_serializer.cpp b/libraries/cppdap/src/nlohmann_json_serializer.cpp new file mode 100644 index 000000000..783423032 --- /dev/null +++ b/libraries/cppdap/src/nlohmann_json_serializer.cpp @@ -0,0 +1,260 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "nlohmann_json_serializer.h" + +#include "null_json_serializer.h" + +// Disable JSON exceptions. We should be guarding against any exceptions being +// fired in this file. +#define JSON_NOEXCEPTION 1 +#include + +namespace dap { +namespace json { + +NlohmannDeserializer::NlohmannDeserializer(const std::string& str) + : json(new nlohmann::json(nlohmann::json::parse(str, nullptr, false))), + ownsJson(true) {} + +NlohmannDeserializer::NlohmannDeserializer(const nlohmann::json* json) + : json(json), ownsJson(false) {} + +NlohmannDeserializer::~NlohmannDeserializer() { + if (ownsJson) { + delete json; + } +} + +bool NlohmannDeserializer::deserialize(dap::boolean* v) const { + if (!json->is_boolean()) { + return false; + } + *v = json->get(); + return true; +} + +bool NlohmannDeserializer::deserialize(dap::integer* v) const { + if (!json->is_number_integer()) { + return false; + } + *v = json->get(); + return true; +} + +bool NlohmannDeserializer::deserialize(dap::number* v) const { + if (!json->is_number()) { + return false; + } + *v = json->get(); + return true; +} + +bool NlohmannDeserializer::deserialize(dap::string* v) const { + if (!json->is_string()) { + return false; + } + *v = json->get(); + return true; +} + +bool NlohmannDeserializer::deserialize(dap::object* v) const { + v->reserve(json->size()); + for (auto& el : json->items()) { + NlohmannDeserializer d(&el.value()); + dap::any val; + if (!d.deserialize(&val)) { + return false; + } + (*v)[el.key()] = val; + } + return true; +} + +bool NlohmannDeserializer::deserialize(dap::any* v) const { + if (json->is_boolean()) { + *v = dap::boolean(json->get()); + } else if (json->is_number_float()) { + *v = dap::number(json->get()); + } else if (json->is_number_integer()) { + *v = dap::integer(json->get()); + } else if (json->is_string()) { + *v = json->get(); + } else if (json->is_object()) { + dap::object obj; + if (!deserialize(&obj)) { + return false; + } + *v = obj; + } else if (json->is_array()) { + dap::array arr; + if (!deserialize(&arr)) { + return false; + } + *v = arr; + } else if (json->is_null()) { + *v = null(); + } else { + return false; + } + return true; +} + +size_t NlohmannDeserializer::count() const { + return json->size(); +} + +bool NlohmannDeserializer::array( + const std::function& cb) const { + if (!json->is_array()) { + return false; + } + for (size_t i = 0; i < json->size(); i++) { + NlohmannDeserializer d(&(*json)[i]); + if (!cb(&d)) { + return false; + } + } + return true; +} + +bool NlohmannDeserializer::field( + const std::string& name, + const std::function& cb) const { + if (!json->is_structured()) { + return false; + } + auto it = json->find(name); + if (it == json->end()) { + return cb(&NullDeserializer::instance); + } + auto obj = *it; + NlohmannDeserializer d(&obj); + return cb(&d); +} + +NlohmannSerializer::NlohmannSerializer() + : json(new nlohmann::json()), ownsJson(true) {} + +NlohmannSerializer::NlohmannSerializer(nlohmann::json* json) + : json(json), ownsJson(false) {} + +NlohmannSerializer::~NlohmannSerializer() { + if (ownsJson) { + delete json; + } +} + +std::string NlohmannSerializer::dump() const { + return json->dump(); +} + +bool NlohmannSerializer::serialize(dap::boolean v) { + *json = (bool)v; + return true; +} + +bool NlohmannSerializer::serialize(dap::integer v) { + *json = (int64_t)v; + return true; +} + +bool NlohmannSerializer::serialize(dap::number v) { + *json = (double)v; + return true; +} + +bool NlohmannSerializer::serialize(const dap::string& v) { + *json = v; + return true; +} + +bool NlohmannSerializer::serialize(const dap::object& v) { + if (!json->is_object()) { + *json = nlohmann::json::object(); + } + for (auto& it : v) { + NlohmannSerializer s(&(*json)[it.first]); + if (!s.serialize(it.second)) { + return false; + } + } + return true; +} + +bool NlohmannSerializer::serialize(const dap::any& v) { + if (v.is()) { + *json = (bool)v.get(); + } else if (v.is()) { + *json = (int64_t)v.get(); + } else if (v.is()) { + *json = (double)v.get(); + } else if (v.is()) { + *json = v.get(); + } else if (v.is()) { + // reachable if dap::object nested is inside other dap::object + return serialize(v.get()); + } else if (v.is()) { + } else { + // reachable if array or custom serialized type is nested inside other + auto type = get_any_type(v); + auto value = get_any_val(v); + if (type && value) { + return type->serialize(this, value); + } + return false; + } + return true; +} + +bool NlohmannSerializer::array( + size_t count, + const std::function& cb) { + *json = std::vector(); + for (size_t i = 0; i < count; i++) { + NlohmannSerializer s(&(*json)[i]); + if (!cb(&s)) { + return false; + } + } + return true; +} + +bool NlohmannSerializer::object( + const std::function& cb) { + struct FS : public FieldSerializer { + nlohmann::json* const json; + + FS(nlohmann::json* json) : json(json) {} + bool field(const std::string& name, const SerializeFunc& cb) override { + NlohmannSerializer s(&(*json)[name]); + auto res = cb(&s); + if (s.removed) { + json->erase(name); + } + return res; + } + }; + + *json = nlohmann::json({}, false, nlohmann::json::value_t::object); + FS fs{json}; + return cb(&fs); +} + +void NlohmannSerializer::remove() { + removed = true; +} + +} // namespace json +} // namespace dap diff --git a/libraries/cppdap/src/nlohmann_json_serializer.h b/libraries/cppdap/src/nlohmann_json_serializer.h new file mode 100644 index 000000000..f75fe78bd --- /dev/null +++ b/libraries/cppdap/src/nlohmann_json_serializer.h @@ -0,0 +1,133 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_nlohmann_json_serializer_h +#define dap_nlohmann_json_serializer_h + +#include "dap/protocol.h" +#include "dap/serialization.h" +#include "dap/types.h" + +#include + +namespace dap { +namespace json { + +struct NlohmannDeserializer : public dap::Deserializer { + explicit NlohmannDeserializer(const std::string&); + ~NlohmannDeserializer(); + + // dap::Deserializer compliance + bool deserialize(boolean* v) const override; + bool deserialize(integer* v) const override; + bool deserialize(number* v) const override; + bool deserialize(string* v) const override; + bool deserialize(object* v) const override; + bool deserialize(any* v) const override; + size_t count() const override; + bool array(const std::function&) const override; + bool field(const std::string& name, + const std::function&) const override; + + // Unhide base overloads + template + inline bool field(const std::string& name, T* v) { + return dap::Deserializer::field(name, v); + } + + template ::has_custom_serialization>> + inline bool deserialize(T* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::array* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::optional* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::variant* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool field(const std::string& name, T* v) const { + return dap::Deserializer::deserialize(name, v); + } + + private: + NlohmannDeserializer(const nlohmann::json*); + const nlohmann::json* const json; + const bool ownsJson; +}; + +struct NlohmannSerializer : public dap::Serializer { + NlohmannSerializer(); + ~NlohmannSerializer(); + + std::string dump() const; + + // dap::Serializer compliance + bool serialize(boolean v) override; + bool serialize(integer v) override; + bool serialize(number v) override; + bool serialize(const string& v) override; + bool serialize(const dap::object& v) override; + bool serialize(const any& v) override; + bool array(size_t count, + const std::function&) override; + bool object(const std::function&) override; + void remove() override; + + // Unhide base overloads + template ::has_custom_serialization>> + inline bool serialize(const T& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::array& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::optional& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::variant& v) { + return dap::Serializer::serialize(v); + } + + inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } + + private: + NlohmannSerializer(nlohmann::json*); + nlohmann::json* const json; + const bool ownsJson; + bool removed = false; +}; + +} // namespace json +} // namespace dap + +#endif // dap_nlohmann_json_serializer_h \ No newline at end of file diff --git a/libraries/cppdap/src/null_json_serializer.cpp b/libraries/cppdap/src/null_json_serializer.cpp new file mode 100644 index 000000000..5aa5a03aa --- /dev/null +++ b/libraries/cppdap/src/null_json_serializer.cpp @@ -0,0 +1,23 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "null_json_serializer.h" + +namespace dap { +namespace json { + +NullDeserializer NullDeserializer::instance; + +} // namespace json +} // namespace dap diff --git a/libraries/cppdap/src/null_json_serializer.h b/libraries/cppdap/src/null_json_serializer.h new file mode 100644 index 000000000..c92b99a38 --- /dev/null +++ b/libraries/cppdap/src/null_json_serializer.h @@ -0,0 +1,47 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_null_json_serializer_h +#define dap_null_json_serializer_h + +#include "dap/protocol.h" +#include "dap/serialization.h" +#include "dap/types.h" + +namespace dap { +namespace json { + +struct NullDeserializer : public dap::Deserializer { + static NullDeserializer instance; + + bool deserialize(dap::boolean*) const override { return false; } + bool deserialize(dap::integer*) const override { return false; } + bool deserialize(dap::number*) const override { return false; } + bool deserialize(dap::string*) const override { return false; } + bool deserialize(dap::object*) const override { return false; } + bool deserialize(dap::any*) const override { return false; } + size_t count() const override { return 0; } + bool array(const std::function&) const override { + return false; + } + bool field(const std::string&, + const std::function&) const override { + return false; + } +}; + +} // namespace json +} // namespace dap + +#endif // dap_null_json_serializer_h diff --git a/libraries/cppdap/src/optional_test.cpp b/libraries/cppdap/src/optional_test.cpp new file mode 100644 index 000000000..b2590fc7a --- /dev/null +++ b/libraries/cppdap/src/optional_test.cpp @@ -0,0 +1,169 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/optional.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +TEST(Optional, EmptyConstruct) { + dap::optional opt; + ASSERT_FALSE(opt); + ASSERT_FALSE(opt.has_value()); +} + +TEST(Optional, ValueConstruct) { + dap::optional opt(10); + ASSERT_TRUE(opt); + ASSERT_TRUE(opt.has_value()); + ASSERT_EQ(opt.value(), 10); +} + +TEST(Optional, CopyConstruct) { + dap::optional a("meow"); + dap::optional b(a); + ASSERT_EQ(a, b); + ASSERT_EQ(a.value(), "meow"); + ASSERT_EQ(b.value(), "meow"); +} + +TEST(Optional, CopyCastConstruct) { + dap::optional a(10); + dap::optional b(a); + ASSERT_EQ(a, b); + ASSERT_EQ(b.value(), (uint16_t)10); +} + +TEST(Optional, MoveConstruct) { + dap::optional a("meow"); + dap::optional b(std::move(a)); + ASSERT_EQ(b.value(), "meow"); +} + +TEST(Optional, MoveCastConstruct) { + dap::optional a(10); + dap::optional b(std::move(a)); + ASSERT_EQ(b.value(), (uint16_t)10); +} + +TEST(Optional, AssignValue) { + dap::optional a; + std::string b = "meow"; + a = b; + ASSERT_EQ(a.value(), "meow"); + ASSERT_EQ(b, "meow"); +} + +TEST(Optional, AssignOptional) { + dap::optional a; + dap::optional b("meow"); + a = b; + ASSERT_EQ(a.value(), "meow"); + ASSERT_EQ(b.value(), "meow"); +} + +TEST(Optional, MoveAssignOptional) { + dap::optional a; + dap::optional b("meow"); + a = std::move(b); + ASSERT_EQ(a.value(), "meow"); +} + +TEST(Optional, StarDeref) { + dap::optional a("meow"); + ASSERT_EQ(*a, "meow"); +} + +TEST(Optional, StarDerefConst) { + const dap::optional a("meow"); + ASSERT_EQ(*a, "meow"); +} + +TEST(Optional, ArrowDeref) { + struct S { + int i; + }; + dap::optional a(S{10}); + ASSERT_EQ(a->i, 10); +} + +TEST(Optional, ArrowDerefConst) { + struct S { + int i; + }; + const dap::optional a(S{10}); + ASSERT_EQ(a->i, 10); +} + +TEST(Optional, Value) { + const dap::optional a("meow"); + ASSERT_EQ(a.value(), "meow"); +} + +TEST(Optional, ValueDefault) { + const dap::optional a; + const dap::optional b("woof"); + ASSERT_EQ(a.value("meow"), "meow"); + ASSERT_EQ(b.value("meow"), "woof"); +} + +TEST(Optional, CompareLT) { + ASSERT_FALSE(dap::optional(5) < dap::optional(3)); + ASSERT_FALSE(dap::optional(5) < dap::optional(5)); + ASSERT_TRUE(dap::optional(5) < dap::optional(10)); + ASSERT_TRUE(dap::optional() < dap::optional(10)); + ASSERT_FALSE(dap::optional() < dap::optional()); +} + +TEST(Optional, CompareLE) { + ASSERT_FALSE(dap::optional(5) <= dap::optional(3)); + ASSERT_TRUE(dap::optional(5) <= dap::optional(5)); + ASSERT_TRUE(dap::optional(5) <= dap::optional(10)); + ASSERT_TRUE(dap::optional() <= dap::optional(10)); + ASSERT_TRUE(dap::optional() <= dap::optional()); +} + +TEST(Optional, CompareGT) { + ASSERT_TRUE(dap::optional(5) > dap::optional(3)); + ASSERT_FALSE(dap::optional(5) > dap::optional(5)); + ASSERT_FALSE(dap::optional(5) > dap::optional(10)); + ASSERT_FALSE(dap::optional() > dap::optional(10)); + ASSERT_FALSE(dap::optional() > dap::optional()); +} + +TEST(Optional, CompareGE) { + ASSERT_TRUE(dap::optional(5) >= dap::optional(3)); + ASSERT_TRUE(dap::optional(5) >= dap::optional(5)); + ASSERT_FALSE(dap::optional(5) >= dap::optional(10)); + ASSERT_FALSE(dap::optional() >= dap::optional(10)); + ASSERT_TRUE(dap::optional() >= dap::optional()); +} + +TEST(Optional, CompareEQ) { + ASSERT_FALSE(dap::optional(5) == dap::optional(3)); + ASSERT_TRUE(dap::optional(5) == dap::optional(5)); + ASSERT_FALSE(dap::optional(5) == dap::optional(10)); + ASSERT_FALSE(dap::optional() == dap::optional(10)); + ASSERT_TRUE(dap::optional() == dap::optional()); +} + +TEST(Optional, CompareNEQ) { + ASSERT_TRUE(dap::optional(5) != dap::optional(3)); + ASSERT_FALSE(dap::optional(5) != dap::optional(5)); + ASSERT_TRUE(dap::optional(5) != dap::optional(10)); + ASSERT_TRUE(dap::optional() != dap::optional(10)); + ASSERT_FALSE(dap::optional() != dap::optional()); +} diff --git a/libraries/cppdap/src/protocol_events.cpp b/libraries/cppdap/src/protocol_events.cpp new file mode 100644 index 000000000..f97324c1a --- /dev/null +++ b/libraries/cppdap/src/protocol_events.cpp @@ -0,0 +1,127 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#include "dap/protocol.h" + +namespace dap { + +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent, + "breakpoint", + DAP_FIELD(breakpoint, "breakpoint"), + DAP_FIELD(reason, "reason")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent, + "capabilities", + DAP_FIELD(capabilities, "capabilities")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent, + "continued", + DAP_FIELD(allThreadsContinued, + "allThreadsContinued"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent, + "exited", + DAP_FIELD(exitCode, "exitCode")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized"); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(InvalidatedEvent, + "invalidated", + DAP_FIELD(areas, "areas"), + DAP_FIELD(stackFrameId, "stackFrameId"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent, + "loadedSource", + DAP_FIELD(reason, "reason"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(MemoryEvent, + "memory", + DAP_FIELD(count, "count"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(offset, "offset")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent, + "module", + DAP_FIELD(module, "module"), + DAP_FIELD(reason, "reason")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent, + "output", + DAP_FIELD(category, "category"), + DAP_FIELD(column, "column"), + DAP_FIELD(data, "data"), + DAP_FIELD(group, "group"), + DAP_FIELD(line, "line"), + DAP_FIELD(locationReference, "locationReference"), + DAP_FIELD(output, "output"), + DAP_FIELD(source, "source"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ProcessEvent, + "process", + DAP_FIELD(isLocalProcess, "isLocalProcess"), + DAP_FIELD(name, "name"), + DAP_FIELD(pointerSize, "pointerSize"), + DAP_FIELD(startMethod, "startMethod"), + DAP_FIELD(systemProcessId, "systemProcessId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressEndEvent, + "progressEnd", + DAP_FIELD(message, "message"), + DAP_FIELD(progressId, "progressId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressStartEvent, + "progressStart", + DAP_FIELD(cancellable, "cancellable"), + DAP_FIELD(message, "message"), + DAP_FIELD(percentage, "percentage"), + DAP_FIELD(progressId, "progressId"), + DAP_FIELD(requestId, "requestId"), + DAP_FIELD(title, "title")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressUpdateEvent, + "progressUpdate", + DAP_FIELD(message, "message"), + DAP_FIELD(percentage, "percentage"), + DAP_FIELD(progressId, "progressId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent, + "stopped", + DAP_FIELD(allThreadsStopped, "allThreadsStopped"), + DAP_FIELD(description, "description"), + DAP_FIELD(hitBreakpointIds, "hitBreakpointIds"), + DAP_FIELD(preserveFocusHint, "preserveFocusHint"), + DAP_FIELD(reason, "reason"), + DAP_FIELD(text, "text"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent, + "terminated", + DAP_FIELD(restart, "restart")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent, + "thread", + DAP_FIELD(reason, "reason"), + DAP_FIELD(threadId, "threadId")); + +} // namespace dap diff --git a/libraries/cppdap/src/protocol_requests.cpp b/libraries/cppdap/src/protocol_requests.cpp new file mode 100644 index 000000000..250ee0193 --- /dev/null +++ b/libraries/cppdap/src/protocol_requests.cpp @@ -0,0 +1,293 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#include "dap/protocol.h" + +namespace dap { + +DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachRequest, + "attach", + DAP_FIELD(restart, "__restart")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsRequest, + "breakpointLocations", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(line, "line"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelRequest, + "cancel", + DAP_FIELD(progressId, "progressId"), + DAP_FIELD(requestId, "requestId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsRequest, + "completions", + DAP_FIELD(column, "column"), + DAP_FIELD(frameId, "frameId"), + DAP_FIELD(line, "line"), + DAP_FIELD(text, "text")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneRequest, "configurationDone"); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest, + "continue", + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest, + "dataBreakpointInfo", + DAP_FIELD(asAddress, "asAddress"), + DAP_FIELD(bytes, "bytes"), + DAP_FIELD(frameId, "frameId"), + DAP_FIELD(mode, "mode"), + DAP_FIELD(name, "name"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleRequest, + "disassemble", + DAP_FIELD(instructionCount, "instructionCount"), + DAP_FIELD(instructionOffset, "instructionOffset"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(offset, "offset"), + DAP_FIELD(resolveSymbols, "resolveSymbols")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectRequest, + "disconnect", + DAP_FIELD(restart, "restart"), + DAP_FIELD(suspendDebuggee, "suspendDebuggee"), + DAP_FIELD(terminateDebuggee, + "terminateDebuggee")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateRequest, + "evaluate", + DAP_FIELD(column, "column"), + DAP_FIELD(context, "context"), + DAP_FIELD(expression, "expression"), + DAP_FIELD(format, "format"), + DAP_FIELD(frameId, "frameId"), + DAP_FIELD(line, "line"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoRequest, + "exceptionInfo", + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoRequest, + "goto", + DAP_FIELD(targetId, "targetId"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsRequest, + "gotoTargets", + DAP_FIELD(column, "column"), + DAP_FIELD(line, "line"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO( + InitializeRequest, + "initialize", + DAP_FIELD(adapterID, "adapterID"), + DAP_FIELD(clientID, "clientID"), + DAP_FIELD(clientName, "clientName"), + DAP_FIELD(columnsStartAt1, "columnsStartAt1"), + DAP_FIELD(linesStartAt1, "linesStartAt1"), + DAP_FIELD(locale, "locale"), + DAP_FIELD(pathFormat, "pathFormat"), + DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"), + DAP_FIELD(supportsArgsCanBeInterpretedByShell, + "supportsArgsCanBeInterpretedByShell"), + DAP_FIELD(supportsInvalidatedEvent, "supportsInvalidatedEvent"), + DAP_FIELD(supportsMemoryEvent, "supportsMemoryEvent"), + DAP_FIELD(supportsMemoryReferences, "supportsMemoryReferences"), + DAP_FIELD(supportsProgressReporting, "supportsProgressReporting"), + DAP_FIELD(supportsRunInTerminalRequest, "supportsRunInTerminalRequest"), + DAP_FIELD(supportsStartDebuggingRequest, "supportsStartDebuggingRequest"), + DAP_FIELD(supportsVariablePaging, "supportsVariablePaging"), + DAP_FIELD(supportsVariableType, "supportsVariableType")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchRequest, + "launch", + DAP_FIELD(restart, "__restart"), + DAP_FIELD(noDebug, "noDebug")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesRequest, "loadedSources"); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LocationsRequest, + "locations", + DAP_FIELD(locationReference, + "locationReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesRequest, + "modules", + DAP_FIELD(moduleCount, "moduleCount"), + DAP_FIELD(startModule, "startModule")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(NextRequest, + "next", + DAP_FIELD(granularity, "granularity"), + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseRequest, + "pause", + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryRequest, + "readMemory", + DAP_FIELD(count, "count"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(offset, "offset")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameRequest, + "restartFrame", + DAP_FIELD(frameId, "frameId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartRequest, + "restart", + DAP_FIELD(arguments, "arguments")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueRequest, + "reverseContinue", + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalRequest, + "runInTerminal", + DAP_FIELD(args, "args"), + DAP_FIELD(argsCanBeInterpretedByShell, + "argsCanBeInterpretedByShell"), + DAP_FIELD(cwd, "cwd"), + DAP_FIELD(env, "env"), + DAP_FIELD(kind, "kind"), + DAP_FIELD(title, "title")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesRequest, + "scopes", + DAP_FIELD(frameId, "frameId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsRequest, + "setBreakpoints", + DAP_FIELD(breakpoints, "breakpoints"), + DAP_FIELD(lines, "lines"), + DAP_FIELD(source, "source"), + DAP_FIELD(sourceModified, "sourceModified")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsRequest, + "setDataBreakpoints", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest, + "setExceptionBreakpoints", + DAP_FIELD(exceptionOptions, "exceptionOptions"), + DAP_FIELD(filterOptions, "filterOptions"), + DAP_FIELD(filters, "filters")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionRequest, + "setExpression", + DAP_FIELD(expression, "expression"), + DAP_FIELD(format, "format"), + DAP_FIELD(frameId, "frameId"), + DAP_FIELD(value, "value")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest, + "setFunctionBreakpoints", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest, + "setInstructionBreakpoints", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableRequest, + "setVariable", + DAP_FIELD(format, "format"), + DAP_FIELD(name, "name"), + DAP_FIELD(value, "value"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceRequest, + "source", + DAP_FIELD(source, "source"), + DAP_FIELD(sourceReference, "sourceReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceRequest, + "stackTrace", + DAP_FIELD(format, "format"), + DAP_FIELD(levels, "levels"), + DAP_FIELD(startFrame, "startFrame"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingRequest, + "startDebugging", + DAP_FIELD(configuration, "configuration"), + DAP_FIELD(request, "request")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackRequest, + "stepBack", + DAP_FIELD(granularity, "granularity"), + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInRequest, + "stepIn", + DAP_FIELD(granularity, "granularity"), + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(targetId, "targetId"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsRequest, + "stepInTargets", + DAP_FIELD(frameId, "frameId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutRequest, + "stepOut", + DAP_FIELD(granularity, "granularity"), + DAP_FIELD(singleThread, "singleThread"), + DAP_FIELD(threadId, "threadId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateRequest, + "terminate", + DAP_FIELD(restart, "restart")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsRequest, + "terminateThreads", + DAP_FIELD(threadIds, "threadIds")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsRequest, "threads"); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesRequest, + "variables", + DAP_FIELD(count, "count"), + DAP_FIELD(filter, "filter"), + DAP_FIELD(format, "format"), + DAP_FIELD(start, "start"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryRequest, + "writeMemory", + DAP_FIELD(allowPartial, "allowPartial"), + DAP_FIELD(data, "data"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(offset, "offset")); + +} // namespace dap diff --git a/libraries/cppdap/src/protocol_response.cpp b/libraries/cppdap/src/protocol_response.cpp new file mode 100644 index 000000000..b44cf1b20 --- /dev/null +++ b/libraries/cppdap/src/protocol_response.cpp @@ -0,0 +1,262 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#include "dap/protocol.h" + +namespace dap { + +DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsResponse, + "", + DAP_FIELD(targets, "targets")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueResponse, + "", + DAP_FIELD(allThreadsContinued, + "allThreadsContinued")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoResponse, + "", + DAP_FIELD(accessTypes, "accessTypes"), + DAP_FIELD(canPersist, "canPersist"), + DAP_FIELD(dataId, "dataId"), + DAP_FIELD(description, "description")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleResponse, + "", + DAP_FIELD(instructions, "instructions")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ErrorResponse, "", DAP_FIELD(error, "error")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateResponse, + "", + DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(namedVariables, "namedVariables"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(result, "result"), + DAP_FIELD(type, "type"), + DAP_FIELD(valueLocationReference, + "valueLocationReference"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoResponse, + "", + DAP_FIELD(breakMode, "breakMode"), + DAP_FIELD(description, "description"), + DAP_FIELD(details, "details"), + DAP_FIELD(exceptionId, "exceptionId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsResponse, + "", + DAP_FIELD(targets, "targets")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO( + InitializeResponse, + "", + DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), + DAP_FIELD(breakpointModes, "breakpointModes"), + DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), + DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), + DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), + DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"), + DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"), + DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"), + DAP_FIELD(supportsBreakpointLocationsRequest, + "supportsBreakpointLocationsRequest"), + DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"), + DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"), + DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"), + DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"), + DAP_FIELD(supportsConfigurationDoneRequest, + "supportsConfigurationDoneRequest"), + DAP_FIELD(supportsDataBreakpointBytes, "supportsDataBreakpointBytes"), + DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"), + DAP_FIELD(supportsDelayedStackTraceLoading, + "supportsDelayedStackTraceLoading"), + DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"), + DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"), + DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"), + DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"), + DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"), + DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"), + DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"), + DAP_FIELD(supportsHitConditionalBreakpoints, + "supportsHitConditionalBreakpoints"), + DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"), + DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"), + DAP_FIELD(supportsLogPoints, "supportsLogPoints"), + DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"), + DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"), + DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"), + DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"), + DAP_FIELD(supportsSetExpression, "supportsSetExpression"), + DAP_FIELD(supportsSetVariable, "supportsSetVariable"), + DAP_FIELD(supportsSingleThreadExecutionRequests, + "supportsSingleThreadExecutionRequests"), + DAP_FIELD(supportsStepBack, "supportsStepBack"), + DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"), + DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"), + DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"), + DAP_FIELD(supportsTerminateThreadsRequest, + "supportsTerminateThreadsRequest"), + DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"), + DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesResponse, + "", + DAP_FIELD(sources, "sources")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(LocationsResponse, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(line, "line"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesResponse, + "", + DAP_FIELD(modules, "modules"), + DAP_FIELD(totalModules, "totalModules")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(NextResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryResponse, + "", + DAP_FIELD(address, "address"), + DAP_FIELD(data, "data"), + DAP_FIELD(unreadableBytes, "unreadableBytes")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalResponse, + "", + DAP_FIELD(processId, "processId"), + DAP_FIELD(shellProcessId, "shellProcessId")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesResponse, "", DAP_FIELD(scopes, "scopes")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionResponse, + "", + DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(namedVariables, "namedVariables"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(type, "type"), + DAP_FIELD(value, "value"), + DAP_FIELD(valueLocationReference, + "valueLocationReference"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse, + "", + DAP_FIELD(breakpoints, "breakpoints")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableResponse, + "", + DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(namedVariables, "namedVariables"), + DAP_FIELD(type, "type"), + DAP_FIELD(value, "value"), + DAP_FIELD(valueLocationReference, + "valueLocationReference"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceResponse, + "", + DAP_FIELD(content, "content"), + DAP_FIELD(mimeType, "mimeType")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceResponse, + "", + DAP_FIELD(stackFrames, "stackFrames"), + DAP_FIELD(totalFrames, "totalFrames")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsResponse, + "", + DAP_FIELD(targets, "targets")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsResponse, ""); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsResponse, + "", + DAP_FIELD(threads, "threads")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesResponse, + "", + DAP_FIELD(variables, "variables")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryResponse, + "", + DAP_FIELD(bytesWritten, "bytesWritten"), + DAP_FIELD(offset, "offset")); + +} // namespace dap diff --git a/libraries/cppdap/src/protocol_types.cpp b/libraries/cppdap/src/protocol_types.cpp new file mode 100644 index 000000000..68b726a05 --- /dev/null +++ b/libraries/cppdap/src/protocol_types.cpp @@ -0,0 +1,333 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated with protocol_gen.go -- do not edit this file. +// go run scripts/protocol_gen/protocol_gen.go +// +// DAP version 1.68.0 + +#include "dap/protocol.h" + +namespace dap { + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Checksum, + "", + DAP_FIELD(algorithm, "algorithm"), + DAP_FIELD(checksum, "checksum")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Source, + "", + DAP_FIELD(adapterData, "adapterData"), + DAP_FIELD(checksums, "checksums"), + DAP_FIELD(name, "name"), + DAP_FIELD(origin, "origin"), + DAP_FIELD(path, "path"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(sourceReference, "sourceReference"), + DAP_FIELD(sources, "sources")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(id, "id"), + DAP_FIELD(instructionReference, + "instructionReference"), + DAP_FIELD(line, "line"), + DAP_FIELD(message, "message"), + DAP_FIELD(offset, "offset"), + DAP_FIELD(reason, "reason"), + DAP_FIELD(source, "source"), + DAP_FIELD(verified, "verified")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocation, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(line, "line")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ColumnDescriptor, + "", + DAP_FIELD(attributeName, "attributeName"), + DAP_FIELD(format, "format"), + DAP_FIELD(label, "label"), + DAP_FIELD(type, "type"), + DAP_FIELD(width, "width")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointMode, + "", + DAP_FIELD(appliesTo, "appliesTo"), + DAP_FIELD(description, "description"), + DAP_FIELD(label, "label"), + DAP_FIELD(mode, "mode")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter, + "", + DAP_FIELD(conditionDescription, + "conditionDescription"), + DAP_FIELD(def, "default"), + DAP_FIELD(description, "description"), + DAP_FIELD(filter, "filter"), + DAP_FIELD(label, "label"), + DAP_FIELD(supportsCondition, + "supportsCondition")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO( + Capabilities, + "", + DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), + DAP_FIELD(breakpointModes, "breakpointModes"), + DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), + DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), + DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), + DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"), + DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"), + DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"), + DAP_FIELD(supportsBreakpointLocationsRequest, + "supportsBreakpointLocationsRequest"), + DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"), + DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"), + DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"), + DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"), + DAP_FIELD(supportsConfigurationDoneRequest, + "supportsConfigurationDoneRequest"), + DAP_FIELD(supportsDataBreakpointBytes, "supportsDataBreakpointBytes"), + DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"), + DAP_FIELD(supportsDelayedStackTraceLoading, + "supportsDelayedStackTraceLoading"), + DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"), + DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"), + DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"), + DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"), + DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"), + DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"), + DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"), + DAP_FIELD(supportsHitConditionalBreakpoints, + "supportsHitConditionalBreakpoints"), + DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"), + DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"), + DAP_FIELD(supportsLogPoints, "supportsLogPoints"), + DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"), + DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"), + DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"), + DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"), + DAP_FIELD(supportsSetExpression, "supportsSetExpression"), + DAP_FIELD(supportsSetVariable, "supportsSetVariable"), + DAP_FIELD(supportsSingleThreadExecutionRequests, + "supportsSingleThreadExecutionRequests"), + DAP_FIELD(supportsStepBack, "supportsStepBack"), + DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"), + DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"), + DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"), + DAP_FIELD(supportsTerminateThreadsRequest, + "supportsTerminateThreadsRequest"), + DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"), + DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItem, + "", + DAP_FIELD(detail, "detail"), + DAP_FIELD(label, "label"), + DAP_FIELD(length, "length"), + DAP_FIELD(selectionLength, "selectionLength"), + DAP_FIELD(selectionStart, "selectionStart"), + DAP_FIELD(sortText, "sortText"), + DAP_FIELD(start, "start"), + DAP_FIELD(text, "text"), + DAP_FIELD(type, "type")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembledInstruction, + "", + DAP_FIELD(address, "address"), + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(instruction, "instruction"), + DAP_FIELD(instructionBytes, "instructionBytes"), + DAP_FIELD(line, "line"), + DAP_FIELD(location, "location"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(symbol, "symbol")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Message, + "", + DAP_FIELD(format, "format"), + DAP_FIELD(id, "id"), + DAP_FIELD(sendTelemetry, "sendTelemetry"), + DAP_FIELD(showUser, "showUser"), + DAP_FIELD(url, "url"), + DAP_FIELD(urlLabel, "urlLabel"), + DAP_FIELD(variables, "variables")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablePresentationHint, + "", + DAP_FIELD(attributes, "attributes"), + DAP_FIELD(kind, "kind"), + DAP_FIELD(lazy, "lazy"), + DAP_FIELD(visibility, "visibility")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ValueFormat, "", DAP_FIELD(hex, "hex")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionDetails, + "", + DAP_FIELD(evaluateName, "evaluateName"), + DAP_FIELD(fullTypeName, "fullTypeName"), + DAP_FIELD(innerException, "innerException"), + DAP_FIELD(message, "message"), + DAP_FIELD(stackTrace, "stackTrace"), + DAP_FIELD(typeName, "typeName")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTarget, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(id, "id"), + DAP_FIELD(instructionPointerReference, + "instructionPointerReference"), + DAP_FIELD(label, "label"), + DAP_FIELD(line, "line")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Module, + "", + DAP_FIELD(addressRange, "addressRange"), + DAP_FIELD(dateTimeStamp, "dateTimeStamp"), + DAP_FIELD(id, "id"), + DAP_FIELD(isOptimized, "isOptimized"), + DAP_FIELD(isUserCode, "isUserCode"), + DAP_FIELD(name, "name"), + DAP_FIELD(path, "path"), + DAP_FIELD(symbolFilePath, "symbolFilePath"), + DAP_FIELD(symbolStatus, "symbolStatus"), + DAP_FIELD(version, "version")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Scope, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(expensive, "expensive"), + DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(line, "line"), + DAP_FIELD(name, "name"), + DAP_FIELD(namedVariables, "namedVariables"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(source, "source"), + DAP_FIELD(variablesReference, + "variablesReference")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceBreakpoint, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(condition, "condition"), + DAP_FIELD(hitCondition, "hitCondition"), + DAP_FIELD(line, "line"), + DAP_FIELD(logMessage, "logMessage"), + DAP_FIELD(mode, "mode")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint, + "", + DAP_FIELD(accessType, "accessType"), + DAP_FIELD(condition, "condition"), + DAP_FIELD(dataId, "dataId"), + DAP_FIELD(hitCondition, "hitCondition")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionPathSegment, + "", + DAP_FIELD(names, "names"), + DAP_FIELD(negate, "negate")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions, + "", + DAP_FIELD(breakMode, "breakMode"), + DAP_FIELD(path, "path")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionFilterOptions, + "", + DAP_FIELD(condition, "condition"), + DAP_FIELD(filterId, "filterId"), + DAP_FIELD(mode, "mode")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint, + "", + DAP_FIELD(condition, "condition"), + DAP_FIELD(hitCondition, "hitCondition"), + DAP_FIELD(name, "name")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(InstructionBreakpoint, + "", + DAP_FIELD(condition, "condition"), + DAP_FIELD(hitCondition, "hitCondition"), + DAP_FIELD(instructionReference, + "instructionReference"), + DAP_FIELD(mode, "mode"), + DAP_FIELD(offset, "offset")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame, + "", + DAP_FIELD(canRestart, "canRestart"), + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(id, "id"), + DAP_FIELD(instructionPointerReference, + "instructionPointerReference"), + DAP_FIELD(line, "line"), + DAP_FIELD(moduleId, "moduleId"), + DAP_FIELD(name, "name"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(source, "source")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrameFormat, + "", + DAP_FIELD(includeAll, "includeAll"), + DAP_FIELD(line, "line"), + DAP_FIELD(module, "module"), + DAP_FIELD(parameterNames, "parameterNames"), + DAP_FIELD(parameterTypes, "parameterTypes"), + DAP_FIELD(parameterValues, "parameterValues"), + DAP_FIELD(parameters, "parameters")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTarget, + "", + DAP_FIELD(column, "column"), + DAP_FIELD(endColumn, "endColumn"), + DAP_FIELD(endLine, "endLine"), + DAP_FIELD(id, "id"), + DAP_FIELD(label, "label"), + DAP_FIELD(line, "line")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO(Thread, + "", + DAP_FIELD(id, "id"), + DAP_FIELD(name, "name")); + +DAP_IMPLEMENT_STRUCT_TYPEINFO( + Variable, + "", + DAP_FIELD(declarationLocationReference, "declarationLocationReference"), + DAP_FIELD(evaluateName, "evaluateName"), + DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), + DAP_FIELD(name, "name"), + DAP_FIELD(namedVariables, "namedVariables"), + DAP_FIELD(presentationHint, "presentationHint"), + DAP_FIELD(type, "type"), + DAP_FIELD(value, "value"), + DAP_FIELD(valueLocationReference, "valueLocationReference"), + DAP_FIELD(variablesReference, "variablesReference")); + +} // namespace dap diff --git a/libraries/cppdap/src/rapid_json_serializer.cpp b/libraries/cppdap/src/rapid_json_serializer.cpp new file mode 100644 index 000000000..16ff43f42 --- /dev/null +++ b/libraries/cppdap/src/rapid_json_serializer.cpp @@ -0,0 +1,290 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rapid_json_serializer.h" + +#include "null_json_serializer.h" + +#include +#include + +namespace dap { +namespace json { + +RapidDeserializer::RapidDeserializer(const std::string& str) + : doc(new rapidjson::Document()) { + doc->Parse(str.c_str()); +} + +RapidDeserializer::RapidDeserializer(rapidjson::Value* json) : val(json) {} + +RapidDeserializer::~RapidDeserializer() { + delete doc; +} + +bool RapidDeserializer::deserialize(dap::boolean* v) const { + if (!json()->IsBool()) { + return false; + } + *v = json()->GetBool(); + return true; +} + +bool RapidDeserializer::deserialize(dap::integer* v) const { + if (json()->IsInt()) { + *v = json()->GetInt(); + return true; + } else if (json()->IsUint()) { + *v = static_cast(json()->GetUint()); + return true; + } else if (json()->IsInt64()) { + *v = json()->GetInt64(); + return true; + } else if (json()->IsUint64()) { + *v = static_cast(json()->GetUint64()); + return true; + } + return false; +} + +bool RapidDeserializer::deserialize(dap::number* v) const { + if (!json()->IsNumber()) { + return false; + } + *v = json()->GetDouble(); + return true; +} + +bool RapidDeserializer::deserialize(dap::string* v) const { + if (!json()->IsString()) { + return false; + } + *v = json()->GetString(); + return true; +} + +bool RapidDeserializer::deserialize(dap::object* v) const { + v->reserve(json()->MemberCount()); + for (auto el = json()->MemberBegin(); el != json()->MemberEnd(); el++) { + dap::any el_val; + RapidDeserializer d(&(el->value)); + if (!d.deserialize(&el_val)) { + return false; + } + (*v)[el->name.GetString()] = el_val; + } + return true; +} + +bool RapidDeserializer::deserialize(dap::any* v) const { + if (json()->IsBool()) { + *v = dap::boolean(json()->GetBool()); + } else if (json()->IsDouble()) { + *v = dap::number(json()->GetDouble()); + } else if (json()->IsInt()) { + *v = dap::integer(json()->GetInt()); + } else if (json()->IsString()) { + *v = dap::string(json()->GetString()); + } else if (json()->IsNull()) { + *v = null(); + } else if (json()->IsObject()) { + dap::object obj; + if (!deserialize(&obj)) { + return false; + } + *v = obj; + } else if (json()->IsArray()) { + dap::array arr; + if (!deserialize(&arr)) { + return false; + } + *v = arr; + } else { + return false; + } + return true; +} + +size_t RapidDeserializer::count() const { + return json()->Size(); +} + +bool RapidDeserializer::array( + const std::function& cb) const { + if (!json()->IsArray()) { + return false; + } + for (uint32_t i = 0; i < json()->Size(); i++) { + RapidDeserializer d(&(*json())[i]); + if (!cb(&d)) { + return false; + } + } + return true; +} + +bool RapidDeserializer::field( + const std::string& name, + const std::function& cb) const { + if (!json()->IsObject()) { + return false; + } + auto it = json()->FindMember(name.c_str()); + if (it == json()->MemberEnd()) { + return cb(&NullDeserializer::instance); + } + RapidDeserializer d(&(it->value)); + return cb(&d); +} + +RapidSerializer::RapidSerializer() + : doc(new rapidjson::Document(rapidjson::kObjectType)), + allocator(doc->GetAllocator()) {} + +RapidSerializer::RapidSerializer(rapidjson::Value* json, + rapidjson::Document::AllocatorType& allocator) + : val(json), allocator(allocator) {} + +RapidSerializer::~RapidSerializer() { + delete doc; +} + +std::string RapidSerializer::dump() const { + rapidjson::StringBuffer sb; + rapidjson::PrettyWriter writer(sb); + json()->Accept(writer); + return sb.GetString(); +} + +bool RapidSerializer::serialize(dap::boolean v) { + json()->SetBool(v); + return true; +} + +bool RapidSerializer::serialize(dap::integer v) { + json()->SetInt64(v); + return true; +} + +bool RapidSerializer::serialize(dap::number v) { + json()->SetDouble(v); + return true; +} + +bool RapidSerializer::serialize(const dap::string& v) { + json()->SetString(v.data(), static_cast(v.length()), allocator); + return true; +} + +bool RapidSerializer::serialize(const dap::object& v) { + if (!json()->IsObject()) { + json()->SetObject(); + } + for (auto& it : v) { + if (!json()->HasMember(it.first.c_str())) { + rapidjson::Value name_value{it.first.c_str(), allocator}; + json()->AddMember(name_value, rapidjson::Value(), allocator); + } + rapidjson::Value& member = (*json())[it.first.c_str()]; + RapidSerializer s(&member, allocator); + if (!s.serialize(it.second)) { + return false; + } + } + return true; +} + +bool RapidSerializer::serialize(const dap::any& v) { + if (v.is()) { + json()->SetBool((bool)v.get()); + } else if (v.is()) { + json()->SetInt64(v.get()); + } else if (v.is()) { + json()->SetDouble((double)v.get()); + } else if (v.is()) { + auto s = v.get(); + json()->SetString(s.data(), static_cast(s.length()), allocator); + } else if (v.is()) { + // reachable if dap::object nested is inside other dap::object + return serialize(v.get()); + } else if (v.is()) { + } else { + // reachable if array or custom serialized type is nested inside other + // dap::object + auto type = get_any_type(v); + auto value = get_any_val(v); + if (type && value) { + return type->serialize(this, value); + } + return false; + } + + return true; +} + +bool RapidSerializer::array(size_t count, + const std::function& cb) { + if (!json()->IsArray()) { + json()->SetArray(); + } + + while (count > json()->Size()) { + json()->PushBack(rapidjson::Value(), allocator); + } + + for (uint32_t i = 0; i < count; i++) { + RapidSerializer s(&(*json())[i], allocator); + if (!cb(&s)) { + return false; + } + } + return true; +} + +bool RapidSerializer::object( + const std::function& cb) { + struct FS : public FieldSerializer { + rapidjson::Value* const json; + rapidjson::Document::AllocatorType& allocator; + + FS(rapidjson::Value* json, rapidjson::Document::AllocatorType& allocator) + : json(json), allocator(allocator) {} + bool field(const std::string& name, const SerializeFunc& cb) override { + if (!json->HasMember(name.c_str())) { + rapidjson::Value name_value{name.c_str(), allocator}; + json->AddMember(name_value, rapidjson::Value(), allocator); + } + rapidjson::Value& member = (*json)[name.c_str()]; + RapidSerializer s(&member, allocator); + auto res = cb(&s); + if (s.removed) { + json->RemoveMember(name.c_str()); + } + return res; + } + }; + + if (!json()->IsObject()) { + json()->SetObject(); + } + FS fs{json(), allocator}; + return cb(&fs); +} + +void RapidSerializer::remove() { + removed = true; +} + +} // namespace json +} // namespace dap diff --git a/libraries/cppdap/src/rapid_json_serializer.h b/libraries/cppdap/src/rapid_json_serializer.h new file mode 100644 index 000000000..6e8338413 --- /dev/null +++ b/libraries/cppdap/src/rapid_json_serializer.h @@ -0,0 +1,138 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_rapid_json_serializer_h +#define dap_rapid_json_serializer_h + +#include "dap/protocol.h" +#include "dap/serialization.h" +#include "dap/types.h" + +#include + +namespace dap { +namespace json { + +struct RapidDeserializer : public dap::Deserializer { + explicit RapidDeserializer(const std::string&); + ~RapidDeserializer(); + + // dap::Deserializer compliance + bool deserialize(boolean* v) const override; + bool deserialize(integer* v) const override; + bool deserialize(number* v) const override; + bool deserialize(string* v) const override; + bool deserialize(object* v) const override; + bool deserialize(any* v) const override; + size_t count() const override; + bool array(const std::function&) const override; + bool field(const std::string& name, + const std::function&) const override; + + // Unhide base overloads + template + inline bool field(const std::string& name, T* v) { + return dap::Deserializer::field(name, v); + } + + template ::has_custom_serialization>> + inline bool deserialize(T* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::array* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::optional* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool deserialize(dap::variant* v) const { + return dap::Deserializer::deserialize(v); + } + + template + inline bool field(const std::string& name, T* v) const { + return dap::Deserializer::deserialize(name, v); + } + + inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; } + + private: + RapidDeserializer(rapidjson::Value*); + rapidjson::Document* const doc = nullptr; + rapidjson::Value* const val = nullptr; +}; + +struct RapidSerializer : public dap::Serializer { + RapidSerializer(); + ~RapidSerializer(); + + std::string dump() const; + + // dap::Serializer compliance + bool serialize(boolean v) override; + bool serialize(integer v) override; + bool serialize(number v) override; + bool serialize(const string& v) override; + bool serialize(const dap::object& v) override; + bool serialize(const any& v) override; + bool array(size_t count, + const std::function&) override; + bool object(const std::function&) override; + void remove() override; + + // Unhide base overloads + template ::has_custom_serialization>> + inline bool serialize(const T& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::array& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::optional& v) { + return dap::Serializer::serialize(v); + } + + template + inline bool serialize(const dap::variant& v) { + return dap::Serializer::serialize(v); + } + + inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } + + inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; } + + private: + RapidSerializer(rapidjson::Value*, rapidjson::Document::AllocatorType&); + rapidjson::Document* const doc = nullptr; + rapidjson::Value* const val = nullptr; + rapidjson::Document::AllocatorType& allocator; + bool removed = false; +}; + +} // namespace json +} // namespace dap + +#endif // dap_rapid_json_serializer_h diff --git a/libraries/cppdap/src/rwmutex.h b/libraries/cppdap/src/rwmutex.h new file mode 100644 index 000000000..9e858913f --- /dev/null +++ b/libraries/cppdap/src/rwmutex.h @@ -0,0 +1,172 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_rwmutex_h +#define dap_rwmutex_h + +#include +#include + +namespace dap { + +//////////////////////////////////////////////////////////////////////////////// +// RWMutex +//////////////////////////////////////////////////////////////////////////////// + +// A RWMutex is a reader/writer mutual exclusion lock. +// The lock can be held by an arbitrary number of readers or a single writer. +// Also known as a shared mutex. +class RWMutex { + public: + inline RWMutex() = default; + + // lockReader() locks the mutex for reading. + // Multiple read locks can be held while there are no writer locks. + inline void lockReader(); + + // unlockReader() unlocks the mutex for reading. + inline void unlockReader(); + + // lockWriter() locks the mutex for writing. + // If the lock is already locked for reading or writing, lockWriter blocks + // until the lock is available. + inline void lockWriter(); + + // unlockWriter() unlocks the mutex for writing. + inline void unlockWriter(); + + private: + RWMutex(const RWMutex&) = delete; + RWMutex& operator=(const RWMutex&) = delete; + + int readLocks = 0; + int pendingWriteLocks = 0; + std::mutex mutex; + std::condition_variable cv; +}; + +void RWMutex::lockReader() { + std::unique_lock lock(mutex); + readLocks++; +} + +void RWMutex::unlockReader() { + std::unique_lock lock(mutex); + readLocks--; + if (readLocks == 0 && pendingWriteLocks > 0) { + cv.notify_one(); + } +} + +void RWMutex::lockWriter() { + std::unique_lock lock(mutex); + if (readLocks > 0) { + pendingWriteLocks++; + cv.wait(lock, [&] { return readLocks == 0; }); + pendingWriteLocks--; + } + lock.release(); // Keep lock held +} + +void RWMutex::unlockWriter() { + if (pendingWriteLocks > 0) { + cv.notify_one(); + } + mutex.unlock(); +} + +//////////////////////////////////////////////////////////////////////////////// +// RLock +//////////////////////////////////////////////////////////////////////////////// + +// RLock is a RAII read lock helper for a RWMutex. +class RLock { + public: + inline RLock(RWMutex& mutex); + inline ~RLock(); + + inline RLock(RLock&&); + inline RLock& operator=(RLock&&); + + private: + RLock(const RLock&) = delete; + RLock& operator=(const RLock&) = delete; + + RWMutex* m; +}; + +RLock::RLock(RWMutex& mutex) : m(&mutex) { + m->lockReader(); +} + +RLock::~RLock() { + if (m != nullptr) { + m->unlockReader(); + } +} + +RLock::RLock(RLock&& other) { + m = other.m; + other.m = nullptr; +} + +RLock& RLock::operator=(RLock&& other) { + m = other.m; + other.m = nullptr; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// WLock +//////////////////////////////////////////////////////////////////////////////// + +// WLock is a RAII write lock helper for a RWMutex. +class WLock { + public: + inline WLock(RWMutex& mutex); + inline ~WLock(); + + inline WLock(WLock&&); + inline WLock& operator=(WLock&&); + + private: + WLock(const WLock&) = delete; + WLock& operator=(const WLock&) = delete; + + RWMutex* m; +}; + +WLock::WLock(RWMutex& mutex) : m(&mutex) { + m->lockWriter(); +} + +WLock::~WLock() { + if (m != nullptr) { + m->unlockWriter(); + } +} + +WLock::WLock(WLock&& other) { + m = other.m; + other.m = nullptr; +} + +WLock& WLock::operator=(WLock&& other) { + m = other.m; + other.m = nullptr; + return *this; +} +} // namespace dap + +#endif diff --git a/libraries/cppdap/src/rwmutex_test.cpp b/libraries/cppdap/src/rwmutex_test.cpp new file mode 100644 index 000000000..944ed7752 --- /dev/null +++ b/libraries/cppdap/src/rwmutex_test.cpp @@ -0,0 +1,113 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rwmutex.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include + +namespace { +constexpr const size_t NumThreads = 8; +} + +// Check that WLock behaves like regular mutex. +TEST(RWMutex, WLock) { + dap::RWMutex rwmutex; + int counter = 0; + + std::vector threads; + for (size_t i = 0; i < NumThreads; i++) { + threads.emplace_back([&] { + for (int j = 0; j < 1000; j++) { + dap::WLock lock(rwmutex); + counter++; + EXPECT_EQ(counter, 1); + counter--; + } + }); + } + + for (auto& thread : threads) { + thread.join(); + } + + EXPECT_EQ(counter, 0); +} + +TEST(RWMutex, NoRLockWithWLock) { + dap::RWMutex rwmutex; + + std::vector threads; + std::array counters = {}; + + { // With WLock held... + dap::WLock wlock(rwmutex); + + for (size_t i = 0; i < counters.size(); i++) { + int* counter = &counters[i]; + threads.emplace_back([&rwmutex, counter] { + dap::RLock lock(rwmutex); + for (int j = 0; j < 1000; j++) { + (*counter)++; + } + }); + } + + // RLocks should block + for (int counter : counters) { + EXPECT_EQ(counter, 0); + } + } + + for (auto& thread : threads) { + thread.join(); + } + + for (int counter : counters) { + EXPECT_EQ(counter, 1000); + } +} + +TEST(RWMutex, NoWLockWithRLock) { + dap::RWMutex rwmutex; + + std::vector threads; + size_t counter = 0; + + { // With RLocks held... + dap::RLock rlockA(rwmutex); + dap::RLock rlockB(rwmutex); + dap::RLock rlockC(rwmutex); + + for (size_t i = 0; i < NumThreads; i++) { + threads.emplace_back(std::thread([&] { + dap::WLock lock(rwmutex); + counter++; + })); + } + + // ... WLocks should block + EXPECT_EQ(counter, 0U); + } + + for (auto& thread : threads) { + thread.join(); + } + + EXPECT_EQ(counter, NumThreads); +} diff --git a/libraries/cppdap/src/session.cpp b/libraries/cppdap/src/session.cpp new file mode 100644 index 000000000..ffc77751f --- /dev/null +++ b/libraries/cppdap/src/session.cpp @@ -0,0 +1,521 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "content_stream.h" + +#include "dap/any.h" +#include "dap/session.h" + +#include "chan.h" +#include "json_serializer.h" +#include "socket.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class Impl : public dap::Session { + public: + void setOnInvalidData(dap::OnInvalidData onInvalidData_) override { + this->onInvalidData = onInvalidData_; + } + + void onError(const ErrorHandler& handler) override { handlers.put(handler); } + + void registerHandler(const dap::TypeInfo* typeinfo, + const GenericRequestHandler& handler) override { + handlers.put(typeinfo, handler); + } + + void registerHandler(const dap::TypeInfo* typeinfo, + const GenericEventHandler& handler) override { + handlers.put(typeinfo, handler); + } + + void registerHandler(const dap::TypeInfo* typeinfo, + const GenericResponseSentHandler& handler) override { + handlers.put(typeinfo, handler); + } + + std::function getPayload() override { + auto request = reader.read(); + if (request.size() > 0) { + if (auto payload = processMessage(request)) { + return payload; + } + } + return {}; + } + + void connect(const std::shared_ptr& r, + const std::shared_ptr& w) override { + if (isBound.exchange(true)) { + handlers.error("Session::connect called twice"); + return; + } + + reader = dap::ContentReader(r, this->onInvalidData); + writer = dap::ContentWriter(w); + } + + void startProcessingMessages( + const ClosedHandler& onClose /* = {} */) override { + if (isProcessingMessages.exchange(true)) { + handlers.error("Session::startProcessingMessages() called twice"); + return; + } + recvThread = std::thread([this, onClose] { + while (reader.isOpen()) { + if (auto payload = getPayload()) { + inbox.put(std::move(payload)); + } + } + if (onClose) { + onClose(); + } + }); + + dispatchThread = std::thread([this] { + while (auto payload = inbox.take()) { + payload.value()(); + } + }); + } + + bool send(const dap::TypeInfo* requestTypeInfo, + const dap::TypeInfo* responseTypeInfo, + const void* request, + const GenericResponseHandler& responseHandler) override { + int seq = nextSeq++; + + handlers.put(seq, responseTypeInfo, responseHandler); + + dap::json::Serializer s; + if (!s.object([&](dap::FieldSerializer* fs) { + return fs->field("seq", dap::integer(seq)) && + fs->field("type", "request") && + fs->field("command", requestTypeInfo->name()) && + fs->field("arguments", [&](dap::Serializer* s) { + return requestTypeInfo->serialize(s, request); + }); + })) { + return false; + } + return send(s.dump()); + } + + bool send(const dap::TypeInfo* typeinfo, const void* event) override { + dap::json::Serializer s; + if (!s.object([&](dap::FieldSerializer* fs) { + return fs->field("seq", dap::integer(nextSeq++)) && + fs->field("type", "event") && + fs->field("event", typeinfo->name()) && + fs->field("body", [&](dap::Serializer* s) { + return typeinfo->serialize(s, event); + }); + })) { + return false; + } + return send(s.dump()); + } + + ~Impl() override { + inbox.close(); + reader.close(); + writer.close(); + if (recvThread.joinable()) { + recvThread.join(); + } + if (dispatchThread.joinable()) { + dispatchThread.join(); + } + } + + private: + using Payload = std::function; + + class EventHandlers { + public: + void put(const ErrorHandler& handler) { + std::unique_lock lock(errorMutex); + errorHandler = handler; + } + + void error(const char* format, ...) { + va_list vararg; + va_start(vararg, format); + std::unique_lock lock(errorMutex); + errorLocked(format, vararg); + va_end(vararg); + } + + std::pair request( + const std::string& name) { + std::unique_lock lock(requestMutex); + auto it = requestMap.find(name); + return (it != requestMap.end()) ? it->second : decltype(it->second){}; + } + + void put(const dap::TypeInfo* typeinfo, + const GenericRequestHandler& handler) { + std::unique_lock lock(requestMutex); + auto added = + requestMap + .emplace(typeinfo->name(), std::make_pair(typeinfo, handler)) + .second; + if (!added) { + errorfLocked("Request handler for '%s' already registered", + typeinfo->name().c_str()); + } + } + + std::pair response( + int64_t seq) { + std::unique_lock lock(responseMutex); + auto responseIt = responseMap.find(seq); + if (responseIt == responseMap.end()) { + errorfLocked("Unknown response with sequence %d", seq); + return {}; + } + auto out = std::move(responseIt->second); + responseMap.erase(seq); + return out; + } + + void put(int seq, + const dap::TypeInfo* typeinfo, + const GenericResponseHandler& handler) { + std::unique_lock lock(responseMutex); + auto added = + responseMap.emplace(seq, std::make_pair(typeinfo, handler)).second; + if (!added) { + errorfLocked("Response handler for sequence %d already registered", + seq); + } + } + + std::pair event( + const std::string& name) { + std::unique_lock lock(eventMutex); + auto it = eventMap.find(name); + return (it != eventMap.end()) ? it->second : decltype(it->second){}; + } + + void put(const dap::TypeInfo* typeinfo, + const GenericEventHandler& handler) { + std::unique_lock lock(eventMutex); + auto added = + eventMap.emplace(typeinfo->name(), std::make_pair(typeinfo, handler)) + .second; + if (!added) { + errorfLocked("Event handler for '%s' already registered", + typeinfo->name().c_str()); + } + } + + GenericResponseSentHandler responseSent(const dap::TypeInfo* typeinfo) { + std::unique_lock lock(responseSentMutex); + auto it = responseSentMap.find(typeinfo); + return (it != responseSentMap.end()) ? it->second + : decltype(it->second){}; + } + + void put(const dap::TypeInfo* typeinfo, + const GenericResponseSentHandler& handler) { + std::unique_lock lock(responseSentMutex); + auto added = responseSentMap.emplace(typeinfo, handler).second; + if (!added) { + errorfLocked("Response sent handler for '%s' already registered", + typeinfo->name().c_str()); + } + } + + private: + void errorfLocked(const char* format, ...) { + va_list vararg; + va_start(vararg, format); + errorLocked(format, vararg); + va_end(vararg); + } + + void errorLocked(const char* format, va_list args) { + char buf[2048]; + vsnprintf(buf, sizeof(buf), format, args); + if (errorHandler) { + errorHandler(buf); + } + } + + std::mutex errorMutex; + ErrorHandler errorHandler; + + std::mutex requestMutex; + std::unordered_map> + requestMap; + + std::mutex responseMutex; + std::unordered_map> + responseMap; + + std::mutex eventMutex; + std::unordered_map> + eventMap; + + std::mutex responseSentMutex; + std::unordered_map + responseSentMap; + }; // EventHandlers + + Payload processMessage(const std::string& str) { + auto d = dap::json::Deserializer(str); + dap::string type; + if (!d.field("type", &type)) { + handlers.error("Message missing string 'type' field"); + return {}; + } + + dap::integer sequence = 0; + if (!d.field("seq", &sequence)) { + handlers.error("Message missing number 'seq' field"); + return {}; + } + + if (type == "request") { + return processRequest(&d, sequence); + } else if (type == "event") { + return processEvent(&d); + } else if (type == "response") { + processResponse(&d); + return {}; + } else { + handlers.error("Unknown message type '%s'", type.c_str()); + } + + return {}; + } + + Payload processRequest(dap::json::Deserializer* d, dap::integer sequence) { + dap::string command; + if (!d->field("command", &command)) { + handlers.error("Request missing string 'command' field"); + return {}; + } + + const dap::TypeInfo* typeinfo; + GenericRequestHandler handler; + std::tie(typeinfo, handler) = handlers.request(command); + if (!typeinfo) { + handlers.error("No request handler registered for command '%s'", + command.c_str()); + return {}; + } + + auto data = new uint8_t[typeinfo->size()]; + typeinfo->construct(data); + + if (!d->field("arguments", [&](dap::Deserializer* d) { + return typeinfo->deserialize(d, data); + })) { + handlers.error("Failed to deserialize request"); + typeinfo->destruct(data); + delete[] data; + return {}; + } + + return [=] { + handler( + data, + [=](const dap::TypeInfo* typeinfo, const void* data) { + // onSuccess + dap::json::Serializer s; + s.object([&](dap::FieldSerializer* fs) { + return fs->field("seq", dap::integer(nextSeq++)) && + fs->field("type", "response") && + fs->field("request_seq", sequence) && + fs->field("success", dap::boolean(true)) && + fs->field("command", command) && + fs->field("body", [&](dap::Serializer* s) { + return typeinfo->serialize(s, data); + }); + }); + send(s.dump()); + + if (auto handler = handlers.responseSent(typeinfo)) { + handler(data, nullptr); + } + }, + [=](const dap::TypeInfo* typeinfo, const dap::Error& error) { + // onError + dap::json::Serializer s; + s.object([&](dap::FieldSerializer* fs) { + return fs->field("seq", dap::integer(nextSeq++)) && + fs->field("type", "response") && + fs->field("request_seq", sequence) && + fs->field("success", dap::boolean(false)) && + fs->field("command", command) && + fs->field("message", error.message); + }); + send(s.dump()); + + if (auto handler = handlers.responseSent(typeinfo)) { + handler(nullptr, &error); + } + }); + typeinfo->destruct(data); + delete[] data; + }; + } + + Payload processEvent(dap::json::Deserializer* d) { + dap::string event; + if (!d->field("event", &event)) { + handlers.error("Event missing string 'event' field"); + return {}; + } + + const dap::TypeInfo* typeinfo; + GenericEventHandler handler; + std::tie(typeinfo, handler) = handlers.event(event); + if (!typeinfo) { + handlers.error("No event handler registered for event '%s'", + event.c_str()); + return {}; + } + + auto data = new uint8_t[typeinfo->size()]; + typeinfo->construct(data); + + // "body" is an optional field for some events, such as "Terminated Event". + bool body_ok = true; + d->field("body", [&](dap::Deserializer* d) { + if (!typeinfo->deserialize(d, data)) { + body_ok = false; + } + return true; + }); + + if (!body_ok) { + handlers.error("Failed to deserialize event '%s' body", event.c_str()); + typeinfo->destruct(data); + delete[] data; + return {}; + } + + return [=] { + handler(data); + typeinfo->destruct(data); + delete[] data; + }; + } + + void processResponse(const dap::Deserializer* d) { + dap::integer requestSeq = 0; + if (!d->field("request_seq", &requestSeq)) { + handlers.error("Response missing int 'request_seq' field"); + return; + } + + const dap::TypeInfo* typeinfo; + GenericResponseHandler handler; + std::tie(typeinfo, handler) = handlers.response(requestSeq); + if (!typeinfo) { + handlers.error("Unknown response with sequence %d", requestSeq); + return; + } + + dap::boolean success = false; + if (!d->field("success", &success)) { + handlers.error("Response missing int 'success' field"); + return; + } + + if (success) { + auto data = std::unique_ptr(new uint8_t[typeinfo->size()]); + typeinfo->construct(data.get()); + + // "body" field in Response is an optional field. + d->field("body", [&](const dap::Deserializer* d) { + return typeinfo->deserialize(d, data.get()); + }); + + handler(data.get(), nullptr); + typeinfo->destruct(data.get()); + } else { + std::string message; + if (!d->field("message", &message)) { + handlers.error("Failed to deserialize message"); + return; + } + auto error = dap::Error("%s", message.c_str()); + handler(nullptr, &error); + } + } + + bool send(const std::string& s) { + std::unique_lock lock(sendMutex); + if (!writer.isOpen()) { + handlers.error("Send failed as the writer is closed"); + return false; + } + return writer.write(s); + } + + std::atomic isBound = {false}; + std::atomic isProcessingMessages = {false}; + dap::ContentReader reader; + dap::ContentWriter writer; + + std::atomic shutdown = {false}; + EventHandlers handlers; + std::thread recvThread; + std::thread dispatchThread; + dap::Chan inbox; + std::atomic nextSeq = {1}; + std::mutex sendMutex; + dap::OnInvalidData onInvalidData = dap::kIgnore; +}; + +} // anonymous namespace + +namespace dap { + +Error::Error(const std::string& message) : message(message) {} + +Error::Error(const char* msg, ...) { + char buf[2048]; + va_list vararg; + va_start(vararg, msg); + vsnprintf(buf, sizeof(buf), msg, vararg); + va_end(vararg); + message = buf; +} + +Session::~Session() = default; + +std::unique_ptr Session::create() { + return std::unique_ptr(new Impl()); +} + +} // namespace dap diff --git a/libraries/cppdap/src/session_test.cpp b/libraries/cppdap/src/session_test.cpp new file mode 100644 index 000000000..361152e74 --- /dev/null +++ b/libraries/cppdap/src/session_test.cpp @@ -0,0 +1,625 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/session.h" +#include "dap/io.h" +#include "dap/protocol.h" + +#include "chan.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include + +namespace dap { + +struct TestResponse : public Response { + boolean b; + integer i; + number n; + array a; + object o; + string s; + optional o1; + optional o2; +}; + +DAP_STRUCT_TYPEINFO(TestResponse, + "test-response", + DAP_FIELD(b, "res_b"), + DAP_FIELD(i, "res_i"), + DAP_FIELD(n, "res_n"), + DAP_FIELD(a, "res_a"), + DAP_FIELD(o, "res_o"), + DAP_FIELD(s, "res_s"), + DAP_FIELD(o1, "res_o1"), + DAP_FIELD(o2, "res_o2")); + +struct TestRequest : public Request { + using Response = TestResponse; + + boolean b; + integer i; + number n; + array a; + object o; + string s; + optional o1; + optional o2; +}; + +DAP_STRUCT_TYPEINFO(TestRequest, + "test-request", + DAP_FIELD(b, "req_b"), + DAP_FIELD(i, "req_i"), + DAP_FIELD(n, "req_n"), + DAP_FIELD(a, "req_a"), + DAP_FIELD(o, "req_o"), + DAP_FIELD(s, "req_s"), + DAP_FIELD(o1, "req_o1"), + DAP_FIELD(o2, "req_o2")); + +struct TestEvent : public Event { + boolean b; + integer i; + number n; + array a; + object o; + string s; + optional o1; + optional o2; +}; + +DAP_STRUCT_TYPEINFO(TestEvent, + "test-event", + DAP_FIELD(b, "evt_b"), + DAP_FIELD(i, "evt_i"), + DAP_FIELD(n, "evt_n"), + DAP_FIELD(a, "evt_a"), + DAP_FIELD(o, "evt_o"), + DAP_FIELD(s, "evt_s"), + DAP_FIELD(o1, "evt_o1"), + DAP_FIELD(o2, "evt_o2")); + +}; // namespace dap + +namespace { + +dap::TestRequest createRequest() { + dap::TestRequest request; + request.b = false; + request.i = 72; + request.n = 9.87; + request.a = {2, 5, 7, 8}; + request.o = { + std::make_pair("a", dap::integer(1)), + std::make_pair("b", dap::number(2)), + std::make_pair("c", dap::string("3")), + }; + request.s = "request"; + request.o2 = 42; + return request; +} + +dap::TestResponse createResponse() { + dap::TestResponse response; + response.b = true; + response.i = 99; + response.n = 123.456; + response.a = {5, 4, 3, 2, 1}; + response.o = { + std::make_pair("one", dap::integer(1)), + std::make_pair("two", dap::number(2)), + std::make_pair("three", dap::string("3")), + }; + response.s = "ROGER"; + response.o1 = 50; + return response; +} + +dap::TestEvent createEvent() { + dap::TestEvent event; + event.b = false; + event.i = 72; + event.n = 9.87; + event.a = {2, 5, 7, 8}; + event.o = { + std::make_pair("a", dap::integer(1)), + std::make_pair("b", dap::number(2)), + std::make_pair("c", dap::string("3")), + }; + event.s = "event"; + event.o2 = 42; + return event; +} + +} // anonymous namespace + +class SessionTest : public testing::Test { + public: + void bind() { + auto client2server = dap::pipe(); + auto server2client = dap::pipe(); + client->bind(server2client, client2server); + server->bind(client2server, server2client); + } + + std::unique_ptr client = dap::Session::create(); + std::unique_ptr server = dap::Session::create(); +}; + +TEST_F(SessionTest, Request) { + dap::TestRequest received; + server->registerHandler([&](const dap::TestRequest& req) { + received = req; + return createResponse(); + }); + + bind(); + + auto request = createRequest(); + client->send(request).get(); + + // Check request was received correctly. + ASSERT_EQ(received.b, request.b); + ASSERT_EQ(received.i, request.i); + ASSERT_EQ(received.n, request.n); + ASSERT_EQ(received.a, request.a); + ASSERT_EQ(received.o.size(), 3U); + ASSERT_EQ(received.o["a"].get(), + request.o["a"].get()); + ASSERT_EQ(received.o["b"].get(), + request.o["b"].get()); + ASSERT_EQ(received.o["c"].get(), + request.o["c"].get()); + ASSERT_EQ(received.s, request.s); + ASSERT_EQ(received.o1, request.o1); + ASSERT_EQ(received.o2, request.o2); +} + +TEST_F(SessionTest, RequestResponseSuccess) { + server->registerHandler( + [&](const dap::TestRequest&) { return createResponse(); }); + + bind(); + + auto request = createRequest(); + auto response = client->send(request); + + auto got = response.get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.b, dap::boolean(true)); + ASSERT_EQ(got.response.i, dap::integer(99)); + ASSERT_EQ(got.response.n, dap::number(123.456)); + ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); + ASSERT_EQ(got.response.o.size(), 3U); + ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); + ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); + ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); + ASSERT_EQ(got.response.s, "ROGER"); + ASSERT_EQ(got.response.o1, dap::optional(50)); + ASSERT_FALSE(got.response.o2.has_value()); +} + +TEST_F(SessionTest, BreakPointRequestResponseSuccess) { + server->registerHandler([&](const dap::SetBreakpointsRequest&) { + dap::SetBreakpointsResponse response; + dap::Breakpoint bp; + bp.line = 2; + response.breakpoints.emplace_back(std::move(bp)); + return response; + }); + + bind(); + + auto got = client->send(dap::SetBreakpointsRequest{}).get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.breakpoints.size(), 1U); +} + +TEST_F(SessionTest, RequestResponseOrError) { + server->registerHandler( + [&](const dap::TestRequest&) -> dap::ResponseOrError { + return dap::Error("Oh noes!"); + }); + + bind(); + + auto response = client->send(createRequest()); + + auto got = response.get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, true); + ASSERT_EQ(got.error.message, "Oh noes!"); +} + +TEST_F(SessionTest, RequestResponseError) { + server->registerHandler( + [&](const dap::TestRequest&) { return dap::Error("Oh noes!"); }); + + bind(); + + auto response = client->send(createRequest()); + + auto got = response.get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, true); + ASSERT_EQ(got.error.message, "Oh noes!"); +} + +TEST_F(SessionTest, RequestCallbackResponse) { + using ResponseCallback = std::function; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { + dap::SetBreakpointsResponse response; + dap::Breakpoint bp; + bp.line = 2; + response.breakpoints.emplace_back(std::move(bp)); + callback(response); + }); + + bind(); + + auto got = client->send(dap::SetBreakpointsRequest{}).get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.breakpoints.size(), 1U); +} + +TEST_F(SessionTest, RequestCallbackResponseOrError) { + using ResponseCallback = + std::function)>; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { + dap::SetBreakpointsResponse response; + dap::Breakpoint bp; + bp.line = 2; + response.breakpoints.emplace_back(std::move(bp)); + callback(response); + }); + + bind(); + + auto got = client->send(dap::SetBreakpointsRequest{}).get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.breakpoints.size(), 1U); +} + +TEST_F(SessionTest, RequestCallbackError) { + using ResponseCallback = + std::function)>; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { + callback(dap::Error("Oh noes!")); + }); + + bind(); + + auto got = client->send(dap::SetBreakpointsRequest{}).get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, true); + ASSERT_EQ(got.error.message, "Oh noes!"); +} + +TEST_F(SessionTest, RequestCallbackSuccessAfterReturn) { + using ResponseCallback = + std::function)>; + + ResponseCallback callback; + std::mutex mutex; + std::condition_variable cv; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { + std::unique_lock lock(mutex); + callback = cb; + cv.notify_all(); + }); + + bind(); + + auto future = client->send(dap::SetBreakpointsRequest{}); + + { + dap::SetBreakpointsResponse response; + dap::Breakpoint bp; + bp.line = 2; + response.breakpoints.emplace_back(std::move(bp)); + + // Wait for the handler to be called. + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return static_cast(callback); }); + + // Issue the callback + callback(response); + } + + auto got = future.get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.breakpoints.size(), 1U); +} + +TEST_F(SessionTest, RequestCallbackErrorAfterReturn) { + using ResponseCallback = + std::function)>; + + ResponseCallback callback; + std::mutex mutex; + std::condition_variable cv; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { + std::unique_lock lock(mutex); + callback = cb; + cv.notify_all(); + }); + + bind(); + + auto future = client->send(dap::SetBreakpointsRequest{}); + + { + // Wait for the handler to be called. + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return static_cast(callback); }); + + // Issue the callback + callback(dap::Error("Oh noes!")); + } + + auto got = future.get(); + + // Check response was received correctly. + ASSERT_EQ(got.error, true); + ASSERT_EQ(got.error.message, "Oh noes!"); +} + +TEST_F(SessionTest, ResponseSentHandlerSuccess) { + const auto response = createResponse(); + + dap::Chan> chan; + server->registerHandler([&](const dap::TestRequest&) { return response; }); + server->registerSentHandler( + [&](const dap::ResponseOrError r) { chan.put(r); }); + + bind(); + + client->send(createRequest()); + + auto got = chan.take().value(); + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.b, dap::boolean(true)); + ASSERT_EQ(got.response.i, dap::integer(99)); + ASSERT_EQ(got.response.n, dap::number(123.456)); + ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); + ASSERT_EQ(got.response.o.size(), 3U); + ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); + ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); + ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); + ASSERT_EQ(got.response.s, "ROGER"); + ASSERT_EQ(got.response.o1, dap::optional(50)); + ASSERT_FALSE(got.response.o2.has_value()); +} + +TEST_F(SessionTest, ResponseSentHandlerError) { + dap::Chan> chan; + server->registerHandler( + [&](const dap::TestRequest&) { return dap::Error("Oh noes!"); }); + server->registerSentHandler( + [&](const dap::ResponseOrError r) { chan.put(r); }); + + bind(); + + client->send(createRequest()); + + auto got = chan.take().value(); + ASSERT_EQ(got.error, true); + ASSERT_EQ(got.error.message, "Oh noes!"); +} + +TEST_F(SessionTest, Event) { + dap::Chan received; + server->registerHandler([&](const dap::TestEvent& e) { received.put(e); }); + + bind(); + + auto event = createEvent(); + client->send(event); + + // Check event was received correctly. + auto got = received.take().value(); + + ASSERT_EQ(got.b, event.b); + ASSERT_EQ(got.i, event.i); + ASSERT_EQ(got.n, event.n); + ASSERT_EQ(got.a, event.a); + ASSERT_EQ(got.o.size(), 3U); + ASSERT_EQ(got.o["a"].get(), event.o["a"].get()); + ASSERT_EQ(got.o["b"].get(), event.o["b"].get()); + ASSERT_EQ(got.o["c"].get(), event.o["c"].get()); + ASSERT_EQ(got.s, event.s); + ASSERT_EQ(got.o1, event.o1); + ASSERT_EQ(got.o2, event.o2); +} + +TEST_F(SessionTest, RegisterHandlerFunction) { + struct S { + static dap::TestResponse requestA(const dap::TestRequest&) { return {}; } + static dap::Error requestB(const dap::TestRequest&) { return {}; } + static dap::ResponseOrError requestC( + const dap::TestRequest&) { + return dap::Error(); + } + static void event(const dap::TestEvent&) {} + static void sent(const dap::ResponseOrError&) {} + }; + client->registerHandler(&S::requestA); + client->registerHandler(&S::requestB); + client->registerHandler(&S::requestC); + client->registerHandler(&S::event); + client->registerSentHandler(&S::sent); +} + +TEST_F(SessionTest, SendRequestNoBind) { + bool errored = false; + client->onError([&](const std::string&) { errored = true; }); + auto res = client->send(createRequest()).get(); + ASSERT_TRUE(errored); + ASSERT_TRUE(res.error); +} + +TEST_F(SessionTest, SendEventNoBind) { + bool errored = false; + client->onError([&](const std::string&) { errored = true; }); + client->send(createEvent()); + ASSERT_TRUE(errored); +} + +TEST_F(SessionTest, SingleThread) { + server->registerHandler( + [&](const dap::TestRequest&) { return createResponse(); }); + + // Explicitly connect and process request on this test thread instead of + // calling bind() which inturn starts processing messages immediately on a new + // thread. + auto client2server = dap::pipe(); + auto server2client = dap::pipe(); + client->connect(server2client, client2server); + server->connect(client2server, server2client); + + auto request = createRequest(); + auto response = client->send(request); + + // Process request and response on this thread + if (auto payload = server->getPayload()) { + payload(); + } + if (auto payload = client->getPayload()) { + payload(); + } + + auto got = response.get(); + // Check response was received correctly. + ASSERT_EQ(got.error, false); + ASSERT_EQ(got.response.b, dap::boolean(true)); + ASSERT_EQ(got.response.i, dap::integer(99)); + ASSERT_EQ(got.response.n, dap::number(123.456)); + ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); + ASSERT_EQ(got.response.o.size(), 3U); + ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); + ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); + ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); + ASSERT_EQ(got.response.s, "ROGER"); + ASSERT_EQ(got.response.o1, dap::optional(50)); + ASSERT_FALSE(got.response.o2.has_value()); +} + +TEST_F(SessionTest, Concurrency) { + std::atomic numEventsHandled = {0}; + std::atomic done = {false}; + + server->registerHandler( + [](const dap::TestRequest&) { return dap::TestResponse(); }); + + server->registerHandler([&](const dap::TestEvent&) { + if (numEventsHandled++ > 10000) { + done = true; + } + }); + + bind(); + + constexpr int numThreads = 32; + std::array threads; + + for (int i = 0; i < numThreads; i++) { + threads[i] = std::thread([&] { + while (!done) { + client->send(createEvent()); + client->send(createRequest()); + } + }); + } + + for (int i = 0; i < numThreads; i++) { + threads[i].join(); + } + + client.reset(); + server.reset(); +} + +TEST_F(SessionTest, OnClientClosed) { + std::mutex mutex; + std::condition_variable cv; + bool clientClosed = false; + + auto client2server = dap::pipe(); + auto server2client = dap::pipe(); + + client->bind(server2client, client2server); + server->bind(client2server, server2client, [&] { + std::unique_lock lock(mutex); + clientClosed = true; + cv.notify_all(); + }); + + client.reset(); + + // Wait for the client closed handler to be called. + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return static_cast(clientClosed); }); +} + +TEST_F(SessionTest, OnServerClosed) { + std::mutex mutex; + std::condition_variable cv; + bool serverClosed = false; + + auto client2server = dap::pipe(); + auto server2client = dap::pipe(); + + client->bind(server2client, client2server, [&] { + std::unique_lock lock(mutex); + serverClosed = true; + cv.notify_all(); + }); + server->bind(client2server, server2client); + + server.reset(); + + // Wait for the client closed handler to be called. + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return static_cast(serverClosed); }); +} diff --git a/libraries/cppdap/src/socket.cpp b/libraries/cppdap/src/socket.cpp new file mode 100644 index 000000000..ad5691d34 --- /dev/null +++ b/libraries/cppdap/src/socket.cpp @@ -0,0 +1,337 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "socket.h" + +#include "rwmutex.h" + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#if defined(_WIN32) +#include +namespace { +std::atomic wsaInitCount = {0}; +} // anonymous namespace +#else +#include +#include +namespace { +using SOCKET = int; +} // anonymous namespace +#endif + +namespace { +constexpr SOCKET InvalidSocket = static_cast(-1); +void init() { +#if defined(_WIN32) + if (wsaInitCount++ == 0) { + WSADATA winsockData; + (void)WSAStartup(MAKEWORD(2, 2), &winsockData); + } +#endif +} + +void term() { +#if defined(_WIN32) + if (--wsaInitCount == 0) { + WSACleanup(); + } +#endif +} + +bool setBlocking(SOCKET s, bool blocking) { +#if defined(_WIN32) + u_long mode = blocking ? 0 : 1; + return ioctlsocket(s, FIONBIO, &mode) == NO_ERROR; +#else + auto arg = fcntl(s, F_GETFL, nullptr); + if (arg < 0) { + return false; + } + arg = blocking ? (arg & ~O_NONBLOCK) : (arg | O_NONBLOCK); + return fcntl(s, F_SETFL, arg) >= 0; +#endif +} + +bool errored(SOCKET s) { + if (s == InvalidSocket) { + return true; + } + char error = 0; + socklen_t len = sizeof(error); + getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len); + return error != 0; +} + +} // anonymous namespace + +class dap::Socket::Shared : public dap::ReaderWriter { + public: + static std::shared_ptr create(const char* address, const char* port) { + init(); + + addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + addrinfo* info = nullptr; + getaddrinfo(address, port, &hints, &info); + + if (info) { + auto socket = + ::socket(info->ai_family, info->ai_socktype, info->ai_protocol); + auto out = std::make_shared(info, socket); + out->setOptions(); + return out; + } + + term(); + return nullptr; + } + + Shared(SOCKET socket) : info(nullptr), s(socket) {} + Shared(addrinfo* info, SOCKET socket) : info(info), s(socket) {} + + ~Shared() { + if (info) { + freeaddrinfo(info); + } + close(); + term(); + } + + template + void lock(FUNCTION&& f) { + RLock l(mutex); + f(s, info); + } + + void setOptions() { + RLock l(mutex); + if (s == InvalidSocket) { + return; + } + + int enable = 1; + +#if !defined(_WIN32) + // Prevent sockets lingering after process termination, causing + // reconnection issues on the same port. + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable)); + + struct { + int l_onoff; /* linger active */ + int l_linger; /* how many seconds to linger for */ + } linger = {false, 0}; + setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)); +#endif // !defined(_WIN32) + + // Enable TCP_NODELAY. + // DAP usually consists of small packet requests, with small packet + // responses. When there are many frequent, blocking requests made, + // Nagle's algorithm can dramatically limit the request->response rates. + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable)); + } + + // dap::ReaderWriter compliance + bool isOpen() { + { + RLock l(mutex); + if ((s != InvalidSocket) && !errored(s)) { + return true; + } + } + WLock lock(mutex); + s = InvalidSocket; + return false; + } + + void close() { + { + RLock l(mutex); + if (s != InvalidSocket) { +#if defined(_WIN32) + closesocket(s); +#elif __APPLE__ + // ::shutdown() *should* be sufficient to unblock ::accept(), but + // apparently on macos it can return ENOTCONN and ::accept() continues + // to block indefinitely. + // Note: There is a race here. Calling ::close() frees the socket ID, + // which may be reused before `s` is assigned InvalidSocket. + ::shutdown(s, SHUT_RDWR); + ::close(s); +#else + // ::shutdown() to unblock ::accept(). We'll actually close the socket + // under lock below. + ::shutdown(s, SHUT_RDWR); +#endif + } + } + + WLock l(mutex); + if (s != InvalidSocket) { +#if !defined(_WIN32) && !defined(__APPLE__) + ::close(s); +#endif + s = InvalidSocket; + } + } + + size_t read(void* buffer, size_t bytes) { + RLock lock(mutex); + if (s == InvalidSocket) { + return 0; + } + auto len = + recv(s, reinterpret_cast(buffer), static_cast(bytes), 0); + return (len < 0) ? 0 : len; + } + + bool write(const void* buffer, size_t bytes) { + RLock lock(mutex); + if (s == InvalidSocket) { + return false; + } + if (bytes == 0) { + return true; + } + return ::send(s, reinterpret_cast(buffer), + static_cast(bytes), 0) > 0; + } + + private: + addrinfo* const info; + SOCKET s = InvalidSocket; + RWMutex mutex; +}; + +namespace dap { + +Socket::Socket(const char* address, const char* port) + : shared(Shared::create(address, port)) { + if (shared) { + bool reset = false; + shared->lock([&](SOCKET socket, const addrinfo* info) { + if (bind(socket, info->ai_addr, (int)info->ai_addrlen) != 0) { + reset = true; + return; + } + + if (listen(socket, 0) != 0) { + reset = true; + } + }); + if (reset) { + shared.reset(); + } + } +} + +std::shared_ptr Socket::accept() const { + std::shared_ptr out; + if (shared) { + shared->lock([&](SOCKET socket, const addrinfo*) { + if (socket != InvalidSocket && !errored(socket)) { + init(); + auto accepted = ::accept(socket, 0, 0); + if (accepted != InvalidSocket) { + out = std::make_shared(accepted); + out->setOptions(); + } + } + }); + } + return out; +} + +bool Socket::isOpen() const { + if (shared) { + return shared->isOpen(); + } + return false; +} + +void Socket::close() const { + if (shared) { + shared->close(); + } +} + +std::shared_ptr Socket::connect(const char* address, + const char* port, + uint32_t timeoutMillis) { + auto shared = Shared::create(address, port); + if (!shared) { + return nullptr; + } + + std::shared_ptr out; + shared->lock([&](SOCKET socket, const addrinfo* info) { + if (socket == InvalidSocket) { + return; + } + + if (timeoutMillis == 0) { + if (::connect(socket, info->ai_addr, (int)info->ai_addrlen) == 0) { + out = shared; + } + return; + } + + if (!setBlocking(socket, false)) { + return; + } + + auto res = ::connect(socket, info->ai_addr, (int)info->ai_addrlen); + if (res == 0) { + if (setBlocking(socket, true)) { + out = shared; + } + } else { + const auto microseconds = timeoutMillis * 1000; + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(socket, &fdset); + + timeval tv; + tv.tv_sec = microseconds / 1000000; + tv.tv_usec = microseconds - static_cast(tv.tv_sec * 1000000); + res = select(static_cast(socket + 1), nullptr, &fdset, nullptr, &tv); + if (res > 0 && !errored(socket) && setBlocking(socket, true)) { + out = shared; + } + } + }); + + if (!out) { + return nullptr; + } + + return out->isOpen() ? out : nullptr; +} + +} // namespace dap diff --git a/libraries/cppdap/src/socket.h b/libraries/cppdap/src/socket.h new file mode 100644 index 000000000..ec5b0dfbd --- /dev/null +++ b/libraries/cppdap/src/socket.h @@ -0,0 +1,47 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_socket_h +#define dap_socket_h + +#include "dap/io.h" + +#include +#include + +namespace dap { + +class Socket { + public: + class Shared; + + // connect() connects to the given TCP address and port. + // If timeoutMillis is non-zero and no connection was made before + // timeoutMillis milliseconds, then nullptr is returned. + static std::shared_ptr connect(const char* address, + const char* port, + uint32_t timeoutMillis); + + Socket(const char* address, const char* port); + bool isOpen() const; + std::shared_ptr accept() const; + void close() const; + + private: + std::shared_ptr shared; +}; + +} // namespace dap + +#endif // dap_socket_h diff --git a/libraries/cppdap/src/socket_test.cpp b/libraries/cppdap/src/socket_test.cpp new file mode 100644 index 000000000..186fd9a38 --- /dev/null +++ b/libraries/cppdap/src/socket_test.cpp @@ -0,0 +1,104 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "socket.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include + +// Basic socket send & receive test +TEST(Socket, SendRecv) { + const char* port = "19021"; + + auto server = dap::Socket("localhost", port); + + auto client = dap::Socket::connect("localhost", port, 0); + ASSERT_TRUE(client != nullptr); + + const std::string expect = "Hello World!"; + std::string read; + + auto thread = std::thread([&] { + auto conn = server.accept(); + ASSERT_TRUE(conn != nullptr); + char c; + while (conn->read(&c, 1) != 0) { + read += c; + } + }); + + ASSERT_TRUE(client->write(expect.data(), expect.size())); + + client->close(); + thread.join(); + + ASSERT_EQ(read, expect); +} + +// See https://github.com/google/cppdap/issues/37 +TEST(Socket, CloseOnDifferentThread) { + const char* port = "19021"; + + auto server = dap::Socket("localhost", port); + + auto client = dap::Socket::connect("localhost", port, 0); + ASSERT_TRUE(client != nullptr); + + auto conn = server.accept(); + + auto thread = std::thread([&] { + // Closing client on different thread should unblock client->read(). + client->close(); + }); + + char c; + while (client->read(&c, 1) != 0) { + } + + thread.join(); +} + +TEST(Socket, ConnectTimeout) { + const char* port = "19021"; + const int timeoutMillis = 200; + const int maxAttempts = 1024; + + using namespace std::chrono; + + auto server = dap::Socket("localhost", port); + + std::vector> connections; + + for (int i = 0; i < maxAttempts; i++) { + auto start = system_clock::now(); + auto connection = dap::Socket::connect("localhost", port, timeoutMillis); + auto end = system_clock::now(); + + if (!connection) { + auto timeTakenMillis = duration_cast(end - start).count(); + ASSERT_GE(timeTakenMillis + 20, // +20ms for a bit of timing wiggle room + timeoutMillis); + return; + } + + // Keep hold of the connections to saturate any incoming socket buffers. + connections.emplace_back(std::move(connection)); + } + + FAIL() << "Failed to test timeout after " << maxAttempts << " attempts"; +} diff --git a/libraries/cppdap/src/string_buffer.h b/libraries/cppdap/src/string_buffer.h new file mode 100644 index 000000000..1c381971a --- /dev/null +++ b/libraries/cppdap/src/string_buffer.h @@ -0,0 +1,95 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef dap_string_buffer_h +#define dap_string_buffer_h + +#include "dap/io.h" + +#include // std::min +#include // memcpy +#include +#include // std::unique_ptr +#include + +namespace dap { + +class StringBuffer : public virtual Reader, public virtual Writer { + public: + static inline std::unique_ptr create(); + + inline bool write(const std::string& s); + inline std::string string() const; + + // Reader / Writer compilance + inline bool isOpen() override; + inline void close() override; + inline size_t read(void* buffer, size_t bytes) override; + inline bool write(const void* buffer, size_t bytes) override; + + private: + std::string str; + std::deque chunk_lengths; + bool closed = false; +}; + +bool StringBuffer::isOpen() { + return !closed; +} +void StringBuffer::close() { + closed = true; +} + +std::unique_ptr StringBuffer::create() { + return std::unique_ptr(new StringBuffer()); +} + +bool StringBuffer::write(const std::string& s) { + return write(s.data(), s.size()); +} + +std::string StringBuffer::string() const { + return str; +} + +size_t StringBuffer::read(void* buffer, size_t bytes) { + if (closed || bytes == 0 || str.size() == 0 || chunk_lengths.size() == 0) { + return 0; + } + size_t& chunk_length = chunk_lengths.front(); + + auto len = std::min(bytes, chunk_length); + memcpy(buffer, str.data(), len); + str = std::string(str.begin() + len, str.end()); + if (bytes < chunk_length) { + chunk_length -= bytes; + } else { + chunk_lengths.pop_front(); + } + return len; +} + +bool StringBuffer::write(const void* buffer, size_t bytes) { + if (closed) { + return false; + } + auto chars = reinterpret_cast(buffer); + str.append(chars, chars + bytes); + chunk_lengths.push_back(bytes); + return true; +} + +} // namespace dap + +#endif // dap_string_buffer_h diff --git a/libraries/cppdap/src/traits_test.cpp b/libraries/cppdap/src/traits_test.cpp new file mode 100644 index 000000000..aafca04e1 --- /dev/null +++ b/libraries/cppdap/src/traits_test.cpp @@ -0,0 +1,387 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/traits.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace dap { +namespace traits { + +namespace { +struct S {}; +struct E : S {}; +void F1(S) {} +void F3(int, S, float) {} +void E1(E) {} +void E3(int, E, float) {} +} // namespace + +TEST(ParameterType, Function) { + F1({}); // Avoid unused method warning + F3(0, {}, 0); // Avoid unused method warning + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, + ""); +} + +TEST(ParameterType, Method) { + class C { + public: + void F1(S) {} + void F3(int, S, float) {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, + ""); +} + +TEST(ParameterType, ConstMethod) { + class C { + public: + void F1(S) const {} + void F3(int, S, float) const {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, + ""); +} + +TEST(ParameterType, StaticMethod) { + class C { + public: + static void F1(S) {} + static void F3(int, S, float) {} + }; + C::F1({}); // Avoid unused method warning + C::F3(0, {}, 0); // Avoid unused method warning + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, + ""); +} + +TEST(ParameterType, FunctionLike) { + using F1 = std::function; + using F3 = std::function; + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, ""); +} + +TEST(ParameterType, Lambda) { + auto l1 = [](S) {}; + auto l3 = [](int, S, float) {}; + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, ""); +} + +TEST(HasSignature, Function) { + F1({}); // Avoid unused method warning + F3(0, {}, 0); // Avoid unused method warning + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, Method) { + class C { + public: + void F1(S) {} + void F3(int, S, float) {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, ConstMethod) { + class C { + public: + void F1(S) const {} + void F3(int, S, float) const {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, StaticMethod) { + class C { + public: + static void F1(S) {} + static void F3(int, S, float) {} + }; + C::F1({}); // Avoid unused method warning + C::F3(0, {}, 0); // Avoid unused method warning + + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, FunctionLike) { + using f1 = std::function; + using f3 = std::function; + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, Lambda) { + auto l1 = [](S) {}; + auto l3 = [](int, S, float) {}; + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +//// + +TEST(CompatibleWith, Function) { + F1({}); // Avoid unused method warning + F3(0, {}, 0); // Avoid unused method warning + E1({}); // Avoid unused method warning + E3(0, {}, 0); // Avoid unused method warning + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, Method) { + class C { + public: + void F1(S) {} + void F3(int, S, float) {} + void E1(E) {} + void E3(int, E, float) {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + C().E1({}); // Avoid unused method warning + C().E3(0, {}, 0); // Avoid unused method warning + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, ConstMethod) { + class C { + public: + void F1(S) const {} + void F3(int, S, float) const {} + void E1(E) const {} + void E3(int, E, float) const {} + }; + C().F1({}); // Avoid unused method warning + C().F3(0, {}, 0); // Avoid unused method warning + C().E1({}); // Avoid unused method warning + C().E3(0, {}, 0); // Avoid unused method warning + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, StaticMethod) { + class C { + public: + static void F1(S) {} + static void F3(int, S, float) {} + static void E1(E) {} + static void E3(int, E, float) {} + }; + C::F1({}); // Avoid unused method warning + C::F3(0, {}, 0); // Avoid unused method warning + C::E1({}); // Avoid unused method warning + C::E3(0, {}, 0); // Avoid unused method warning + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, FunctionLike) { + using f1 = std::function; + using f3 = std::function; + using e1 = std::function; + using e3 = std::function; + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, Lambda) { + auto f1 = [](S) {}; + auto f3 = [](int, S, float) {}; + auto e1 = [](E) {}; + auto e3 = [](int, E, float) {}; + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +} // namespace traits +} // namespace dap diff --git a/libraries/cppdap/src/typeinfo.cpp b/libraries/cppdap/src/typeinfo.cpp new file mode 100644 index 000000000..dda481f9c --- /dev/null +++ b/libraries/cppdap/src/typeinfo.cpp @@ -0,0 +1,21 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/typeinfo.h" + +namespace dap { + +TypeInfo::~TypeInfo() = default; + +} // namespace dap diff --git a/libraries/cppdap/src/typeinfo_test.cpp b/libraries/cppdap/src/typeinfo_test.cpp new file mode 100644 index 000000000..23d579317 --- /dev/null +++ b/libraries/cppdap/src/typeinfo_test.cpp @@ -0,0 +1,65 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/typeof.h" +#include "dap/types.h" +#include "json_serializer.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace dap { + +struct BaseStruct { + dap::integer i; + dap::number n; +}; + +DAP_STRUCT_TYPEINFO(BaseStruct, + "BaseStruct", + DAP_FIELD(i, "i"), + DAP_FIELD(n, "n")); + +struct DerivedStruct : public BaseStruct { + dap::string s; + dap::boolean b; +}; + +DAP_STRUCT_TYPEINFO_EXT(DerivedStruct, + BaseStruct, + "DerivedStruct", + DAP_FIELD(s, "s"), + DAP_FIELD(b, "b")); + +} // namespace dap + +TEST(TypeInfo, Derived) { + dap::DerivedStruct in; + in.s = "hello world"; + in.b = true; + in.i = 42; + in.n = 3.14; + + dap::json::Serializer s; + ASSERT_TRUE(s.serialize(in)); + + dap::DerivedStruct out; + dap::json::Deserializer d(s.dump()); + ASSERT_TRUE(d.deserialize(&out)) << "Failed to deserialize\n" << s.dump(); + + ASSERT_EQ(out.s, "hello world"); + ASSERT_EQ(out.b, true); + ASSERT_EQ(out.i, 42); + ASSERT_EQ(out.n, 3.14); +} diff --git a/libraries/cppdap/src/typeof.cpp b/libraries/cppdap/src/typeof.cpp new file mode 100644 index 000000000..055421c66 --- /dev/null +++ b/libraries/cppdap/src/typeof.cpp @@ -0,0 +1,144 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/typeof.h" + +#include +#include +#include + +namespace { + +// TypeInfos owns all the dap::TypeInfo instances. +struct TypeInfos { + // get() returns the TypeInfos singleton pointer. + // TypeInfos is constructed with an internal reference count of 1. + static TypeInfos* get(); + + // reference() increments the TypeInfos reference count. + inline void reference() { + assert(refcount.load() > 0); + refcount++; + } + + // release() decrements the TypeInfos reference count. + // If the reference count becomes 0, then the TypeInfos is destructed. + inline void release() { + if (--refcount == 0) { + this->~TypeInfos(); + } + } + + struct NullTI : public dap::TypeInfo { + using null = dap::null; + inline std::string name() const override { return "null"; } + inline size_t size() const override { return sizeof(null); } + inline size_t alignment() const override { return alignof(null); } + inline void construct(void* ptr) const override { new (ptr) null(); } + inline void copyConstruct(void* dst, const void* src) const override { + new (dst) null(*reinterpret_cast(src)); + } + inline void destruct(void* ptr) const override { + reinterpret_cast(ptr)->~null(); + } + inline bool deserialize(const dap::Deserializer*, void*) const override { + return true; + } + inline bool serialize(dap::Serializer*, const void*) const override { + return true; + } + }; + + dap::BasicTypeInfo boolean = {"boolean"}; + dap::BasicTypeInfo string = {"string"}; + dap::BasicTypeInfo integer = {"integer"}; + dap::BasicTypeInfo number = {"number"}; + dap::BasicTypeInfo object = {"object"}; + dap::BasicTypeInfo any = {"any"}; + NullTI null; + std::vector> types; + + private: + TypeInfos() = default; + ~TypeInfos() = default; + std::atomic refcount = {1}; +}; + +// aligned_storage() is a replacement for std::aligned_storage that isn't busted +// on older versions of MSVC. +template +struct aligned_storage { + struct alignas(ALIGNMENT) type { + unsigned char data[SIZE]; + }; +}; + +TypeInfos* TypeInfos::get() { + static aligned_storage::type memory; + + struct Instance { + TypeInfos* ptr() { return reinterpret_cast(memory.data); } + Instance() { new (ptr()) TypeInfos(); } + ~Instance() { ptr()->release(); } + }; + + static Instance instance; + return instance.ptr(); +} + +} // namespace + +namespace dap { + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->boolean; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->string; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->integer; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->number; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->object; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->any; +} + +const TypeInfo* TypeOf::type() { + return &TypeInfos::get()->null; +} + +void TypeInfo::deleteOnExit(TypeInfo* ti) { + TypeInfos::get()->types.emplace_back(std::unique_ptr(ti)); +} + +void initialize() { + TypeInfos::get()->reference(); +} + +void terminate() { + TypeInfos::get()->release(); +} + +} // namespace dap diff --git a/libraries/cppdap/src/variant_test.cpp b/libraries/cppdap/src/variant_test.cpp new file mode 100644 index 000000000..5a1f4ebaf --- /dev/null +++ b/libraries/cppdap/src/variant_test.cpp @@ -0,0 +1,94 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dap/variant.h" +#include "dap/typeof.h" +#include "dap/types.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace dap { + +struct VariantTestObject { + dap::integer i; + dap::number n; +}; + +DAP_STRUCT_TYPEINFO(VariantTestObject, + "VariantTestObject", + DAP_FIELD(i, "i"), + DAP_FIELD(n, "n")); + +} // namespace dap + +TEST(Variant, EmptyConstruct) { + dap::variant variant; + ASSERT_TRUE(variant.is()); + ASSERT_FALSE(variant.is()); + ASSERT_FALSE(variant.is()); +} + +TEST(Variant, Boolean) { + dap::variant variant( + dap::boolean(true)); + ASSERT_TRUE(variant.is()); + ASSERT_EQ(variant.get(), dap::boolean(true)); +} + +TEST(Variant, Integer) { + dap::variant variant( + dap::integer(10)); + ASSERT_TRUE(variant.is()); + ASSERT_EQ(variant.get(), dap::integer(10)); +} + +TEST(Variant, TestObject) { + dap::variant variant( + dap::VariantTestObject{5, 3.0}); + ASSERT_TRUE(variant.is()); + ASSERT_EQ(variant.get().i, 5); + ASSERT_EQ(variant.get().n, 3.0); +} + +TEST(Variant, Assign) { + dap::variant variant( + dap::integer(10)); + variant = dap::integer(10); + ASSERT_TRUE(variant.is()); + ASSERT_FALSE(variant.is()); + ASSERT_FALSE(variant.is()); + ASSERT_EQ(variant.get(), dap::integer(10)); + variant = dap::boolean(true); + ASSERT_FALSE(variant.is()); + ASSERT_TRUE(variant.is()); + ASSERT_FALSE(variant.is()); + ASSERT_EQ(variant.get(), dap::boolean(true)); + variant = dap::VariantTestObject{5, 3.0}; + ASSERT_FALSE(variant.is()); + ASSERT_FALSE(variant.is()); + ASSERT_TRUE(variant.is()); + ASSERT_EQ(variant.get().i, 5); + ASSERT_EQ(variant.get().n, 3.0); +} + +TEST(Variant, Accepts) { + using variant = + dap::variant; + ASSERT_TRUE(variant::accepts()); + ASSERT_TRUE(variant::accepts()); + ASSERT_TRUE(variant::accepts()); + ASSERT_FALSE(variant::accepts()); + ASSERT_FALSE(variant::accepts()); +} diff --git a/libraries/cppdap/third_party/json/.circleci/config.yml b/libraries/cppdap/third_party/json/.circleci/config.yml new file mode 100644 index 000000000..0d2edbf6a --- /dev/null +++ b/libraries/cppdap/third_party/json/.circleci/config.yml @@ -0,0 +1,27 @@ +version: 2 +jobs: + build: + docker: + - image: debian:stretch + + steps: + - checkout + + - run: + name: Install sudo + command: 'apt-get update && apt-get install -y sudo && rm -rf /var/lib/apt/lists/*' + - run: + name: Install GCC + command: 'apt-get update && apt-get install -y gcc g++' + - run: + name: Install CMake + command: 'apt-get update && sudo apt-get install -y cmake' + - run: + name: Create build files + command: 'mkdir build ; cd build ; cmake ..' + - run: + name: Compile + command: 'cmake --build build' + - run: + name: Execute test suite + command: 'cd build ; ctest -j 2' diff --git a/libraries/cppdap/third_party/json/.clang-tidy b/libraries/cppdap/third_party/json/.clang-tidy new file mode 100644 index 000000000..feee81945 --- /dev/null +++ b/libraries/cppdap/third_party/json/.clang-tidy @@ -0,0 +1,26 @@ +Checks: '-*, + bugprone-*, + cert-*, + clang-analyzer-*, + google-*, + -google-runtime-references, + -google-explicit-constructor, + hicpp-*, + -hicpp-no-array-decay, + -hicpp-uppercase-literal-suffix, + -hicpp-explicit-conversions, + misc-*, + -misc-non-private-member-variables-in-classes, + llvm-*, + -llvm-header-guard, + modernize-*, + -modernize-use-trailing-return-type, + performance-*, + portability-*, + readability-*, + -readability-magic-numbers, + -readability-uppercase-literal-suffix' + +CheckOptions: + - key: hicpp-special-member-functions.AllowSoleDefaultDtor + value: 1 diff --git a/libraries/cppdap/third_party/json/.doozer.json b/libraries/cppdap/third_party/json/.doozer.json new file mode 100644 index 000000000..687aead8a --- /dev/null +++ b/libraries/cppdap/third_party/json/.doozer.json @@ -0,0 +1,82 @@ +{ + "targets": { + "raspbian-jessie": { + "buildenv": "raspbian-jessie", + "builddeps": ["build-essential", "wget"], + "buildcmd": [ + "uname -a", + "cat /etc/os-release", + "g++ --version", + "cd", + "wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz", + "tar xfz cmake-3.14.0.tar.gz", + "cd cmake-3.14.0", + "./bootstrap", + "make -j8", + "cd", + "mkdir build", + "cd build", + "../cmake-3.14.0/bin/cmake /project/repo/checkout", + "make -j8", + "../cmake-3.14.0/bin/ctest -VV -j4 --timeout 10000" + ] + }, + "xenial-armhf": { + "buildenv": "xenial-armhf", + "builddeps": ["build-essential", "wget"], + "buildcmd": [ + "uname -a", + "lsb_release -a", + "g++ --version", + "cd", + "wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz", + "tar xfz cmake-3.14.0.tar.gz", + "cd cmake-3.14.0", + "./bootstrap", + "make -j8", + "cd", + "mkdir build", + "cd build", + "../cmake-3.14.0/bin/cmake /project/repo/checkout", + "make -j8", + "../cmake-3.14.0/bin/ctest -VV -j4 --timeout 10000" + ] + }, + "fedora24-x86_64": { + "buildenv": "fedora24-x86_64", + "builddeps": ["cmake", "make", "gcc gcc-c++"], + "buildcmd": [ + "uname -a", + "cat /etc/fedora-release", + "g++ --version", + "cd", + "mkdir build", + "cd build", + "cmake /project/repo/checkout", + "make -j8", + "ctest -VV -j8" + ] + }, + "centos7-x86_64": { + "buildenv": "centos7-x86_64", + "builddeps": ["make", "wget", "gcc-c++"], + "buildcmd": [ + "uname -a", + "rpm -q centos-release", + "g++ --version", + "cd", + "wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz", + "tar xfz cmake-3.14.0.tar.gz", + "cd cmake-3.14.0", + "./bootstrap", + "make -j8", + "cd", + "mkdir build", + "cd build", + "../cmake-3.14.0/bin/cmake /project/repo/checkout", + "make -j8", + "../cmake-3.14.0/bin/ctest -VV -j8" + ] + } + } +} diff --git a/libraries/cppdap/third_party/json/.github/CODEOWNERS b/libraries/cppdap/third_party/json/.github/CODEOWNERS new file mode 100644 index 000000000..5d9d51d6b --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# JSON for Modern C++ has been originally written by Niels Lohmann. +# Since 2013 over 140 contributors have helped to improve the library. +# This CODEOWNERS file is only to make sure that @nlohmann is requsted +# for a code review in case of a pull request. + +* @nlohmann diff --git a/libraries/cppdap/third_party/json/.github/CONTRIBUTING.md b/libraries/cppdap/third_party/json/.github/CONTRIBUTING.md new file mode 100644 index 000000000..2287cad47 --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/CONTRIBUTING.md @@ -0,0 +1,71 @@ +[![Issue Stats](http://issuestats.com/github/nlohmann/json/badge/pr?style=flat)](http://issuestats.com/github/nlohmann/json) [![Issue Stats](http://issuestats.com/github/nlohmann/json/badge/issue?style=flat)](http://issuestats.com/github/nlohmann/json) + +# How to contribute + +This project started as a little excuse to exercise some of the cool new C++11 features. Over time, people actually started to use the JSON library (yey!) and started to help improve it by proposing features, finding bugs, or even fixing my mistakes. I am really [thankful](https://github.com/nlohmann/json/blob/master/README.md#thanks) for this and try to keep track of all the helpers. + +To make it as easy as possible for you to contribute and for me to keep an overview, here are a few guidelines which should help us avoid all kinds of unnecessary work or disappointment. And of course, this document is subject to discussion, so please [create an issue](https://github.com/nlohmann/json/issues/new/choose) or a pull request if you find a way to improve it! + +## Private reports + +Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to . + +## Prerequisites + +Please [create an issue](https://github.com/nlohmann/json/issues/new/choose), assuming one does not already exist, and describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this. + +## Describe your issue + +Clearly describe the issue: + +- If it is a bug, please describe how to **reproduce** it. If possible, attach a complete example which demonstrates the error. Please also state what you **expected** to happen instead of the error. +- If you propose a change or addition, try to give an **example** how the improved code could look like or how to use it. +- If you found a compilation error, please tell us which **compiler** (version and operating system) you used and paste the (relevant part of) the error messages to the ticket. + +Please stick to the provided issue templates ([bug report](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/Bug_report.md), [feature request](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/Feature_request.md), or [question](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/question.md)) if possible. + +## Files to change + +:exclamation: Before you make any changes, note the single-header file [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is **generated** from the source files in the [`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). Please **do not** edit file `single_include/nlohmann/json.hpp` directly, but change the `include/nlohmann` sources and regenerate file `single_include/nlohmann/json.hpp` by executing `make amalgamate`. + +To make changes, you need to edit the following files: + +1. [`include/nlohmann/*`](https://github.com/nlohmann/json/tree/develop/include/nlohmann) - These files are the sources of the library. Before testing or creating a pull request, execute `make amalgamate` to regenerate `single_include/nlohmann/json.hpp`. + +2. [`test/src/unit-*.cpp`](https://github.com/nlohmann/json/tree/develop/test/src) - These files contain the [doctest](https://github.com/onqtam/doctest) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code. + + If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with + + ```sh + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + $ ctest + ``` + + The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request. + + +## Note + +- If you open a pull request, the code will be automatically tested with [Valgrind](http://valgrind.org)'s Memcheck tool to detect memory leaks. Please be aware that the execution with Valgrind _may_ in rare cases yield different behavior than running the code directly. This can result in failing unit tests which run successfully without Valgrind. +- There is a Makefile target `make pretty` which runs [Artistic Style](http://astyle.sourceforge.net) to fix indentation. If possible, run it before opening the pull request. Otherwise, we shall run it afterward. + +## Please don't + +- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means. +- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. +- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. + - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments. + - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map). + +- Please do not open pull requests that address **multiple issues**. + +## Wanted + +The following areas really need contribution: + +- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the bleeding-edge versions of GCC or Clang. +- Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser with hand coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for parsing. +- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency is not everything, speed and memory consumption are very important characteristics for C++ developers, so having proper comparisons would be interesting. diff --git a/libraries/cppdap/third_party/json/.github/FUNDING.yml b/libraries/cppdap/third_party/json/.github/FUNDING.yml new file mode 100644 index 000000000..c202804be --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: http://paypal.me/nlohmann diff --git a/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 000000000..748357a95 --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'kind: bug' +assignees: '' + +--- + +- What is the issue you have? + +- Please describe the steps to reproduce the issue. Can you provide a small but working code example? + +- What is the expected behavior? + +- And what is the actual behavior instead? + +- Which compiler and operating system are you using? Is it a [supported compiler](https://github.com/nlohmann/json#supported-compilers)? + +- Did you use a released version of the library or the version from the `develop` branch? + +- If you experience a compilation error: can you [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests)? diff --git a/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 000000000..05a2c946e --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,12 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'kind: enhancement/improvement' +assignees: '' + +--- + +- Describe the feature in as much detail as possible. + +- Include sample usage where appropriate. diff --git a/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 000000000..0870cd17b --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,16 @@ +--- +name: Question +about: Ask a question regarding the library. +title: '' +labels: 'kind: question' +assignees: '' + +--- + +- Describe what you want to achieve. + +- Describe what you tried. + +- Describe which system (OS, compiler) you are using. + +- Describe which version of the library you are using (release version, develop branch). diff --git a/libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md b/libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..f2c3af6a8 --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +[Describe your pull request here. Please read the text below the line, and make sure you follow the checklist.] + +* * * + +## Pull request checklist + +Read the [Contribution Guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) for detailed information. + +- [ ] Changes are described in the pull request, or an [existing issue is referenced](https://github.com/nlohmann/json/issues). +- [ ] The test suite [compiles and runs](https://github.com/nlohmann/json/blob/develop/README.md#execute-unit-tests) without error. +- [ ] [Code coverage](https://coveralls.io/github/nlohmann/json) is 100%. Test cases can be added by editing the [test suite](https://github.com/nlohmann/json/tree/develop/test/src). +- [ ] The source code is amalgamated; that is, after making changes to the sources in the `include/nlohmann` directory, run `make amalgamate` to create the single-header file `single_include/nlohmann/json.hpp`. The whole process is described [here](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#files-to-change). + +## Please don't + +- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means. +- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. +- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. +- Please do not open pull requests that address **multiple issues**. diff --git a/libraries/cppdap/third_party/json/.github/SECURITY.md b/libraries/cppdap/third_party/json/.github/SECURITY.md new file mode 100644 index 000000000..4d010ebda --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to . You can use [this key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69) for encryption. diff --git a/libraries/cppdap/third_party/json/.github/config.yml b/libraries/cppdap/third_party/json/.github/config.yml new file mode 100644 index 000000000..7a8f41e6d --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/config.yml @@ -0,0 +1,19 @@ +# Configuration for sentiment-bot - https://github.com/behaviorbot/sentiment-bot + +# *Required* toxicity threshold between 0 and .99 with the higher numbers being the most toxic +# Anything higher than this threshold will be marked as toxic and commented on +sentimentBotToxicityThreshold: .7 + +# *Required* Comment to reply with +sentimentBotReplyComment: > + Please be sure to review the [code of conduct](https://github.com/nlohmann/json/blob/develop/CODE_OF_CONDUCT.md) and be respectful of other users. cc/ @nlohmann + + +# Configuration for request-info - https://github.com/behaviorbot/request-info + +# *Required* Comment to reply with +requestInfoReplyComment: > + We would appreciate it if you could provide us with more info about this issue or pull request! Please check the [issue template](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE.md) and the [pull request template](https://github.com/nlohmann/json/blob/develop/.github/PULL_REQUEST_TEMPLATE.md). + +# *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given +requestInfoLabelToAdd: "state: needs more info" diff --git a/libraries/cppdap/third_party/json/.github/stale.yml b/libraries/cppdap/third_party/json/.github/stale.yml new file mode 100644 index 000000000..d30c78be7 --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 30 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: "state: stale" +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/libraries/cppdap/third_party/json/.github/workflows/ccpp.yml b/libraries/cppdap/third_party/json/.github/workflows/ccpp.yml new file mode 100644 index 000000000..c6f8afb3c --- /dev/null +++ b/libraries/cppdap/third_party/json/.github/workflows/ccpp.yml @@ -0,0 +1,19 @@ +name: C/C++ CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: prepare + run: mkdir build + - name: cmake + run: cd build ; cmake .. + - name: build + run: make -C build + - name: test + run: cd build ; ctest -j 10 diff --git a/libraries/cppdap/third_party/json/.gitignore b/libraries/cppdap/third_party/json/.gitignore new file mode 100644 index 000000000..086e855c0 --- /dev/null +++ b/libraries/cppdap/third_party/json/.gitignore @@ -0,0 +1,25 @@ +json_unit +json_benchmarks +json_benchmarks_simple +fuzz-testing + +*.dSYM +*.o +*.gcno +*.gcda + +build +build_coverage +clang_analyze_build + +doc/xml +doc/html +me.nlohmann.json.docset + +benchmarks/files/numbers/*.json + +.idea +cmake-build-debug + +test/test-* +/.vs diff --git a/libraries/cppdap/third_party/json/.travis.yml b/libraries/cppdap/third_party/json/.travis.yml new file mode 100644 index 000000000..8f871c09a --- /dev/null +++ b/libraries/cppdap/third_party/json/.travis.yml @@ -0,0 +1,345 @@ +######################### +# project configuration # +######################### + +# C++ project +language: cpp + +dist: trusty +sudo: required +group: edge + + +################### +# global settings # +################### + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" + + +################ +# build matrix # +################ + +matrix: + include: + + # Valgrind + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - CMAKE_OPTIONS=-DJSON_Valgrind=ON + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'valgrind', 'ninja-build'] + + # clang sanitizer + - os: linux + compiler: clang + env: + - COMPILER=clang++-7 + - CMAKE_OPTIONS=-DJSON_Sanitizer=ON + - UBSAN_OPTIONS=print_stacktrace=1,suppressions=$(pwd)/test/src/UBSAN.supp + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7'] + packages: ['g++-6', 'clang-7', 'ninja-build'] + before_script: + - export PATH=$PATH:/usr/lib/llvm-7/bin + + # cppcheck + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=cppcheck + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'cppcheck', 'ninja-build'] + after_success: + - make cppcheck + + # no exceptions + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - CMAKE_OPTIONS=-DJSON_NoExceptions=ON + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'ninja-build'] + + # check amalgamation + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=amalgamation + - MULTIPLE_HEADERS=ON + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'astyle', 'ninja-build'] + after_success: + - make check-amalgamation + + # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'ninja-build'] + before_script: + - pip install --user cpp-coveralls + after_success: + - coveralls --build-root test --include include/nlohmann --gcov 'gcov-4.9' --gcov-options '\-lp' + env: + - COMPILER=g++-4.9 + - CMAKE_OPTIONS=-DJSON_Coverage=ON + - MULTIPLE_HEADERS=ON + + # Coverity (only for branch coverity_scan) + + - os: linux + compiler: clang + before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] + packages: ['g++-6', 'clang-3.6', 'ninja-build'] + coverity_scan: + project: + name: "nlohmann/json" + description: "Build submitted via Travis CI" + notification_email: niels.lohmann@gmail.com + build_command_prepend: "mkdir coverity_build ; cd coverity_build ; cmake .. ; cd .." + build_command: "make -C coverity_build" + branch_pattern: coverity_scan + env: + - SPECIAL=coverity + - COMPILER=clang++-3.6 + + # OSX / Clang + + - os: osx + osx_image: xcode8.3 + + - os: osx + osx_image: xcode9 + + - os: osx + osx_image: xcode9.1 + + - os: osx + osx_image: xcode9.2 + + - os: osx + osx_image: xcode9.3 + + - os: osx + osx_image: xcode9.4 + + - os: osx + osx_image: xcode10 + + - os: osx + osx_image: xcode10.1 + + # Linux / GCC + + - os: linux + compiler: gcc + env: compiler=g++-4.8 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.8', 'ninja-build'] + + - os: linux + compiler: gcc + env: compiler=g++-4.9 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'ninja-build'] + + - os: linux + compiler: gcc + env: COMPILER=g++-5 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'ninja-build'] + + - os: linux + compiler: gcc + env: COMPILER=g++-6 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-6', 'ninja-build'] + + - os: linux + compiler: gcc + env: COMPILER=g++-7 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-7', 'ninja-build'] + + - os: linux + compiler: gcc + env: COMPILER=g++-8 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-8', 'ninja-build'] + + - os: linux + compiler: gcc + env: COMPILER=g++-9 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-9', 'ninja-build'] + + - os: linux + compiler: gcc + env: + - COMPILER=g++-9 + - CXXFLAGS=-std=c++2a + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-9', 'ninja-build'] + + # Linux / Clang + + - os: linux + compiler: clang + env: COMPILER=clang++-3.5 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5'] + packages: ['g++-6', 'clang-3.5', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-3.6 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] + packages: ['g++-6', 'clang-3.6', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-3.7 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] + packages: ['g++-6', 'clang-3.7', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-3.8 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-6', 'clang-3.8', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-3.9 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-6', 'clang-3.9', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-4.0 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0'] + packages: ['g++-6', 'clang-4.0', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-5.0 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0'] + packages: ['g++-6', 'clang-5.0', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-6.0 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0'] + packages: ['g++-6', 'clang-6.0', 'ninja-build'] + + - os: linux + compiler: clang + env: COMPILER=clang++-7 + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7'] + packages: ['g++-6', 'clang-7', 'ninja-build'] + + - os: linux + compiler: clang + env: + - COMPILER=clang++-7 + - CXXFLAGS=-std=c++1z + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7'] + packages: ['g++-6', 'clang-7', 'ninja-build'] + +################ +# build script # +################ + +script: + # get CMake and Ninja (only for systems with brew - macOS) + - | + if [[ (-x $(which brew)) ]]; then + brew update + brew install cmake ninja + brew upgrade cmake + cmake --version + fi + + # make sure CXX is correctly set + - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi + # by default, use the single-header version + - if [[ "${MULTIPLE_HEADERS}" == "" ]]; then export MULTIPLE_HEADERS=OFF; fi + + # show OS/compiler version + - uname -a + - $CXX --version + + # compile and execute unit tests + - mkdir -p build && cd build + - cmake .. ${CMAKE_OPTIONS} -DJSON_MultipleHeaders=${MULTIPLE_HEADERS} -GNinja && cmake --build . --config Release + - ctest -C Release --timeout 2700 -V -j + - cd .. + + # check if homebrew works (only checks develop branch) + - if [ `which brew` ]; then + brew update ; + brew tap nlohmann/json ; + brew install nlohmann_json --HEAD ; + brew test nlohmann_json ; + fi diff --git a/libraries/cppdap/third_party/json/CMakeLists.txt b/libraries/cppdap/third_party/json/CMakeLists.txt new file mode 100644 index 000000000..f717ff462 --- /dev/null +++ b/libraries/cppdap/third_party/json/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.1) + +## +## PROJECT +## name and version +## +project(nlohmann_json VERSION 3.7.0 LANGUAGES CXX) + +## +## INCLUDE +## +## +include(ExternalProject) + +## +## OPTIONS +## +option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) +option(JSON_Install "Install CMake targets during install step." ON) +option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) + +## +## CONFIGURATION +## +include(GNUInstallDirs) + +set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME}) +set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "") +set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") +set(NLOHMANN_JSON_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE "cmake/config.cmake.in") +set(NLOHMANN_JSON_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}") +set(NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake") +set(NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake") +set(NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Targets.cmake") + +if (JSON_MultipleHeaders) + set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/") + message(STATUS "Using the multi-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}") +else() + set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/single_include/") + message(STATUS "Using the single-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}") +endif() + +## +## TARGET +## create target and add include path +## +add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE) +add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME}) +if (${CMAKE_VERSION} VERSION_LESS "3.8.0") + target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_range_for) +else() + target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11) +endif() + +target_include_directories( + ${NLOHMANN_JSON_TARGET_NAME} + INTERFACE + $ + $ +) + +## add debug view definition file for msvc (natvis) +if (MSVC) + set(NLOHMANN_ADD_NATVIS TRUE) + set(NLOHMANN_NATVIS_FILE "nlohmann_json.natvis") + target_sources( + ${NLOHMANN_JSON_TARGET_NAME} + INTERFACE + $ + $ + ) +endif() + +## +## TESTS +## create and configure the unit test target +## +include(CTest) #adds option BUILD_TESTING (default ON) + +if(BUILD_TESTING AND JSON_BuildTests) + enable_testing() + add_subdirectory(test) +endif() + +## +## INSTALL +## install header files, generate and install cmake config files for find_package() +## +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE} COMPATIBILITY SameMajorVersion +) +configure_file( + ${NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE} + ${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE} + @ONLY +) + +if(JSON_Install) + install( + DIRECTORY ${NLOHMANN_JSON_INCLUDE_BUILD_DIR} + DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR} + ) + install( + FILES ${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE} ${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE} + DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR} + ) + if (NLOHMANN_ADD_NATVIS) + install( + FILES ${NLOHMANN_NATVIS_FILE} + DESTINATION . + ) + endif() + export( + TARGETS ${NLOHMANN_JSON_TARGET_NAME} + NAMESPACE ${PROJECT_NAME}:: + FILE ${NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE} + ) + install( + TARGETS ${NLOHMANN_JSON_TARGET_NAME} + EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME} + INCLUDES DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR} + ) + install( + EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME} + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR} + ) +endif() diff --git a/libraries/cppdap/third_party/json/CODE_OF_CONDUCT.md b/libraries/cppdap/third_party/json/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..770b8173e --- /dev/null +++ b/libraries/cppdap/third_party/json/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@nlohmann.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/libraries/cppdap/third_party/json/ChangeLog.md b/libraries/cppdap/third_party/json/ChangeLog.md new file mode 100644 index 000000000..dffc858d2 --- /dev/null +++ b/libraries/cppdap/third_party/json/ChangeLog.md @@ -0,0 +1,1670 @@ +# Change Log +All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). + +## [v3.7.0](https://github.com/nlohmann/json/releases/tag/v3.7.0) (2019-07-28) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.6.1...v3.7.0) + +- How can I retrieve uknown strings from json file in my C++ program. [\#1684](https://github.com/nlohmann/json/issues/1684) +- contains\(\) is sometimes causing stack-based buffer overrun exceptions [\#1683](https://github.com/nlohmann/json/issues/1683) +- How to deserialize arrays from json [\#1681](https://github.com/nlohmann/json/issues/1681) +- Compilation failed in VS2015 [\#1678](https://github.com/nlohmann/json/issues/1678) +- Why the compiled object file is so huge? [\#1677](https://github.com/nlohmann/json/issues/1677) +- From Version 2.1.1 to 3.6.1 serialize std::set [\#1676](https://github.com/nlohmann/json/issues/1676) +- Qt deprecation model halting compiltion [\#1675](https://github.com/nlohmann/json/issues/1675) +- Build For Raspberry pi , Rapbery with new Compiler C++17 [\#1671](https://github.com/nlohmann/json/issues/1671) +- Build from Raspberry pi [\#1667](https://github.com/nlohmann/json/issues/1667) +- Can not translate map with integer key to dict string ? [\#1664](https://github.com/nlohmann/json/issues/1664) +- Double type converts to scientific notation [\#1661](https://github.com/nlohmann/json/issues/1661) +- Missing v3.6.1 tag on master branch [\#1657](https://github.com/nlohmann/json/issues/1657) +- Support Fleese Binary Data Format [\#1654](https://github.com/nlohmann/json/issues/1654) +- Suggestion: replace alternative tokens for !, && and || with their symbols [\#1652](https://github.com/nlohmann/json/issues/1652) +- Build failure test-allocator.vcxproj [\#1651](https://github.com/nlohmann/json/issues/1651) +- How to provide function json& to\_json\(\) which is similar as 'void to\_json\(json&j, const CObject& obj\)' ? [\#1650](https://github.com/nlohmann/json/issues/1650) +- Can't throw exception when starting file is a number [\#1649](https://github.com/nlohmann/json/issues/1649) +- to\_json / from\_json with nested type [\#1648](https://github.com/nlohmann/json/issues/1648) +- How to create a json object from a std::string, created by j.dump? [\#1645](https://github.com/nlohmann/json/issues/1645) +- Problem getting vector \(array\) of strings [\#1644](https://github.com/nlohmann/json/issues/1644) +- json.hpp compilation issue with other typedefs with same name [\#1642](https://github.com/nlohmann/json/issues/1642) +- nlohmann::adl\_serializer\::to\_json no matching overloaded function found [\#1641](https://github.com/nlohmann/json/issues/1641) +- overwrite adl\_serializer\ to change behaviour [\#1638](https://github.com/nlohmann/json/issues/1638) +- json.SelectToken\("Manufacturers.Products.Price"\); [\#1637](https://github.com/nlohmann/json/issues/1637) +- Add json type as value [\#1636](https://github.com/nlohmann/json/issues/1636) +- Unit conversion test error: conversion from 'nlohmann::json' to non-scalar type 'std::string\_view' requested [\#1634](https://github.com/nlohmann/json/issues/1634) +- nlohmann VS JsonCpp by C++17 [\#1633](https://github.com/nlohmann/json/issues/1633) +- To integrate an inline helper function that return type name as string [\#1632](https://github.com/nlohmann/json/issues/1632) +- Return JSON as reference [\#1631](https://github.com/nlohmann/json/issues/1631) +- Updating from an older version causes problems with assing a json object to a struct [\#1630](https://github.com/nlohmann/json/issues/1630) +- Can without default constructor function for user defined classes when only to\_json is needed? [\#1629](https://github.com/nlohmann/json/issues/1629) +- Compilation fails with clang 6.x-8.x in C++14 mode [\#1628](https://github.com/nlohmann/json/issues/1628) +- Treating floating point as string [\#1627](https://github.com/nlohmann/json/issues/1627) +- error parsing character å [\#1626](https://github.com/nlohmann/json/issues/1626) +- \[Help\] How to Improve Json Output Performance with Large Json Arrays [\#1624](https://github.com/nlohmann/json/issues/1624) +- Suggested link changes for reporting new issues \[blob/develop/REAME.md and blob/develop/.github/CONTRIBUTING.md\] [\#1623](https://github.com/nlohmann/json/issues/1623) +- Broken link to issue template in CONTRIBUTING.md [\#1622](https://github.com/nlohmann/json/issues/1622) +- Missing word in README.md file [\#1621](https://github.com/nlohmann/json/issues/1621) +- Package manager instructions in README for brew is incorrect [\#1620](https://github.com/nlohmann/json/issues/1620) +- Building with Visual Studio 2019 [\#1619](https://github.com/nlohmann/json/issues/1619) +- Precedence of to\_json and builtin harmful [\#1617](https://github.com/nlohmann/json/issues/1617) +- The type json is missing from the html documentation [\#1616](https://github.com/nlohmann/json/issues/1616) +- variant is not support in Release 3.6.1? [\#1615](https://github.com/nlohmann/json/issues/1615) +- Replace assert with throw for const operator\[\] [\#1614](https://github.com/nlohmann/json/issues/1614) +- Memory Overhead is Too High \(10x or more\) [\#1613](https://github.com/nlohmann/json/issues/1613) +- program crash everytime, when other data type incomming in json stream as expected [\#1612](https://github.com/nlohmann/json/issues/1612) +- Improved Enum Support [\#1611](https://github.com/nlohmann/json/issues/1611) +- is it possible convert json object back to stl container ? [\#1610](https://github.com/nlohmann/json/issues/1610) +- Add C++17-like emplace.back\(\) for arrays. [\#1609](https://github.com/nlohmann/json/issues/1609) +- is\_nothrow\_copy\_constructible fails for json::const\_iterator on MSVC2015 x86 Debug build [\#1608](https://github.com/nlohmann/json/issues/1608) +- Reading and writing array elements [\#1607](https://github.com/nlohmann/json/issues/1607) +- Converting json::value to int [\#1605](https://github.com/nlohmann/json/issues/1605) +- I have a vector of keys and and a string of value and i want to create nested json array [\#1604](https://github.com/nlohmann/json/issues/1604) +- In compatible JSON object from nlohmann::json to nohman::json - unexpected end of input; expected '\[', '{', or a literal [\#1603](https://github.com/nlohmann/json/issues/1603) +- json parser crash if having a large number integer in message [\#1602](https://github.com/nlohmann/json/issues/1602) +- Value method with undocumented throwing 302 exception [\#1601](https://github.com/nlohmann/json/issues/1601) +- Accessing value with json pointer adds key if not existing [\#1600](https://github.com/nlohmann/json/issues/1600) +- README.md broken link to project documentation [\#1597](https://github.com/nlohmann/json/issues/1597) +- Random Kudos: Thanks for your work on this! [\#1596](https://github.com/nlohmann/json/issues/1596) +- json::parse return value and errors [\#1595](https://github.com/nlohmann/json/issues/1595) +- initializer list constructor makes curly brace initialization fragile [\#1594](https://github.com/nlohmann/json/issues/1594) +- trying to log message for missing keyword, difference between \["foo"\] and at\("foo"\) [\#1593](https://github.com/nlohmann/json/issues/1593) +- std::string and std::wstring `to\_json` [\#1592](https://github.com/nlohmann/json/issues/1592) +- I have a C structure which I need to convert to a JSON. How do I do it? Haven't found proper examples so far. [\#1591](https://github.com/nlohmann/json/issues/1591) +- dump\_escaped possible error ? [\#1589](https://github.com/nlohmann/json/issues/1589) +- json::parse\(\) into a vector\ results in unhandled exception [\#1587](https://github.com/nlohmann/json/issues/1587) +- push\_back\(\)/emplace\_back\(\) on array invalidates pointers to existing array items [\#1586](https://github.com/nlohmann/json/issues/1586) +- Getting nlohmann::detail::parse\_error on JSON generated by nlohmann::json not sure why [\#1583](https://github.com/nlohmann/json/issues/1583) +- getting error terminate called after throwing an instance of 'std::domain\_error' what\(\): cannot use at\(\) with string [\#1582](https://github.com/nlohmann/json/issues/1582) +- how i create json file [\#1581](https://github.com/nlohmann/json/issues/1581) +- prevent rounding of double datatype values [\#1580](https://github.com/nlohmann/json/issues/1580) +- Documentation Container Overview Doesn't Reference Const Methods [\#1579](https://github.com/nlohmann/json/issues/1579) +- Writing an array into a nlohmann::json object [\#1578](https://github.com/nlohmann/json/issues/1578) +- compilation error when using with another library [\#1577](https://github.com/nlohmann/json/issues/1577) +- Homebrew on OSX doesn't install cmake config file [\#1576](https://github.com/nlohmann/json/issues/1576) +- `unflatten` vs objects with number-ish keys [\#1575](https://github.com/nlohmann/json/issues/1575) +- JSON Parse Out of Range Error [\#1574](https://github.com/nlohmann/json/issues/1574) +- Integrating into existing CMake Project [\#1573](https://github.com/nlohmann/json/issues/1573) +- A "thinner" source code tar as part of release? [\#1572](https://github.com/nlohmann/json/issues/1572) +- conversion to std::string failed [\#1571](https://github.com/nlohmann/json/issues/1571) +- jPtr operation does not throw [\#1569](https://github.com/nlohmann/json/issues/1569) +- How to generate dll file for this project [\#1568](https://github.com/nlohmann/json/issues/1568) +- how to pass variable data to json in c [\#1567](https://github.com/nlohmann/json/issues/1567) +- I want to achieve an upgraded function. [\#1566](https://github.com/nlohmann/json/issues/1566) +- How to determine the type of elements read from a JSON array? [\#1564](https://github.com/nlohmann/json/issues/1564) +- try\_get\_to [\#1563](https://github.com/nlohmann/json/issues/1563) +- example code compile error [\#1562](https://github.com/nlohmann/json/issues/1562) +- How to iterate over nested json object [\#1561](https://github.com/nlohmann/json/issues/1561) +- Build Option/Separate Function to Allow to Throw on Duplicate Keys [\#1560](https://github.com/nlohmann/json/issues/1560) +- Compiler Switches -Weffc++ & -Wshadow are throwing errors [\#1558](https://github.com/nlohmann/json/issues/1558) +- warning: use of the 'nodiscard' attribute is a C++17 extension [\#1557](https://github.com/nlohmann/json/issues/1557) +- Import/Export compressed JSON files [\#1556](https://github.com/nlohmann/json/issues/1556) +- GDB renderers for json library [\#1554](https://github.com/nlohmann/json/issues/1554) +- Is it possible to construct a json string object from a binary buffer? [\#1553](https://github.com/nlohmann/json/issues/1553) +- json objects in list [\#1552](https://github.com/nlohmann/json/issues/1552) +- Matrix output [\#1550](https://github.com/nlohmann/json/issues/1550) +- Using json merge\_patch on ordered non-alphanumeric datasets [\#1549](https://github.com/nlohmann/json/issues/1549) +- Invalid parsed value for big integer [\#1548](https://github.com/nlohmann/json/issues/1548) +- Integrating with android ndk issues. [\#1547](https://github.com/nlohmann/json/issues/1547) +- add noexcept json::value\("key", default\) method variant? [\#1546](https://github.com/nlohmann/json/issues/1546) +- Thank you! 🙌 [\#1545](https://github.com/nlohmann/json/issues/1545) +- Output and input matrix [\#1544](https://github.com/nlohmann/json/issues/1544) +- Add regression tests for MSVC [\#1543](https://github.com/nlohmann/json/issues/1543) +- \[Help Needed!\] Season of Docs [\#1542](https://github.com/nlohmann/json/issues/1542) +- program still abort\(\) or exit\(\) with try catch [\#1541](https://github.com/nlohmann/json/issues/1541) +- Have a json::type\_error exception because of JSON object [\#1540](https://github.com/nlohmann/json/issues/1540) +- Using versioned namespaces [\#1539](https://github.com/nlohmann/json/issues/1539) +- Quoted numbers [\#1538](https://github.com/nlohmann/json/issues/1538) +- Reading a JSON file into an object [\#1537](https://github.com/nlohmann/json/issues/1537) +- Releases 3.6.0 and 3.6.1 don't build on conda / windows [\#1536](https://github.com/nlohmann/json/issues/1536) +- \[Clang\] warning: use of the 'nodiscard' attribute is a C++17 extension \[-Wc++17-extensions\] [\#1535](https://github.com/nlohmann/json/issues/1535) +- wchar\_t/std::wstring json can be created but not accessed [\#1533](https://github.com/nlohmann/json/issues/1533) +- json stringify [\#1532](https://github.com/nlohmann/json/issues/1532) +- How can I use std::string\_view as the json\_key to "operator \[\]" ? [\#1529](https://github.com/nlohmann/json/issues/1529) +- How can I use it from gcc on RPI [\#1528](https://github.com/nlohmann/json/issues/1528) +- std::pair treated as an array instead of key-value in `std::vector\\>` [\#1520](https://github.com/nlohmann/json/issues/1520) +- Excessive Memory Usage for Large Json File [\#1516](https://github.com/nlohmann/json/issues/1516) +- SAX dumper [\#1512](https://github.com/nlohmann/json/issues/1512) +- Conversion to user type containing a std::vector not working with documented approach [\#1511](https://github.com/nlohmann/json/issues/1511) +- How to get position info or parser context with custom from\_json\(\) that may throw exceptions? [\#1508](https://github.com/nlohmann/json/issues/1508) +- Inconsistent use of type alias. [\#1507](https://github.com/nlohmann/json/issues/1507) +- Is there a current way to represent strings as json int? [\#1503](https://github.com/nlohmann/json/issues/1503) +- Intermittent issues with loadJSON [\#1484](https://github.com/nlohmann/json/issues/1484) +- use json construct std::string [\#1462](https://github.com/nlohmann/json/issues/1462) +- JSON Creation [\#1461](https://github.com/nlohmann/json/issues/1461) +- Substantial performance penalty caused by polymorphic input adapter [\#1457](https://github.com/nlohmann/json/issues/1457) +- Null bytes in files are treated like EOF [\#1095](https://github.com/nlohmann/json/issues/1095) +- Feature: to\_string\(const json& j\); [\#916](https://github.com/nlohmann/json/issues/916) + +- Use GNUInstallDirs instead of hard-coded path. [\#1673](https://github.com/nlohmann/json/pull/1673) ([remyabel](https://github.com/remyabel)) +- Package Manager: MSYS2 \(pacman\) [\#1670](https://github.com/nlohmann/json/pull/1670) ([podsvirov](https://github.com/podsvirov)) +- Fix json.hpp compilation issue with other typedefs with same name \(Issue \#1642\) [\#1643](https://github.com/nlohmann/json/pull/1643) ([kevinlul](https://github.com/kevinlul)) +- Add explicit conversion from json to std::string\_view in conversion unit test [\#1639](https://github.com/nlohmann/json/pull/1639) ([taylorhoward92](https://github.com/taylorhoward92)) +- Minor fixes in docs [\#1625](https://github.com/nlohmann/json/pull/1625) ([nickaein](https://github.com/nickaein)) +- Fix broken links to documentation [\#1598](https://github.com/nlohmann/json/pull/1598) ([nickaein](https://github.com/nickaein)) +- Added to\_string and added basic tests [\#1585](https://github.com/nlohmann/json/pull/1585) ([Macr0Nerd](https://github.com/Macr0Nerd)) +- Regression tests for MSVC [\#1570](https://github.com/nlohmann/json/pull/1570) ([nickaein](https://github.com/nickaein)) +- Fix/1511 [\#1555](https://github.com/nlohmann/json/pull/1555) ([theodelrieu](https://github.com/theodelrieu)) +- Remove C++17 extension warning from clang; \#1535 [\#1551](https://github.com/nlohmann/json/pull/1551) ([heavywatal](https://github.com/heavywatal)) +- moved from Catch to doctest for unit tests [\#1439](https://github.com/nlohmann/json/pull/1439) ([onqtam](https://github.com/onqtam)) + +## [v3.6.1](https://github.com/nlohmann/json/releases/tag/v3.6.1) (2019-03-20) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.6.0...v3.6.1) + +- Failed to build with \ [\#1531](https://github.com/nlohmann/json/issues/1531) +- Compiling 3.6.0 with GCC \> 7, array vs std::array \#590 is back [\#1530](https://github.com/nlohmann/json/issues/1530) +- 3.6.0: warning: missing initializer for member 'std::array\::\_M\_elems' \[-Wmissing-field-initializers\] [\#1527](https://github.com/nlohmann/json/issues/1527) +- unable to parse json [\#1525](https://github.com/nlohmann/json/issues/1525) + +## [v3.6.0](https://github.com/nlohmann/json/releases/tag/v3.6.0) (2019-03-19) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.5.0...v3.6.0) + +- How can I turn a string of a json array into a json array? [\#1526](https://github.com/nlohmann/json/issues/1526) +- Minor: missing a std:: namespace tag [\#1521](https://github.com/nlohmann/json/issues/1521) +- how to precision to four decimal for double when use to\_json [\#1519](https://github.com/nlohmann/json/issues/1519) +- error parse [\#1518](https://github.com/nlohmann/json/issues/1518) +- Compile error: template argument deduction/substitution failed [\#1515](https://github.com/nlohmann/json/issues/1515) +- Support for Comments [\#1513](https://github.com/nlohmann/json/issues/1513) +- std::complex type [\#1510](https://github.com/nlohmann/json/issues/1510) +- CBOR byte string support [\#1509](https://github.com/nlohmann/json/issues/1509) +- Compilation error getting a std::pair\<\> on latest VS 2017 compiler [\#1506](https://github.com/nlohmann/json/issues/1506) +- "Integration" section of documentation needs update? [\#1505](https://github.com/nlohmann/json/issues/1505) +- Json object from string from a TCP socket [\#1504](https://github.com/nlohmann/json/issues/1504) +- MSVC warning C4946 \("reinterpret\_cast used between related classes"\) compiling json.hpp [\#1502](https://github.com/nlohmann/json/issues/1502) +- How to programmatically fill an n-th dimensional JSON object? [\#1501](https://github.com/nlohmann/json/issues/1501) +- Error compiling with clang and `JSON\_NOEXCEPTION`: need to include `cstdlib` [\#1500](https://github.com/nlohmann/json/issues/1500) +- The code compiles unsuccessfully with android-ndk-r10e [\#1499](https://github.com/nlohmann/json/issues/1499) +- Cmake 3.1 in develop, when is it likely to make it into a stable release? [\#1498](https://github.com/nlohmann/json/issues/1498) +- Repository is almost 450MB [\#1497](https://github.com/nlohmann/json/issues/1497) +- Some Help please object inside array [\#1494](https://github.com/nlohmann/json/issues/1494) +- How to get data into vector of user-defined type from a Json object [\#1493](https://github.com/nlohmann/json/issues/1493) +- how to find subelement without loop [\#1490](https://github.com/nlohmann/json/issues/1490) +- json to std::map [\#1487](https://github.com/nlohmann/json/issues/1487) +- Type in README.md [\#1486](https://github.com/nlohmann/json/issues/1486) +- Error in parsing and reading msgpack-lite [\#1485](https://github.com/nlohmann/json/issues/1485) +- Compiling issues with libc 2.12 [\#1483](https://github.com/nlohmann/json/issues/1483) +- How do I use reference or pointer binding values? [\#1482](https://github.com/nlohmann/json/issues/1482) +- Compilation fails in MSVC with the Microsoft Language Extensions disabled [\#1481](https://github.com/nlohmann/json/issues/1481) +- Functional visit [\#1480](https://github.com/nlohmann/json/issues/1480) +- \[Question\] Unescaped dump [\#1479](https://github.com/nlohmann/json/issues/1479) +- Some Help please [\#1478](https://github.com/nlohmann/json/issues/1478) +- Global variables are stored within the JSON file, how do I declare them as global variables when I read them out in my C++ program? [\#1476](https://github.com/nlohmann/json/issues/1476) +- Unable to modify one of the values within the JSON file, and save it [\#1475](https://github.com/nlohmann/json/issues/1475) +- Documentation of parse function has two identical @pre causes [\#1473](https://github.com/nlohmann/json/issues/1473) +- GCC 9.0 build failure [\#1472](https://github.com/nlohmann/json/issues/1472) +- Can we have an `exists\(\)` method? [\#1471](https://github.com/nlohmann/json/issues/1471) +- How to parse multi object json from file? [\#1470](https://github.com/nlohmann/json/issues/1470) +- How to returns the name of the upper object? [\#1467](https://github.com/nlohmann/json/issues/1467) +- Error: "tuple\_size" has already been declared in the current scope [\#1466](https://github.com/nlohmann/json/issues/1466) +- Checking keys of two jsons against eachother [\#1465](https://github.com/nlohmann/json/issues/1465) +- Disable installation when used as meson subproject [\#1463](https://github.com/nlohmann/json/issues/1463) +- Unpack list of integers to a std::vector\ [\#1460](https://github.com/nlohmann/json/issues/1460) +- Implement DRY definition of JSON representation of a c++ class [\#1459](https://github.com/nlohmann/json/issues/1459) +- json.exception.type\_error.305 with GCC 4.9 when using C++ {} initializer [\#1458](https://github.com/nlohmann/json/issues/1458) +- API to convert an "uninitialized" json into an empty object or empty array [\#1456](https://github.com/nlohmann/json/issues/1456) +- How to parse a vector of objects with const attributes [\#1453](https://github.com/nlohmann/json/issues/1453) +- NLOHMANN\_JSON\_SERIALIZE\_ENUM potentially requires duplicate definitions [\#1450](https://github.com/nlohmann/json/issues/1450) +- Question about making json object from file directory [\#1449](https://github.com/nlohmann/json/issues/1449) +- .get\(\) throws error if used with userdefined structs in unordered\_map [\#1448](https://github.com/nlohmann/json/issues/1448) +- Integer Overflow \(OSS-Fuzz 12506\) [\#1447](https://github.com/nlohmann/json/issues/1447) +- If a string has too many invalid UTF-8 characters, json::dump attempts to index an array out of bounds. [\#1445](https://github.com/nlohmann/json/issues/1445) +- Setting values of .JSON file [\#1444](https://github.com/nlohmann/json/issues/1444) +- alias object\_t::key\_type in basic\_json [\#1442](https://github.com/nlohmann/json/issues/1442) +- Latest Ubuntu package is 2.1.1 [\#1438](https://github.com/nlohmann/json/issues/1438) +- lexer.hpp\(1363\) '\_snprintf': is not a member | Visualstudio 2017 [\#1437](https://github.com/nlohmann/json/issues/1437) +- Static method invites inadvertent logic error. [\#1433](https://github.com/nlohmann/json/issues/1433) +- EOS compilation produces "fatal error: 'nlohmann/json.hpp' file not found" [\#1432](https://github.com/nlohmann/json/issues/1432) +- Support for bad commas [\#1429](https://github.com/nlohmann/json/issues/1429) +- Please have one base exception class for all json exceptions [\#1427](https://github.com/nlohmann/json/issues/1427) +- Compilation warning: 'tuple\_size' defined as a class template here but previously declared as a struct template [\#1426](https://github.com/nlohmann/json/issues/1426) +- Which version can be used with GCC 4.8.2 ? [\#1424](https://github.com/nlohmann/json/issues/1424) +- Ignore nullptr values on constructing json object from a container [\#1422](https://github.com/nlohmann/json/issues/1422) +- Support for custom float precision via unquoted strings [\#1421](https://github.com/nlohmann/json/issues/1421) +- Segmentation fault \(stack overflow\) due to unbounded recursion [\#1419](https://github.com/nlohmann/json/issues/1419) +- It is possible to call `json::find` with a json\_pointer as argument. This causes runtime UB/crash. [\#1418](https://github.com/nlohmann/json/issues/1418) +- Dump throwing exception [\#1416](https://github.com/nlohmann/json/issues/1416) +- Build error [\#1415](https://github.com/nlohmann/json/issues/1415) +- Append version to include.zip [\#1412](https://github.com/nlohmann/json/issues/1412) +- error C2039: '\_snprintf': is not a member of 'std' - Windows [\#1408](https://github.com/nlohmann/json/issues/1408) +- Deserializing to vector [\#1407](https://github.com/nlohmann/json/issues/1407) +- Efficient way to set a `json` object as value into another `json` key [\#1406](https://github.com/nlohmann/json/issues/1406) +- Document return value of parse\(\) when allow\_exceptions == false and parsing fails [\#1405](https://github.com/nlohmann/json/issues/1405) +- Unexpected behaviour with structured binding [\#1404](https://github.com/nlohmann/json/issues/1404) +- Which native types does get\\(\) allow? [\#1403](https://github.com/nlohmann/json/issues/1403) +- Add something like Json::StaticString [\#1402](https://github.com/nlohmann/json/issues/1402) +- -Wmismatched-tags in 3.5.0? [\#1401](https://github.com/nlohmann/json/issues/1401) +- Coverity Scan reports an UNCAUGHT\_EXCEPT issue [\#1400](https://github.com/nlohmann/json/issues/1400) +- fff [\#1399](https://github.com/nlohmann/json/issues/1399) +- sorry this is not an issue, just a Question, How to change a key value in a file and save it ? [\#1398](https://github.com/nlohmann/json/issues/1398) +- Add library versioning using inline namespaces [\#1394](https://github.com/nlohmann/json/issues/1394) +- appveyor x64 builds appear to be using Win32 toolset [\#1374](https://github.com/nlohmann/json/issues/1374) +- Serializing/Deserializing a Class containing a vector of itself [\#1373](https://github.com/nlohmann/json/issues/1373) +- Retrieving array elements. [\#1369](https://github.com/nlohmann/json/issues/1369) +- Deserialize [\#1366](https://github.com/nlohmann/json/issues/1366) +- call of overloaded for push\_back and operator+= is ambiguous [\#1352](https://github.com/nlohmann/json/issues/1352) +- got an error and cann't figure it out [\#1351](https://github.com/nlohmann/json/issues/1351) +- Improve number-to-string conversion [\#1334](https://github.com/nlohmann/json/issues/1334) +- Implicit type conversion error on MSVC [\#1333](https://github.com/nlohmann/json/issues/1333) +- NuGet Package [\#1132](https://github.com/nlohmann/json/issues/1132) + +- Change macros to numeric\_limits [\#1514](https://github.com/nlohmann/json/pull/1514) ([naszta](https://github.com/naszta)) +- fix GCC 7.1.1 - 7.2.1 on CentOS [\#1496](https://github.com/nlohmann/json/pull/1496) ([lieff](https://github.com/lieff)) +- Update Buckaroo instructions in README.md [\#1495](https://github.com/nlohmann/json/pull/1495) ([njlr](https://github.com/njlr)) +- Fix gcc9 build error test/src/unit-allocator.cpp \(Issue \#1472\) [\#1492](https://github.com/nlohmann/json/pull/1492) ([stac47](https://github.com/stac47)) +- Fix typo in README.md [\#1491](https://github.com/nlohmann/json/pull/1491) ([nickaein](https://github.com/nickaein)) +- Do proper endian conversions [\#1489](https://github.com/nlohmann/json/pull/1489) ([andreas-schwab](https://github.com/andreas-schwab)) +- Fix documentation [\#1477](https://github.com/nlohmann/json/pull/1477) ([nickaein](https://github.com/nickaein)) +- Implement contains\(\) member function [\#1474](https://github.com/nlohmann/json/pull/1474) ([nickaein](https://github.com/nickaein)) +- Add operator/= and operator/ to construct a JSON pointer by appending two JSON pointers [\#1469](https://github.com/nlohmann/json/pull/1469) ([garethsb-sony](https://github.com/garethsb-sony)) +- Disable Clang -Wmismatched-tags warning on tuple\_size / tuple\_element [\#1468](https://github.com/nlohmann/json/pull/1468) ([past-due](https://github.com/past-due)) +- Disable installation when used as meson subproject. \#1463 [\#1464](https://github.com/nlohmann/json/pull/1464) ([elvisoric](https://github.com/elvisoric)) +- docs: README typo [\#1455](https://github.com/nlohmann/json/pull/1455) ([wythe](https://github.com/wythe)) +- remove extra semicolon from readme [\#1451](https://github.com/nlohmann/json/pull/1451) ([Afforix](https://github.com/Afforix)) +- attempt to fix \#1445, flush buffer in serializer::dump\_escaped in UTF8\_REJECT case. [\#1446](https://github.com/nlohmann/json/pull/1446) ([scinart](https://github.com/scinart)) +- Use C++11 features supported by CMake 3.1. [\#1441](https://github.com/nlohmann/json/pull/1441) ([iwanders](https://github.com/iwanders)) +- :rotating\_light: fixed unused variable warning [\#1435](https://github.com/nlohmann/json/pull/1435) ([pboettch](https://github.com/pboettch)) +- allow push\_back\(\) and pop\_back\(\) calls on json\_pointer [\#1434](https://github.com/nlohmann/json/pull/1434) ([pboettch](https://github.com/pboettch)) +- Add instructions about using nlohmann/json with the conda package manager [\#1430](https://github.com/nlohmann/json/pull/1430) ([nicoddemus](https://github.com/nicoddemus)) +- Updated year in README.md [\#1425](https://github.com/nlohmann/json/pull/1425) ([hijxf](https://github.com/hijxf)) +- Fixed broken links in the README file [\#1423](https://github.com/nlohmann/json/pull/1423) ([skypjack](https://github.com/skypjack)) +- Fixed broken links in the README file [\#1420](https://github.com/nlohmann/json/pull/1420) ([skypjack](https://github.com/skypjack)) +- docs: typo in README [\#1417](https://github.com/nlohmann/json/pull/1417) ([wythe](https://github.com/wythe)) +- Fix x64 target platform for appveyor [\#1414](https://github.com/nlohmann/json/pull/1414) ([nickaein](https://github.com/nickaein)) +- Improve dump\_integer performance [\#1411](https://github.com/nlohmann/json/pull/1411) ([nickaein](https://github.com/nickaein)) +- buildsystem: relax requirement on cmake version [\#1409](https://github.com/nlohmann/json/pull/1409) ([yann-morin-1998](https://github.com/yann-morin-1998)) +- CMake: Optional Install if Embedded [\#1330](https://github.com/nlohmann/json/pull/1330) ([ax3l](https://github.com/ax3l)) + +## [v3.5.0](https://github.com/nlohmann/json/releases/tag/v3.5.0) (2018-12-21) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.4.0...v3.5.0) + +- Copyconstructor inserts original into array with single element [\#1397](https://github.com/nlohmann/json/issues/1397) +- Get value without explicit typecasting [\#1395](https://github.com/nlohmann/json/issues/1395) +- Big file parsing [\#1393](https://github.com/nlohmann/json/issues/1393) +- some static analysis warning at line 11317 [\#1390](https://github.com/nlohmann/json/issues/1390) +- Adding Structured Binding Support [\#1388](https://github.com/nlohmann/json/issues/1388) +- map\ exhibits unexpected behavior [\#1387](https://github.com/nlohmann/json/issues/1387) +- Error Code Return [\#1386](https://github.com/nlohmann/json/issues/1386) +- using unordered\_map as object type [\#1385](https://github.com/nlohmann/json/issues/1385) +- float precision [\#1384](https://github.com/nlohmann/json/issues/1384) +- \[json.exception.type\_error.316\] invalid UTF-8 byte at index 1: 0xC3 [\#1383](https://github.com/nlohmann/json/issues/1383) +- Inconsistent Constructor \(GCC vs. Clang\) [\#1381](https://github.com/nlohmann/json/issues/1381) +- \#define or || [\#1379](https://github.com/nlohmann/json/issues/1379) +- How to iterate inside the values ? [\#1377](https://github.com/nlohmann/json/issues/1377) +- items\(\) unable to get the elements [\#1375](https://github.com/nlohmann/json/issues/1375) +- conversion json to std::map doesn't work for types \ [\#1372](https://github.com/nlohmann/json/issues/1372) +- A minor issue in the build instructions [\#1371](https://github.com/nlohmann/json/issues/1371) +- Using this library without stream ? [\#1370](https://github.com/nlohmann/json/issues/1370) +- Writing and reading BSON data [\#1368](https://github.com/nlohmann/json/issues/1368) +- Retrieving array elements from object type iterator. [\#1367](https://github.com/nlohmann/json/issues/1367) +- json::dump\(\) silently crashes if items contain accented letters [\#1365](https://github.com/nlohmann/json/issues/1365) +- warnings in MSVC \(2015\) in 3.4.0 related to bool... [\#1364](https://github.com/nlohmann/json/issues/1364) +- Cant compile with -C++17 and beyond compiler options [\#1362](https://github.com/nlohmann/json/issues/1362) +- json to concrete type conversion through reference or pointer fails [\#1361](https://github.com/nlohmann/json/issues/1361) +- the first attributes of JSON string is misplaced [\#1360](https://github.com/nlohmann/json/issues/1360) +- Copy-construct using initializer-list converts objects to arrays [\#1359](https://github.com/nlohmann/json/issues/1359) +- About value\(key, default\_value\) and operator\[\]\(key\) [\#1358](https://github.com/nlohmann/json/issues/1358) +- Problem with printing json response object [\#1356](https://github.com/nlohmann/json/issues/1356) +- Serializing pointer segfaults [\#1355](https://github.com/nlohmann/json/issues/1355) +- Read `long long int` data as a number. [\#1354](https://github.com/nlohmann/json/issues/1354) +- eclipse oxygen in ubuntu get\ is ambiguous [\#1353](https://github.com/nlohmann/json/issues/1353) +- Can't build on Visual Studio 2017 v15.8.9 [\#1350](https://github.com/nlohmann/json/issues/1350) +- cannot parse from string? [\#1349](https://github.com/nlohmann/json/issues/1349) +- Error: out\_of\_range [\#1348](https://github.com/nlohmann/json/issues/1348) +- expansion pattern 'CompatibleObjectType' contains no argument packs, with CUDA 10 [\#1347](https://github.com/nlohmann/json/issues/1347) +- Unable to update a value for a nested\(multi-level\) json file [\#1344](https://github.com/nlohmann/json/issues/1344) +- Fails to compile when std::iterator\_traits is not SFINAE friendly. [\#1341](https://github.com/nlohmann/json/issues/1341) +- EOF flag not set on exhausted input streams. [\#1340](https://github.com/nlohmann/json/issues/1340) +- Shadowed Member in merge\_patch [\#1339](https://github.com/nlohmann/json/issues/1339) +- Periods/literal dots in keys? [\#1338](https://github.com/nlohmann/json/issues/1338) +- Protect macro expansion of commonly defined macros [\#1337](https://github.com/nlohmann/json/issues/1337) +- How to validate an input before parsing? [\#1336](https://github.com/nlohmann/json/issues/1336) +- Non-verifying dump\(\) alternative for debugging/logging needed [\#1335](https://github.com/nlohmann/json/issues/1335) +- Json Libarary is not responding for me in c++ [\#1332](https://github.com/nlohmann/json/issues/1332) +- Question - how to find an object in an array [\#1331](https://github.com/nlohmann/json/issues/1331) +- Nesting additional data in json object [\#1328](https://github.com/nlohmann/json/issues/1328) +- can to\_json\(\) be defined inside a class? [\#1324](https://github.com/nlohmann/json/issues/1324) +- CodeBlocks IDE can't find `json.hpp` header [\#1318](https://github.com/nlohmann/json/issues/1318) +- Change json\_pointer to provide an iterator begin/end/etc, don't use vectors, and also enable string\_view [\#1312](https://github.com/nlohmann/json/issues/1312) +- Xcode - adding it to library [\#1300](https://github.com/nlohmann/json/issues/1300) +- unicode: accept char16\_t, char32\_t sequences [\#1298](https://github.com/nlohmann/json/issues/1298) +- unicode: char16\_t\* is compiler error, but char16\_t\[\] is accepted [\#1297](https://github.com/nlohmann/json/issues/1297) +- Dockerfile Project Help Needed [\#1296](https://github.com/nlohmann/json/issues/1296) +- Comparisons between large unsigned and negative signed integers [\#1295](https://github.com/nlohmann/json/issues/1295) +- CMake alias to `nlohmann::json` [\#1291](https://github.com/nlohmann/json/issues/1291) +- Release zips without tests [\#1285](https://github.com/nlohmann/json/issues/1285) +- Suggestion to improve value\(\) accessors with respect to move semantics [\#1275](https://github.com/nlohmann/json/issues/1275) +- separate object\_t::key\_type from basic\_json::key\_type, and use an allocator which returns object\_t::key\_type [\#1274](https://github.com/nlohmann/json/issues/1274) +- Is there a nice way to associate external values with json elements? [\#1256](https://github.com/nlohmann/json/issues/1256) +- Delete by json\_pointer [\#1248](https://github.com/nlohmann/json/issues/1248) +- Expose lexer, as a StAX parser [\#1219](https://github.com/nlohmann/json/issues/1219) +- Subclassing json\(\) & error on recursive load [\#1201](https://github.com/nlohmann/json/issues/1201) +- Check value for existence by json\_pointer [\#1194](https://github.com/nlohmann/json/issues/1194) + +- Feature/add file input adapter [\#1392](https://github.com/nlohmann/json/pull/1392) ([dumarjo](https://github.com/dumarjo)) +- Added Support for Structured Bindings [\#1391](https://github.com/nlohmann/json/pull/1391) ([pratikpc](https://github.com/pratikpc)) +- Link to issue \#958 broken [\#1382](https://github.com/nlohmann/json/pull/1382) ([kjpus](https://github.com/kjpus)) +- readme: fix typo [\#1380](https://github.com/nlohmann/json/pull/1380) ([manu-chroma](https://github.com/manu-chroma)) +- recommend using explicit from JSON conversions [\#1363](https://github.com/nlohmann/json/pull/1363) ([theodelrieu](https://github.com/theodelrieu)) +- Fix merge\_patch shadow warning [\#1346](https://github.com/nlohmann/json/pull/1346) ([ax3l](https://github.com/ax3l)) +- Allow installation via Meson [\#1345](https://github.com/nlohmann/json/pull/1345) ([mpoquet](https://github.com/mpoquet)) +- Set eofbit on exhausted input stream. [\#1343](https://github.com/nlohmann/json/pull/1343) ([mefyl](https://github.com/mefyl)) +- Add a SFINAE friendly iterator\_traits and use that instead. [\#1342](https://github.com/nlohmann/json/pull/1342) ([dgavedissian](https://github.com/dgavedissian)) +- Fix EOL Whitespaces & CMake Spelling [\#1329](https://github.com/nlohmann/json/pull/1329) ([ax3l](https://github.com/ax3l)) + +## [v3.4.0](https://github.com/nlohmann/json/releases/tag/v3.4.0) (2018-10-30) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.3.0...v3.4.0) + +- Big uint64\_t values are serialized wrong [\#1327](https://github.com/nlohmann/json/issues/1327) +- \[Question\] Efficient check for equivalency? [\#1325](https://github.com/nlohmann/json/issues/1325) +- Can't use ifstream and .clear\(\) [\#1321](https://github.com/nlohmann/json/issues/1321) +- \[Warning\] -Wparentheses on line 555 on single\_include [\#1319](https://github.com/nlohmann/json/issues/1319) +- Compilation error using at and find with enum struct [\#1316](https://github.com/nlohmann/json/issues/1316) +- Parsing JSON from a web address [\#1311](https://github.com/nlohmann/json/issues/1311) +- How to convert JSON to Struct with embeded subject [\#1310](https://github.com/nlohmann/json/issues/1310) +- Null safety/coalescing function? [\#1309](https://github.com/nlohmann/json/issues/1309) +- Building fails using single include file: json.hpp [\#1308](https://github.com/nlohmann/json/issues/1308) +- json::parse\(std::string\) Exception inside packaged Lib [\#1306](https://github.com/nlohmann/json/issues/1306) +- Problem in Dockerfile with installation of library [\#1304](https://github.com/nlohmann/json/issues/1304) +- compile error in from\_json converting to container with std::pair [\#1299](https://github.com/nlohmann/json/issues/1299) +- Json that I am trying to parse, and I am lost Structure Array below top level [\#1293](https://github.com/nlohmann/json/issues/1293) +- Serializing std::variant causes stack overflow [\#1292](https://github.com/nlohmann/json/issues/1292) +- How do I go about customising from\_json to support \_\_int128\_t/\_\_uint128\_t? [\#1290](https://github.com/nlohmann/json/issues/1290) +- merge\_patch: inconsistent behaviour merging empty sub-object [\#1289](https://github.com/nlohmann/json/issues/1289) +- Buffer over/underrun using UBJson? [\#1288](https://github.com/nlohmann/json/issues/1288) +- Enable the latest C++ standard with Visual Studio [\#1287](https://github.com/nlohmann/json/issues/1287) +- truncation of constant value in to\_cbor\(\) [\#1286](https://github.com/nlohmann/json/issues/1286) +- eosio.wasmsdk error [\#1284](https://github.com/nlohmann/json/issues/1284) +- use the same interface for writing arrays and non-arrays [\#1283](https://github.com/nlohmann/json/issues/1283) +- How to read json file with optional entries and entries with different types [\#1281](https://github.com/nlohmann/json/issues/1281) +- merge result not as espected [\#1279](https://github.com/nlohmann/json/issues/1279) +- how to get only "name" from below json [\#1278](https://github.com/nlohmann/json/issues/1278) +- syntax error on right json string [\#1276](https://github.com/nlohmann/json/issues/1276) +- Parsing JSON Array where members have no key, using custom types [\#1267](https://github.com/nlohmann/json/issues/1267) +- I get a json exception periodically from json::parse for the same json [\#1263](https://github.com/nlohmann/json/issues/1263) +- serialize std::variant\<...\> [\#1261](https://github.com/nlohmann/json/issues/1261) +- GCC 8.2.1. Compilation error: invalid conversion from... [\#1246](https://github.com/nlohmann/json/issues/1246) +- BSON support [\#1244](https://github.com/nlohmann/json/issues/1244) +- enum to json mapping [\#1208](https://github.com/nlohmann/json/issues/1208) +- Soften the landing when dumping non-UTF8 strings \(type\_error.316 exception\) [\#1198](https://github.com/nlohmann/json/issues/1198) +- CMakeLists.txt in release zips? [\#1184](https://github.com/nlohmann/json/issues/1184) + +- Add macro to define enum/JSON mapping [\#1323](https://github.com/nlohmann/json/pull/1323) ([nlohmann](https://github.com/nlohmann)) +- Add BSON support [\#1320](https://github.com/nlohmann/json/pull/1320) ([nlohmann](https://github.com/nlohmann)) +- Properly convert constants to CharType [\#1315](https://github.com/nlohmann/json/pull/1315) ([nlohmann](https://github.com/nlohmann)) +- Allow to set error handler for decoding errors [\#1314](https://github.com/nlohmann/json/pull/1314) ([nlohmann](https://github.com/nlohmann)) +- Add Meson related info to README [\#1305](https://github.com/nlohmann/json/pull/1305) ([koponomarenko](https://github.com/koponomarenko)) +- Improve diagnostic messages for binary formats [\#1303](https://github.com/nlohmann/json/pull/1303) ([nlohmann](https://github.com/nlohmann)) +- add new is\_constructible\_\* traits used in from\_json [\#1301](https://github.com/nlohmann/json/pull/1301) ([theodelrieu](https://github.com/theodelrieu)) +- add constraints for variadic json\_ref constructors [\#1294](https://github.com/nlohmann/json/pull/1294) ([theodelrieu](https://github.com/theodelrieu)) +- Improve diagnostic messages [\#1282](https://github.com/nlohmann/json/pull/1282) ([nlohmann](https://github.com/nlohmann)) +- Removed linter warnings [\#1280](https://github.com/nlohmann/json/pull/1280) ([nlohmann](https://github.com/nlohmann)) +- Thirdparty benchmark: Fix Clang detection. [\#1277](https://github.com/nlohmann/json/pull/1277) ([Lord-Kamina](https://github.com/Lord-Kamina)) + +## [v3.3.0](https://github.com/nlohmann/json/releases/tag/v3.3.0) (2018-10-05) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.2.0...v3.3.0) + +- When key is not found print the key name into error too [\#1273](https://github.com/nlohmann/json/issues/1273) +- Visual Studio 2017 15.8.5 "conditional expression is constant" warning on Line 1851 in json.hpp [\#1268](https://github.com/nlohmann/json/issues/1268) +- how can we get this working on WSL? [\#1264](https://github.com/nlohmann/json/issues/1264) +- Help needed [\#1259](https://github.com/nlohmann/json/issues/1259) +- A way to get to a JSON values "key" [\#1258](https://github.com/nlohmann/json/issues/1258) +- While compiling got 76 errors [\#1255](https://github.com/nlohmann/json/issues/1255) +- Two blackslashes on json output file [\#1253](https://github.com/nlohmann/json/issues/1253) +- Including nlohmann the badwrong way. [\#1250](https://github.com/nlohmann/json/issues/1250) +- how to build with clang? [\#1247](https://github.com/nlohmann/json/issues/1247) +- Cmake target\_link\_libraries unable to find nlohmann\_json since version 3.2.0 [\#1243](https://github.com/nlohmann/json/issues/1243) +- \[Question\] Access to end\(\) iterator reference [\#1242](https://github.com/nlohmann/json/issues/1242) +- Parsing different json format [\#1241](https://github.com/nlohmann/json/issues/1241) +- Parsing Multiple JSON Files [\#1240](https://github.com/nlohmann/json/issues/1240) +- Doesn't compile under C++17 [\#1239](https://github.com/nlohmann/json/issues/1239) +- Conversion operator for nlohmann::json is not SFINAE friendly [\#1237](https://github.com/nlohmann/json/issues/1237) +- Custom deserialization of number\_float\_t [\#1236](https://github.com/nlohmann/json/issues/1236) +- Move tests to a separate repo [\#1235](https://github.com/nlohmann/json/issues/1235) +- deprecated-declarations warnings when compiling tests with GCC 8.2.1. [\#1233](https://github.com/nlohmann/json/issues/1233) +- Incomplete type with json\_fwd.hpp [\#1232](https://github.com/nlohmann/json/issues/1232) +- Parse Error [\#1229](https://github.com/nlohmann/json/issues/1229) +- json::get function with argument [\#1227](https://github.com/nlohmann/json/issues/1227) +- questions regarding from\_json [\#1226](https://github.com/nlohmann/json/issues/1226) +- Lambda in unevaluated context [\#1225](https://github.com/nlohmann/json/issues/1225) +- NLohmann doesn't compile when enabling strict warning policies [\#1224](https://github.com/nlohmann/json/issues/1224) +- Creating array of objects [\#1223](https://github.com/nlohmann/json/issues/1223) +- Somewhat unhelpful error message "cannot use operator\[\] with object" [\#1220](https://github.com/nlohmann/json/issues/1220) +- single\_include json.hpp [\#1218](https://github.com/nlohmann/json/issues/1218) +- Maps with enum class keys which are convertible to JSON strings should be converted to JSON dictionaries [\#1217](https://github.com/nlohmann/json/issues/1217) +- Adding JSON Array to the Array [\#1216](https://github.com/nlohmann/json/issues/1216) +- Best way to output a vector of a given type to json [\#1215](https://github.com/nlohmann/json/issues/1215) +- compiler warning: double definition of macro JSON\_INTERNAL\_CATCH [\#1213](https://github.com/nlohmann/json/issues/1213) +- Compilation error when using MOCK\_METHOD1 from GMock and nlohmann::json [\#1212](https://github.com/nlohmann/json/issues/1212) +- Issues parsing a previously encoded binary \(non-UTF8\) string. [\#1211](https://github.com/nlohmann/json/issues/1211) +- Yet another ordering question: char \* and parse\(\) [\#1209](https://github.com/nlohmann/json/issues/1209) +- Error using gcc 8.1.0 on Ubuntu 14.04 [\#1207](https://github.com/nlohmann/json/issues/1207) +- "type must be string, but is " std::string\(j.type\_name\(\) [\#1206](https://github.com/nlohmann/json/issues/1206) +- Returning empty json object from a function of type const json& ? [\#1205](https://github.com/nlohmann/json/issues/1205) +- VS2017 compiler suggests using constexpr if [\#1204](https://github.com/nlohmann/json/issues/1204) +- Template instatiation error on compiling [\#1203](https://github.com/nlohmann/json/issues/1203) +- BUG - json dump field with unicode -\> array of ints \(instead of string\) [\#1197](https://github.com/nlohmann/json/issues/1197) +- Compile error using Code::Blocks // mingw-w64 GCC 8.1.0 - "Incomplete Type" [\#1193](https://github.com/nlohmann/json/issues/1193) +- SEGFAULT on arm target [\#1190](https://github.com/nlohmann/json/issues/1190) +- Compiler crash with old Clang [\#1179](https://github.com/nlohmann/json/issues/1179) +- Custom Precision on floating point numbers [\#1170](https://github.com/nlohmann/json/issues/1170) +- Can we have a json\_view class like std::string\_view? [\#1158](https://github.com/nlohmann/json/issues/1158) +- improve error handling [\#1152](https://github.com/nlohmann/json/issues/1152) +- We should remove static\_asserts [\#960](https://github.com/nlohmann/json/issues/960) + +- Fix warning C4127: conditional expression is constant [\#1272](https://github.com/nlohmann/json/pull/1272) ([antonioborondo](https://github.com/antonioborondo)) +- Turn off additional deprecation warnings for GCC. [\#1271](https://github.com/nlohmann/json/pull/1271) ([chuckatkins](https://github.com/chuckatkins)) +- docs: Add additional CMake documentation [\#1270](https://github.com/nlohmann/json/pull/1270) ([chuckatkins](https://github.com/chuckatkins)) +- unit-testsuites.cpp: fix hangup if file not found [\#1262](https://github.com/nlohmann/json/pull/1262) ([knilch0r](https://github.com/knilch0r)) +- Fix broken cmake imported target alias [\#1260](https://github.com/nlohmann/json/pull/1260) ([chuckatkins](https://github.com/chuckatkins)) +- GCC 48 [\#1257](https://github.com/nlohmann/json/pull/1257) ([henryiii](https://github.com/henryiii)) +- Add version and license to meson.build [\#1252](https://github.com/nlohmann/json/pull/1252) ([koponomarenko](https://github.com/koponomarenko)) +- \#1179 Reordered the code. It seems to stop clang 3.4.2 in RHEL 7 from crash… [\#1249](https://github.com/nlohmann/json/pull/1249) ([LEgregius](https://github.com/LEgregius)) +- Use a version check to provide backwards comatible CMake imported target names [\#1245](https://github.com/nlohmann/json/pull/1245) ([chuckatkins](https://github.com/chuckatkins)) +- Fix issue \#1237 [\#1238](https://github.com/nlohmann/json/pull/1238) ([theodelrieu](https://github.com/theodelrieu)) +- Add a get overload taking a parameter. [\#1231](https://github.com/nlohmann/json/pull/1231) ([theodelrieu](https://github.com/theodelrieu)) +- Move lambda out of unevaluated context [\#1230](https://github.com/nlohmann/json/pull/1230) ([mandreyel](https://github.com/mandreyel)) +- Remove static asserts [\#1228](https://github.com/nlohmann/json/pull/1228) ([theodelrieu](https://github.com/theodelrieu)) +- Better error 305 [\#1221](https://github.com/nlohmann/json/pull/1221) ([rivertam](https://github.com/rivertam)) +- Fix \#1213 [\#1214](https://github.com/nlohmann/json/pull/1214) ([simnalamburt](https://github.com/simnalamburt)) +- Export package to allow builds without installing [\#1202](https://github.com/nlohmann/json/pull/1202) ([dennisfischer](https://github.com/dennisfischer)) + +## [v3.2.0](https://github.com/nlohmann/json/releases/tag/v3.2.0) (2018-08-20) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.1.2...v3.2.0) + +- Am I doing this wrong? Getting an empty string [\#1199](https://github.com/nlohmann/json/issues/1199) +- Incompatible Pointer Type [\#1196](https://github.com/nlohmann/json/issues/1196) +- json.exception.type\_error.316 [\#1195](https://github.com/nlohmann/json/issues/1195) +- Strange warnings in Code::Blocks 17.12, GNU GCC [\#1192](https://github.com/nlohmann/json/issues/1192) +- \[Question\] Current place in code to change floating point resolution [\#1191](https://github.com/nlohmann/json/issues/1191) +- Add key name when throwing type error [\#1189](https://github.com/nlohmann/json/issues/1189) +- Not able to include in visual studio code? [\#1188](https://github.com/nlohmann/json/issues/1188) +- Get an Index or row number of an element [\#1186](https://github.com/nlohmann/json/issues/1186) +- reduce repos size [\#1185](https://github.com/nlohmann/json/issues/1185) +- Difference between `merge\_patch` and `update` [\#1183](https://github.com/nlohmann/json/issues/1183) +- Is there a way to get an element from a JSON without throwing an exception on failure? [\#1182](https://github.com/nlohmann/json/issues/1182) +- to\_string? [\#1181](https://github.com/nlohmann/json/issues/1181) +- How to cache a json object's pointer into a map? [\#1180](https://github.com/nlohmann/json/issues/1180) +- Can this library work within a Qt project for Android using Qt Creator? [\#1178](https://github.com/nlohmann/json/issues/1178) +- How to get all keys of one object? [\#1177](https://github.com/nlohmann/json/issues/1177) +- How can I only parse the first level and get the value as string? [\#1175](https://github.com/nlohmann/json/issues/1175) +- I have a query regarding nlohmann::basic\_json::basic\_json [\#1174](https://github.com/nlohmann/json/issues/1174) +- unordered\_map with vectors won't convert to json? [\#1173](https://github.com/nlohmann/json/issues/1173) +- return json objects from functions [\#1172](https://github.com/nlohmann/json/issues/1172) +- Problem when exporting to CBOR [\#1171](https://github.com/nlohmann/json/issues/1171) +- Roundtripping null to nullptr does not work [\#1169](https://github.com/nlohmann/json/issues/1169) +- MSVC fails to compile std::swap specialization for nlohmann::json [\#1168](https://github.com/nlohmann/json/issues/1168) +- Unexpected behaviour of is\_null - Part II [\#1167](https://github.com/nlohmann/json/issues/1167) +- Floating point imprecision [\#1166](https://github.com/nlohmann/json/issues/1166) +- Combine json objects into one? [\#1165](https://github.com/nlohmann/json/issues/1165) +- Is there any way to know if the object has changed? [\#1164](https://github.com/nlohmann/json/issues/1164) +- Value throws on null string [\#1163](https://github.com/nlohmann/json/issues/1163) +- Weird template issue in large project [\#1162](https://github.com/nlohmann/json/issues/1162) +- \_json returns a different result vs ::parse [\#1161](https://github.com/nlohmann/json/issues/1161) +- Showing difference between two json objects [\#1160](https://github.com/nlohmann/json/issues/1160) +- no instance of overloaded function "std::swap" matches the specified type [\#1159](https://github.com/nlohmann/json/issues/1159) +- resize\(...\)? [\#1157](https://github.com/nlohmann/json/issues/1157) +- Issue with struct nested in class' to\_json [\#1155](https://github.com/nlohmann/json/issues/1155) +- Deserialize std::map with std::nan [\#1154](https://github.com/nlohmann/json/issues/1154) +- Parse throwing errors [\#1149](https://github.com/nlohmann/json/issues/1149) +- cocoapod integration [\#1148](https://github.com/nlohmann/json/issues/1148) +- wstring parsing [\#1147](https://github.com/nlohmann/json/issues/1147) +- Is it possible to dump a two-dimensional array to "\[\[null\],\[1,2,3\]\]"? [\#1146](https://github.com/nlohmann/json/issues/1146) +- Want to write a class member variable and a struct variable \( this structure is inside the class\) to the json file [\#1145](https://github.com/nlohmann/json/issues/1145) +- Does json support converting an instance of a struct into json string? [\#1143](https://github.com/nlohmann/json/issues/1143) +- \#Most efficient way to search for child parameters \(recursive find?\) [\#1141](https://github.com/nlohmann/json/issues/1141) +- could not find to\_json\(\) method in T's namespace [\#1140](https://github.com/nlohmann/json/issues/1140) +- chars get treated as JSON numbers not JSON strings [\#1139](https://github.com/nlohmann/json/issues/1139) +- How do I count number of objects in array? [\#1137](https://github.com/nlohmann/json/issues/1137) +- Serializing a vector of classes? [\#1136](https://github.com/nlohmann/json/issues/1136) +- Compile error. Unable convert form nullptr to nullptr&& [\#1135](https://github.com/nlohmann/json/issues/1135) +- std::unordered\_map in struct, serialization [\#1133](https://github.com/nlohmann/json/issues/1133) +- dump\(\) can't handle umlauts [\#1131](https://github.com/nlohmann/json/issues/1131) +- Add a way to get a key reference from the iterator [\#1127](https://github.com/nlohmann/json/issues/1127) +- can't not parse "\\“ string [\#1123](https://github.com/nlohmann/json/issues/1123) +- if json file contain Internationalization chars , get exception [\#1122](https://github.com/nlohmann/json/issues/1122) +- How to use a json::iterator dereferenced value in code? [\#1120](https://github.com/nlohmann/json/issues/1120) +- clang compiler: error : unknown type name 'not' [\#1119](https://github.com/nlohmann/json/issues/1119) +- Disable implicit conversions from json to std::initializer\_list\ for any T [\#1118](https://github.com/nlohmann/json/issues/1118) +- Implicit conversions to complex types can lead to surprising and confusing errors [\#1116](https://github.com/nlohmann/json/issues/1116) +- How can I write from\_json for a complex datatype that is not default constructible? [\#1115](https://github.com/nlohmann/json/issues/1115) +- Compile error in VS2015 when compiling unit-conversions.cpp [\#1114](https://github.com/nlohmann/json/issues/1114) +- ADL Serializer for std::any / boost::any [\#1113](https://github.com/nlohmann/json/issues/1113) +- Unexpected behaviour of is\_null [\#1112](https://github.com/nlohmann/json/issues/1112) +- How to resolve " undefined reference to `std::\_\_throw\_bad\_cast\(\)'" [\#1111](https://github.com/nlohmann/json/issues/1111) +- cannot compile on ubuntu 18.04 and 16.04 [\#1110](https://github.com/nlohmann/json/issues/1110) +- JSON representation for floating point values has too many digits [\#1109](https://github.com/nlohmann/json/issues/1109) +- Not working for classes containing "\_declspec\(dllimport\)" in their declaration [\#1108](https://github.com/nlohmann/json/issues/1108) +- Get keys from json object [\#1107](https://github.com/nlohmann/json/issues/1107) +- dump\(\) without alphabetical order [\#1106](https://github.com/nlohmann/json/issues/1106) +- Cannot deserialize types using std::ratio [\#1105](https://github.com/nlohmann/json/issues/1105) +- i want to learn json [\#1104](https://github.com/nlohmann/json/issues/1104) +- Type checking during compile [\#1103](https://github.com/nlohmann/json/issues/1103) +- Iterate through sub items [\#1102](https://github.com/nlohmann/json/issues/1102) +- cppcheck failing for version 3.1.2 [\#1101](https://github.com/nlohmann/json/issues/1101) +- Deserializing std::map [\#1100](https://github.com/nlohmann/json/issues/1100) +- accessing key by reference [\#1098](https://github.com/nlohmann/json/issues/1098) +- clang 3.8.0 croaks while trying to compile with debug symbols [\#1097](https://github.com/nlohmann/json/issues/1097) +- Serialize a list of class objects with json [\#1096](https://github.com/nlohmann/json/issues/1096) +- Small question [\#1094](https://github.com/nlohmann/json/issues/1094) +- Upgrading to 3.x: to\_/from\_json with enum class [\#1093](https://github.com/nlohmann/json/issues/1093) +- Q: few questions about json construction [\#1092](https://github.com/nlohmann/json/issues/1092) +- general crayCC compilation failure [\#1091](https://github.com/nlohmann/json/issues/1091) +- Merge Patch clears original data [\#1090](https://github.com/nlohmann/json/issues/1090) +- \[Question\] how to use nlohmann/json in c++? [\#1088](https://github.com/nlohmann/json/issues/1088) +- C++17 decomposition declaration support [\#1087](https://github.com/nlohmann/json/issues/1087) +- \[Question\] Access multi-level json objects [\#1086](https://github.com/nlohmann/json/issues/1086) +- Serializing vector [\#1085](https://github.com/nlohmann/json/issues/1085) +- update nested value in multi hierarchy json object [\#1084](https://github.com/nlohmann/json/issues/1084) +- Overriding default values? [\#1083](https://github.com/nlohmann/json/issues/1083) +- detail namespace collision with Cereal? [\#1082](https://github.com/nlohmann/json/issues/1082) +- Error using json.dump\(\); [\#1081](https://github.com/nlohmann/json/issues/1081) +- Consuming TCP Stream [\#1080](https://github.com/nlohmann/json/issues/1080) +- Compilation error with strong typed enums in map in combination with namespaces [\#1079](https://github.com/nlohmann/json/issues/1079) +- cassert error [\#1076](https://github.com/nlohmann/json/issues/1076) +- Valid json data not being parsed [\#1075](https://github.com/nlohmann/json/issues/1075) +- Feature request :: Better testing for key existance without try/catch [\#1074](https://github.com/nlohmann/json/issues/1074) +- Hi, I have input like a.b.c and want to convert it to \"a\"{\"b\": \"c\"} form. Any suggestions how do I do this? Thanks. [\#1073](https://github.com/nlohmann/json/issues/1073) +- ADL deserializer not picked up for non default-constructible type [\#1072](https://github.com/nlohmann/json/issues/1072) +- Deserializing std::array doesn't compiler \(no insert\(\)\) [\#1071](https://github.com/nlohmann/json/issues/1071) +- Serializing OpenCV Mat problem [\#1070](https://github.com/nlohmann/json/issues/1070) +- Compilation error with ICPC compiler [\#1068](https://github.com/nlohmann/json/issues/1068) +- Minimal branch? [\#1066](https://github.com/nlohmann/json/issues/1066) +- Not existing value, crash [\#1065](https://github.com/nlohmann/json/issues/1065) +- cyryllic symbols [\#1064](https://github.com/nlohmann/json/issues/1064) +- newbie usage question [\#1063](https://github.com/nlohmann/json/issues/1063) +- Trying j\["strTest"\] = "%A" produces "strTest": "-0X1.CCCCCCCCCCCCCP+205" [\#1062](https://github.com/nlohmann/json/issues/1062) +- convert json value to std::string??? [\#1061](https://github.com/nlohmann/json/issues/1061) +- Commented out test cases, should they be removed? [\#1060](https://github.com/nlohmann/json/issues/1060) +- different behaviour between clang and gcc with braced initialization [\#1059](https://github.com/nlohmann/json/issues/1059) +- json array: initialize with prescribed size and `resize` method. [\#1057](https://github.com/nlohmann/json/issues/1057) +- Is it possible to use exceptions istead of assertions? [\#1056](https://github.com/nlohmann/json/issues/1056) +- when using assign operator in with json object a static assertion fails.. [\#1055](https://github.com/nlohmann/json/issues/1055) +- Iterate over leafs of a JSON data structure: enrich the JSON pointer API [\#1054](https://github.com/nlohmann/json/issues/1054) +- \[Feature request\] Access by path [\#1053](https://github.com/nlohmann/json/issues/1053) +- document that implicit js -\> primitive conversion does not work for std::string::value\_type and why [\#1052](https://github.com/nlohmann/json/issues/1052) +- error: ‘BasicJsonType’ in namespace ‘::’ does not name a type [\#1051](https://github.com/nlohmann/json/issues/1051) +- Destructor is called when filling object through assignement [\#1050](https://github.com/nlohmann/json/issues/1050) +- Is this thing thread safe for reads? [\#1049](https://github.com/nlohmann/json/issues/1049) +- clang-tidy: Call to virtual function during construction [\#1046](https://github.com/nlohmann/json/issues/1046) +- Using STL algorithms with JSON containers with expected results? [\#1045](https://github.com/nlohmann/json/issues/1045) +- Usage with gtest/gmock not working as expected [\#1044](https://github.com/nlohmann/json/issues/1044) +- Consequences of from\_json / to\_json being in namespace of data struct. [\#1042](https://github.com/nlohmann/json/issues/1042) +- const\_reference operator\[\]\(const typename object\_t::key\_type& key\) const throw instead of assert [\#1039](https://github.com/nlohmann/json/issues/1039) +- Trying to retrieve data from nested objects [\#1038](https://github.com/nlohmann/json/issues/1038) +- Direct download link for json\_fwd.hpp? [\#1037](https://github.com/nlohmann/json/issues/1037) +- I know the library supports UTF-8, but failed to dump the value [\#1036](https://github.com/nlohmann/json/issues/1036) +- Putting a Vec3-like vector into a json object [\#1035](https://github.com/nlohmann/json/issues/1035) +- Ternary operator crash [\#1034](https://github.com/nlohmann/json/issues/1034) +- Issued with Clion Inspection Resolution since 2018.1 [\#1033](https://github.com/nlohmann/json/issues/1033) +- Some testcases fail and one never finishes [\#1032](https://github.com/nlohmann/json/issues/1032) +- Can this class work with wchar\_t / std::wstring? [\#1031](https://github.com/nlohmann/json/issues/1031) +- Makefile: Valgrind flags have no effect [\#1030](https://github.com/nlohmann/json/issues/1030) +- 「==」 Should be 「\>」 [\#1029](https://github.com/nlohmann/json/issues/1029) +- HOCON reader? [\#1027](https://github.com/nlohmann/json/issues/1027) +- add json string in previous string?? [\#1025](https://github.com/nlohmann/json/issues/1025) +- RFC: fluent parsing interface [\#1023](https://github.com/nlohmann/json/issues/1023) +- Does it support chinese character? [\#1022](https://github.com/nlohmann/json/issues/1022) +- to/from\_msgpack only works with standard typization [\#1021](https://github.com/nlohmann/json/issues/1021) +- Build failure using latest clang and GCC compilers [\#1020](https://github.com/nlohmann/json/issues/1020) +- can two json objects be concatenated? [\#1019](https://github.com/nlohmann/json/issues/1019) +- Erase by integer index [\#1018](https://github.com/nlohmann/json/issues/1018) +- Function find overload taking a json\_pointer [\#1017](https://github.com/nlohmann/json/issues/1017) +- I think should implement an parser function [\#1016](https://github.com/nlohmann/json/issues/1016) +- Readme gif [\#1015](https://github.com/nlohmann/json/issues/1015) +- Python bindings [\#1014](https://github.com/nlohmann/json/issues/1014) +- how to add two json string in single object?? [\#1012](https://github.com/nlohmann/json/issues/1012) +- how to serialize class Object \(convert data in object into json\)?? [\#1011](https://github.com/nlohmann/json/issues/1011) +- Enable forward declaration of json by making json a class instead of a using declaration [\#997](https://github.com/nlohmann/json/issues/997) +- compilation error while using intel c++ compiler 2018 [\#994](https://github.com/nlohmann/json/issues/994) +- How to create a json variable? [\#990](https://github.com/nlohmann/json/issues/990) +- istream \>\> json --- 1st character skipped in stream [\#976](https://github.com/nlohmann/json/issues/976) +- Add a SAX parser [\#971](https://github.com/nlohmann/json/issues/971) +- Add Key name to Exception [\#932](https://github.com/nlohmann/json/issues/932) +- How to solve large json file? [\#927](https://github.com/nlohmann/json/issues/927) +- json\_pointer public push\_back, pop\_back [\#837](https://github.com/nlohmann/json/issues/837) +- Using input\_adapter in a slightly unexpected way [\#834](https://github.com/nlohmann/json/issues/834) +- Stack-overflow \(OSS-Fuzz 4234\) [\#832](https://github.com/nlohmann/json/issues/832) + +- Fix -Wno-sometimes-uninitialized by initializing "result" in parse\_sax [\#1200](https://github.com/nlohmann/json/pull/1200) ([thyu](https://github.com/thyu)) +- \[RFC\] Introduce a new macro function: JSON\_INTERNAL\_CATCH [\#1187](https://github.com/nlohmann/json/pull/1187) ([simnalamburt](https://github.com/simnalamburt)) +- Fix unit tests that were silently skipped or crashed \(depending on the compiler\) [\#1176](https://github.com/nlohmann/json/pull/1176) ([grembo](https://github.com/grembo)) +- Refactor/no virtual sax [\#1153](https://github.com/nlohmann/json/pull/1153) ([theodelrieu](https://github.com/theodelrieu)) +- Fixed compiler error in VS 2015 for debug mode [\#1151](https://github.com/nlohmann/json/pull/1151) ([sonulohani](https://github.com/sonulohani)) +- Fix links to cppreference named requirements \(formerly concepts\) [\#1144](https://github.com/nlohmann/json/pull/1144) ([jrakow](https://github.com/jrakow)) +- meson: fix include directory [\#1142](https://github.com/nlohmann/json/pull/1142) ([jrakow](https://github.com/jrakow)) +- Feature/unordered map conversion [\#1138](https://github.com/nlohmann/json/pull/1138) ([theodelrieu](https://github.com/theodelrieu)) +- fixed compile error for \#1045 [\#1134](https://github.com/nlohmann/json/pull/1134) ([Daniel599](https://github.com/Daniel599)) +- test \(non\)equality for alt\_string implementation [\#1130](https://github.com/nlohmann/json/pull/1130) ([agrianius](https://github.com/agrianius)) +- remove stringstream dependency [\#1117](https://github.com/nlohmann/json/pull/1117) ([TinyTinni](https://github.com/TinyTinni)) +- Provide a from\_json overload for std::map [\#1089](https://github.com/nlohmann/json/pull/1089) ([theodelrieu](https://github.com/theodelrieu)) +- fix typo in README [\#1078](https://github.com/nlohmann/json/pull/1078) ([martin-mfg](https://github.com/martin-mfg)) +- Fix typo [\#1058](https://github.com/nlohmann/json/pull/1058) ([dns13](https://github.com/dns13)) +- Misc cmake packaging enhancements [\#1048](https://github.com/nlohmann/json/pull/1048) ([chuckatkins](https://github.com/chuckatkins)) +- Fixed incorrect LLVM version number in README [\#1047](https://github.com/nlohmann/json/pull/1047) ([jammehcow](https://github.com/jammehcow)) +- Fix trivial typo in comment. [\#1043](https://github.com/nlohmann/json/pull/1043) ([coryan](https://github.com/coryan)) +- Package Manager: Spack [\#1041](https://github.com/nlohmann/json/pull/1041) ([ax3l](https://github.com/ax3l)) +- CMake: 3.8+ is Sufficient [\#1040](https://github.com/nlohmann/json/pull/1040) ([ax3l](https://github.com/ax3l)) +- Added support for string\_view in C++17 [\#1028](https://github.com/nlohmann/json/pull/1028) ([gracicot](https://github.com/gracicot)) +- Added public target\_compile\_features for auto and constexpr [\#1026](https://github.com/nlohmann/json/pull/1026) ([ktonon](https://github.com/ktonon)) + +## [v3.1.2](https://github.com/nlohmann/json/releases/tag/v3.1.2) (2018-03-14) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.1.1...v3.1.2) + +- STL containers are always serialized to a nested array like \[\[1,2,3\]\] [\#1013](https://github.com/nlohmann/json/issues/1013) +- The library doesn't want to insert an unordered\_map [\#1010](https://github.com/nlohmann/json/issues/1010) +- Convert Json to uint8\_t [\#1008](https://github.com/nlohmann/json/issues/1008) +- How to compare two JSON objects? [\#1007](https://github.com/nlohmann/json/issues/1007) +- Syntax checking [\#1003](https://github.com/nlohmann/json/issues/1003) +- more than one operator '=' matches these operands [\#1002](https://github.com/nlohmann/json/issues/1002) +- How to check if key existed [\#1000](https://github.com/nlohmann/json/issues/1000) +- nlohmann::json::parse exhaust memory in go binding [\#999](https://github.com/nlohmann/json/issues/999) +- Range-based iteration over a non-array object [\#998](https://github.com/nlohmann/json/issues/998) +- get\ for types that are not default constructible [\#996](https://github.com/nlohmann/json/issues/996) +- Prevent Null values to appear in .dump\(\) [\#995](https://github.com/nlohmann/json/issues/995) +- number parsing [\#993](https://github.com/nlohmann/json/issues/993) +- C2664 \(C++/CLR\) cannot convert 'nullptr' to 'nullptr &&' [\#987](https://github.com/nlohmann/json/issues/987) +- Uniform initialization from another json object differs between gcc and clang. [\#985](https://github.com/nlohmann/json/issues/985) +- Problem with adding the lib as a submodule [\#983](https://github.com/nlohmann/json/issues/983) +- UTF-8/Unicode error [\#982](https://github.com/nlohmann/json/issues/982) +- "forcing MSVC stacktrace to show which T we're talking about." error [\#980](https://github.com/nlohmann/json/issues/980) +- reverse order of serialization [\#979](https://github.com/nlohmann/json/issues/979) +- Assigning between different json types [\#977](https://github.com/nlohmann/json/issues/977) +- Support serialisation of `unique\_ptr\<\>` and `shared\_ptr\<\>` [\#975](https://github.com/nlohmann/json/issues/975) +- Unexpected end of input \(not same as one before\) [\#974](https://github.com/nlohmann/json/issues/974) +- Segfault on direct initializing json object [\#973](https://github.com/nlohmann/json/issues/973) +- Segmentation fault on G++ when trying to assign json string literal to custom json type. [\#972](https://github.com/nlohmann/json/issues/972) +- os\_defines.h:44:19: error: missing binary operator before token "\(" [\#970](https://github.com/nlohmann/json/issues/970) +- Passing an iteration object by reference to a function [\#967](https://github.com/nlohmann/json/issues/967) +- Json and fmt::lib's format\_arg\(\) [\#964](https://github.com/nlohmann/json/issues/964) + +- Allowing for user-defined string type in lexer/parser [\#1009](https://github.com/nlohmann/json/pull/1009) ([nlohmann](https://github.com/nlohmann)) +- dump to alternative string type, as defined in basic\_json template [\#1006](https://github.com/nlohmann/json/pull/1006) ([agrianius](https://github.com/agrianius)) +- Fix memory leak during parser callback [\#1001](https://github.com/nlohmann/json/pull/1001) ([nlohmann](https://github.com/nlohmann)) +- fixed misprinted condition detected by PVS Studio. [\#992](https://github.com/nlohmann/json/pull/992) ([bogemic](https://github.com/bogemic)) +- Fix/basic json conversion [\#986](https://github.com/nlohmann/json/pull/986) ([theodelrieu](https://github.com/theodelrieu)) +- Make integration section concise [\#981](https://github.com/nlohmann/json/pull/981) ([wla80](https://github.com/wla80)) + +## [v3.1.1](https://github.com/nlohmann/json/releases/tag/v3.1.1) (2018-02-13) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.1.0...v3.1.1) + +- Updation of child object isn't reflected in parent Object [\#968](https://github.com/nlohmann/json/issues/968) +- How to add user defined C++ path to sublime text [\#966](https://github.com/nlohmann/json/issues/966) +- fast number parsing [\#965](https://github.com/nlohmann/json/issues/965) +- With non-unique keys, later stored entries are not taken into account anymore [\#963](https://github.com/nlohmann/json/issues/963) +- Timeout \(OSS-Fuzz 6034\) [\#962](https://github.com/nlohmann/json/issues/962) +- Incorrect parsing of indefinite length CBOR strings. [\#961](https://github.com/nlohmann/json/issues/961) +- Reload a json file at runtime without emptying my std::ifstream [\#959](https://github.com/nlohmann/json/issues/959) +- Split headers should be part of the release [\#956](https://github.com/nlohmann/json/issues/956) +- Coveralls shows no coverage data [\#953](https://github.com/nlohmann/json/issues/953) +- Feature request: Implicit conversion to bool [\#951](https://github.com/nlohmann/json/issues/951) +- converting json to vector of type with templated constructor [\#924](https://github.com/nlohmann/json/issues/924) +- No structured bindings support? [\#901](https://github.com/nlohmann/json/issues/901) +- \[Request\] Macro generating from\_json\(\) and to\_json\(\) [\#895](https://github.com/nlohmann/json/issues/895) +- basic\_json::value throws exception instead of returning default value [\#871](https://github.com/nlohmann/json/issues/871) + +- Fix constraints on from\_json\(CompatibleArrayType\) [\#969](https://github.com/nlohmann/json/pull/969) ([theodelrieu](https://github.com/theodelrieu)) +- Make coveralls watch the include folder [\#957](https://github.com/nlohmann/json/pull/957) ([theodelrieu](https://github.com/theodelrieu)) +- Fix links in README.md [\#955](https://github.com/nlohmann/json/pull/955) ([patrikhuber](https://github.com/patrikhuber)) +- Add a note about installing the library with cget [\#954](https://github.com/nlohmann/json/pull/954) ([pfultz2](https://github.com/pfultz2)) + +## [v3.1.0](https://github.com/nlohmann/json/releases/tag/v3.1.0) (2018-02-01) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.0.1...v3.1.0) + +- Order of the elements in JSON object [\#952](https://github.com/nlohmann/json/issues/952) +- I have a proposal [\#949](https://github.com/nlohmann/json/issues/949) +- VERSION define\(s\) [\#948](https://github.com/nlohmann/json/issues/948) +- v3.0.1 compile error in icc 16.0.4 [\#947](https://github.com/nlohmann/json/issues/947) +- Use in VS2017 15.5.5 [\#946](https://github.com/nlohmann/json/issues/946) +- Process for reporting Security Bugs? [\#945](https://github.com/nlohmann/json/issues/945) +- Please expose a NLOHMANN\_JSON\_VERSION macro [\#943](https://github.com/nlohmann/json/issues/943) +- Change header include directory to nlohmann/json [\#942](https://github.com/nlohmann/json/issues/942) +- string\_type in binary\_reader [\#941](https://github.com/nlohmann/json/issues/941) +- compile error with clang 5.0 -std=c++1z and no string\_view [\#939](https://github.com/nlohmann/json/issues/939) +- Allow overriding JSON\_THROW to something else than abort\(\) [\#938](https://github.com/nlohmann/json/issues/938) +- Handle invalid string in Json file [\#937](https://github.com/nlohmann/json/issues/937) +- Unused variable 'kMinExp' [\#935](https://github.com/nlohmann/json/issues/935) +- yytext is already defined [\#933](https://github.com/nlohmann/json/issues/933) +- Equality operator fails [\#931](https://github.com/nlohmann/json/issues/931) +- use in visual studio 2015 [\#929](https://github.com/nlohmann/json/issues/929) +- Relative includes of json\_fwd.hpp in detail/meta.hpp. \[Develop branch\] [\#928](https://github.com/nlohmann/json/issues/928) +- GCC 7.x issue [\#926](https://github.com/nlohmann/json/issues/926) +- json\_fwd.hpp not installed [\#923](https://github.com/nlohmann/json/issues/923) +- Use Google Benchmarks [\#921](https://github.com/nlohmann/json/issues/921) +- Move class json\_pointer to separate file [\#920](https://github.com/nlohmann/json/issues/920) +- Unable to locate 'to\_json\(\)' and 'from\_json\(\)' methods in the same namespace [\#917](https://github.com/nlohmann/json/issues/917) +- \[answered\]Read key1 from .value example [\#914](https://github.com/nlohmann/json/issues/914) +- Don't use `define private public` in test files [\#913](https://github.com/nlohmann/json/issues/913) +- value\(\) template argument type deduction [\#912](https://github.com/nlohmann/json/issues/912) +- Installation path is incorrect [\#910](https://github.com/nlohmann/json/issues/910) +- H [\#909](https://github.com/nlohmann/json/issues/909) +- Build failure using clang 5 [\#908](https://github.com/nlohmann/json/issues/908) +- Amalgate [\#907](https://github.com/nlohmann/json/issues/907) +- Update documentation and tests wrt. split headers [\#906](https://github.com/nlohmann/json/issues/906) +- Lib not working on ubuntu 16.04 [\#905](https://github.com/nlohmann/json/issues/905) +- Problem when writing to file. [\#904](https://github.com/nlohmann/json/issues/904) +- C2864 error when compiling with VS2015 and VS 2017 [\#903](https://github.com/nlohmann/json/issues/903) +- \[json.exception.type\_error.304\] cannot use at\(\) with object [\#902](https://github.com/nlohmann/json/issues/902) +- How do I forward nlohmann::json declaration? [\#899](https://github.com/nlohmann/json/issues/899) +- How to effectively store binary data? [\#898](https://github.com/nlohmann/json/issues/898) +- How to get the length of a JSON string without retrieving its std::string? [\#897](https://github.com/nlohmann/json/issues/897) +- Regression Tests Failure using "ctest" [\#887](https://github.com/nlohmann/json/issues/887) +- Discuss: add JSON Merge Patch \(RFC 7396\)? [\#877](https://github.com/nlohmann/json/issues/877) +- Discuss: replace static "iterator\_wrapper" function with "items" member function [\#874](https://github.com/nlohmann/json/issues/874) +- Make optional user-data available in from\_json [\#864](https://github.com/nlohmann/json/issues/864) +- Casting to std::string not working in VS2015 [\#861](https://github.com/nlohmann/json/issues/861) +- Sequential reading of JSON arrays [\#851](https://github.com/nlohmann/json/issues/851) +- Idea: Handle Multimaps Better [\#816](https://github.com/nlohmann/json/issues/816) +- Floating point rounding [\#777](https://github.com/nlohmann/json/issues/777) +- Loss of precision when serializing \ [\#360](https://github.com/nlohmann/json/issues/360) + +- Templatize std::string in binary\_reader \#941 [\#950](https://github.com/nlohmann/json/pull/950) ([kaidokert](https://github.com/kaidokert)) +- fix cmake install directory \(for real this time\) [\#944](https://github.com/nlohmann/json/pull/944) ([theodelrieu](https://github.com/theodelrieu)) +- Allow overriding THROW/CATCH/TRY macros with no-exceptions \#938 [\#940](https://github.com/nlohmann/json/pull/940) ([kaidokert](https://github.com/kaidokert)) +- Removed compiler warning about unused variable 'kMinExp' [\#936](https://github.com/nlohmann/json/pull/936) ([zerodefect](https://github.com/zerodefect)) +- Fix a typo in README.md [\#930](https://github.com/nlohmann/json/pull/930) ([Pipeliner](https://github.com/Pipeliner)) +- Howto installation of json\_fwd.hpp \(fixes \#923\) [\#925](https://github.com/nlohmann/json/pull/925) ([zerodefect](https://github.com/zerodefect)) +- fix sfinae on basic\_json UDT constructor [\#919](https://github.com/nlohmann/json/pull/919) ([theodelrieu](https://github.com/theodelrieu)) +- Floating-point formatting [\#915](https://github.com/nlohmann/json/pull/915) ([abolz](https://github.com/abolz)) +- Fix/cmake install [\#911](https://github.com/nlohmann/json/pull/911) ([theodelrieu](https://github.com/theodelrieu)) +- fix link to the documentation of the emplace function [\#900](https://github.com/nlohmann/json/pull/900) ([Dobiasd](https://github.com/Dobiasd)) +- JSON Merge Patch \(RFC 7396\) [\#876](https://github.com/nlohmann/json/pull/876) ([nlohmann](https://github.com/nlohmann)) +- Refactor/split it [\#700](https://github.com/nlohmann/json/pull/700) ([theodelrieu](https://github.com/theodelrieu)) + +## [v3.0.1](https://github.com/nlohmann/json/releases/tag/v3.0.1) (2017-12-29) +[Full Changelog](https://github.com/nlohmann/json/compare/v3.0.0...v3.0.1) + +- Problem parsing array to global vector [\#896](https://github.com/nlohmann/json/issues/896) +- Invalid RFC6902 copy operation succeeds [\#894](https://github.com/nlohmann/json/issues/894) +- How to rename a key during looping? [\#893](https://github.com/nlohmann/json/issues/893) +- clang++-6.0 \(6.0.0-svn321357-1\) warning [\#892](https://github.com/nlohmann/json/issues/892) +- Make json.hpp aware of the modules TS? [\#891](https://github.com/nlohmann/json/issues/891) +- All enum values not handled in switch cases. \( -Wswitch-enum \) [\#889](https://github.com/nlohmann/json/issues/889) +- JSON Pointer resolve failure resulting in incorrect exception code [\#888](https://github.com/nlohmann/json/issues/888) +- Unexpected nested arrays from std::vector [\#886](https://github.com/nlohmann/json/issues/886) +- erase multiple elements from a json object [\#884](https://github.com/nlohmann/json/issues/884) +- Container function overview in Doxygen is not updated [\#883](https://github.com/nlohmann/json/issues/883) +- How to use this for binary file uploads [\#881](https://github.com/nlohmann/json/issues/881) +- Allow setting JSON\_BuildTests=OFF from parent CMakeLists.txt [\#846](https://github.com/nlohmann/json/issues/846) +- Unit test fails for local-independent str-to-num [\#845](https://github.com/nlohmann/json/issues/845) +- Another idea about type support [\#774](https://github.com/nlohmann/json/issues/774) + +- Includes CTest module/adds BUILD\_TESTING option [\#885](https://github.com/nlohmann/json/pull/885) ([TinyTinni](https://github.com/TinyTinni)) +- Fix MSVC warning C4819 [\#882](https://github.com/nlohmann/json/pull/882) ([erengy](https://github.com/erengy)) +- Merge branch 'develop' into coverity\_scan [\#880](https://github.com/nlohmann/json/pull/880) ([nlohmann](https://github.com/nlohmann)) +- :wrench: Fix up a few more effc++ items [\#858](https://github.com/nlohmann/json/pull/858) ([mattismyname](https://github.com/mattismyname)) + +## [v3.0.0](https://github.com/nlohmann/json/releases/tag/v3.0.0) (2017-12-17) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.1...v3.0.0) + +- unicode strings [\#878](https://github.com/nlohmann/json/issues/878) +- Visual Studio 2017 15.5 C++17 std::allocator deprecations [\#872](https://github.com/nlohmann/json/issues/872) +- Typo "excpetion" [\#869](https://github.com/nlohmann/json/issues/869) +- Explicit array example in README.md incorrect [\#867](https://github.com/nlohmann/json/issues/867) +- why don't you release this from Feb. ? [\#865](https://github.com/nlohmann/json/issues/865) +- json::parse throws std::invalid\_argument when processing string generated by json::dump\(\) [\#863](https://github.com/nlohmann/json/issues/863) +- code analysis: potential bug? [\#859](https://github.com/nlohmann/json/issues/859) +- MSVC2017, 15.5 new issues. [\#857](https://github.com/nlohmann/json/issues/857) +- very basic: fetching string value/content without quotes [\#853](https://github.com/nlohmann/json/issues/853) +- Ambiguous function call to get with pointer type and constant json object in VS2015 \(15.4.4\) [\#852](https://github.com/nlohmann/json/issues/852) +- How to put object in the array as a member? [\#850](https://github.com/nlohmann/json/issues/850) +- misclick, please ignore [\#849](https://github.com/nlohmann/json/issues/849) +- Make XML great again. [\#847](https://github.com/nlohmann/json/issues/847) +- Converting to array not working [\#843](https://github.com/nlohmann/json/issues/843) +- Iteration weirdness [\#842](https://github.com/nlohmann/json/issues/842) +- Use reference or pointer as Object value [\#841](https://github.com/nlohmann/json/issues/841) +- Ambiguity in parsing nested maps [\#840](https://github.com/nlohmann/json/issues/840) +- could not find from\_json\(\) method in T's namespace [\#839](https://github.com/nlohmann/json/issues/839) +- Incorrect parse error with binary data in keys? [\#838](https://github.com/nlohmann/json/issues/838) +- using dump\(\) when std::wstring is StringType with VS2017 [\#836](https://github.com/nlohmann/json/issues/836) +- Show the path of the currently parsed value when an error occurs [\#835](https://github.com/nlohmann/json/issues/835) +- Repetitive data type while reading [\#833](https://github.com/nlohmann/json/issues/833) +- Storing multiple types inside map [\#831](https://github.com/nlohmann/json/issues/831) +- Application terminating [\#830](https://github.com/nlohmann/json/issues/830) +- Missing CMake hunter package? [\#828](https://github.com/nlohmann/json/issues/828) +- std::map\ from json object yields C2665: 'std::pair\::pair': none of the 2 overloads could convert all the argument types [\#827](https://github.com/nlohmann/json/issues/827) +- object.dump gives quoted string, want to use .dump\(\) to generate javascripts. [\#826](https://github.com/nlohmann/json/issues/826) +- Assertion failed on \["NoExistKey"\] of an not existing key of const json& [\#825](https://github.com/nlohmann/json/issues/825) +- vs2015 error : static member will remain uninitialized at runtime but use in constant-expressions is supported [\#824](https://github.com/nlohmann/json/issues/824) +- Code Checking Warnings from json.hpp on VS2017 Community [\#821](https://github.com/nlohmann/json/issues/821) +- Missing iostream in try online [\#820](https://github.com/nlohmann/json/issues/820) +- Floating point value loses decimal point during dump [\#818](https://github.com/nlohmann/json/issues/818) +- Conan package for the library [\#817](https://github.com/nlohmann/json/issues/817) +- stream error [\#815](https://github.com/nlohmann/json/issues/815) +- Link error when using find\(\) on the latest commit [\#814](https://github.com/nlohmann/json/issues/814) +- ABI issue with json object between 2 shared libraries [\#813](https://github.com/nlohmann/json/issues/813) +- scan\_string\(\) return token\_type::parse\_error; when parse ansi file [\#812](https://github.com/nlohmann/json/issues/812) +- segfault when using fifo\_map with json [\#810](https://github.com/nlohmann/json/issues/810) +- This shit is shit [\#809](https://github.com/nlohmann/json/issues/809) +- \_finite and \_isnan are no members of "std" [\#808](https://github.com/nlohmann/json/issues/808) +- how to print out the line which causing exception? [\#806](https://github.com/nlohmann/json/issues/806) +- {} uses copy constructor, while = does not [\#805](https://github.com/nlohmann/json/issues/805) +- json.hpp:8955: multiple definition of function that is not defined twice or more. [\#804](https://github.com/nlohmann/json/issues/804) +- \[question\] to\_json for base and derived class [\#803](https://github.com/nlohmann/json/issues/803) +- Misleading error message - unexpected '"' - on incorrect utf-8 symbol [\#802](https://github.com/nlohmann/json/issues/802) +- json data = std::string\_view\("hi"\); doesn't work? [\#801](https://github.com/nlohmann/json/issues/801) +- Thread safety of parse\(\) [\#800](https://github.com/nlohmann/json/issues/800) +- Numbers as strings [\#799](https://github.com/nlohmann/json/issues/799) +- Tests failing on arm [\#797](https://github.com/nlohmann/json/issues/797) +- Using your library \(without modification\) in another library [\#796](https://github.com/nlohmann/json/issues/796) +- Iterating over sub-object [\#794](https://github.com/nlohmann/json/issues/794) +- how to get the json object again from which printed by the method of dump\(\) [\#792](https://github.com/nlohmann/json/issues/792) +- ppa to include source [\#791](https://github.com/nlohmann/json/issues/791) +- Different include paths in macOS and Ubuntu [\#790](https://github.com/nlohmann/json/issues/790) +- Missing break after line 12886 in switch/case [\#789](https://github.com/nlohmann/json/issues/789) +- All unit tests fail? [\#787](https://github.com/nlohmann/json/issues/787) +- More use of move semantics in deserialization [\#786](https://github.com/nlohmann/json/issues/786) +- warning C4706 - Visual Studio 2017 \(/W4\) [\#784](https://github.com/nlohmann/json/issues/784) +- Compile error in clang 5.0 [\#782](https://github.com/nlohmann/json/issues/782) +- Error Installing appium\_lib with Ruby v2.4.2 Due to JSON [\#781](https://github.com/nlohmann/json/issues/781) +- ::get\\(\) fails in new\(er\) release \[MSVC\] [\#780](https://github.com/nlohmann/json/issues/780) +- Type Conversion [\#779](https://github.com/nlohmann/json/issues/779) +- Segfault on nested parsing [\#778](https://github.com/nlohmann/json/issues/778) +- Build warnings: shadowing exception id [\#776](https://github.com/nlohmann/json/issues/776) +- multi-level JSON support. [\#775](https://github.com/nlohmann/json/issues/775) +- SIGABRT on dump\(\) [\#773](https://github.com/nlohmann/json/issues/773) +- \[Question\] Custom StringType template parameter \(possibility for a KeyType template parameter\) [\#772](https://github.com/nlohmann/json/issues/772) +- constexpr ALL the Things! [\#771](https://github.com/nlohmann/json/issues/771) +- error: ‘BasicJsonType’ in namespace ‘::’ does not name a type [\#770](https://github.com/nlohmann/json/issues/770) +- Program calls abort function [\#769](https://github.com/nlohmann/json/issues/769) +- \[Question\] Floating point resolution config during dump\(\) ? [\#768](https://github.com/nlohmann/json/issues/768) +- make check - no test ran [\#767](https://github.com/nlohmann/json/issues/767) +- The library cannot work properly with custom allocator based containers [\#766](https://github.com/nlohmann/json/issues/766) +- Documentation or feature request. [\#763](https://github.com/nlohmann/json/issues/763) +- warnings in msvc about mix/max macro while windows.h is used in the project [\#762](https://github.com/nlohmann/json/issues/762) +- std::signbit ambiguous [\#761](https://github.com/nlohmann/json/issues/761) +- How to use value for std::experimental::optional type? [\#760](https://github.com/nlohmann/json/issues/760) +- Cannot load json file properly [\#759](https://github.com/nlohmann/json/issues/759) +- Compilation error with unordered\_map\< int, int \> [\#758](https://github.com/nlohmann/json/issues/758) +- CBOR string [\#757](https://github.com/nlohmann/json/issues/757) +- Proposal: out\_of\_range should be a subclass of std::out\_of\_range [\#756](https://github.com/nlohmann/json/issues/756) +- Compiling with icpc [\#755](https://github.com/nlohmann/json/issues/755) +- Getter is setting the value to null if the key does not exist [\#754](https://github.com/nlohmann/json/issues/754) +- parsing works sometimes and crashes others [\#752](https://github.com/nlohmann/json/issues/752) +- Static\_assert failed "incompatible pointer type" with Xcode [\#751](https://github.com/nlohmann/json/issues/751) +- user-defined literal operator not found [\#750](https://github.com/nlohmann/json/issues/750) +- getting clean string from it.key\(\) [\#748](https://github.com/nlohmann/json/issues/748) +- Best method for exploring and obtaining values of nested json objects when the names are not known beforehand? [\#747](https://github.com/nlohmann/json/issues/747) +- null char at the end of string [\#746](https://github.com/nlohmann/json/issues/746) +- Incorrect sample for operator \>\> in docs [\#745](https://github.com/nlohmann/json/issues/745) +- User-friendly documentation [\#744](https://github.com/nlohmann/json/issues/744) +- Retrieve all values that match a json path [\#743](https://github.com/nlohmann/json/issues/743) +- Compilation issue with gcc 7.2 [\#742](https://github.com/nlohmann/json/issues/742) +- CMake target nlohmann\_json does not have src into its interface includes [\#741](https://github.com/nlohmann/json/issues/741) +- Error when serializing empty json: type must be string, but is object [\#740](https://github.com/nlohmann/json/issues/740) +- Conversion error for std::map\ [\#739](https://github.com/nlohmann/json/issues/739) +- Dumping Json to file as array [\#738](https://github.com/nlohmann/json/issues/738) +- nesting json objects [\#737](https://github.com/nlohmann/json/issues/737) +- where to find general help? [\#736](https://github.com/nlohmann/json/issues/736) +- Compilation Error on Clang 5.0 Upgrade [\#735](https://github.com/nlohmann/json/issues/735) +- Compilation error with std::map\ on vs 2015 [\#734](https://github.com/nlohmann/json/issues/734) +- Benchmarks for Binary formats [\#733](https://github.com/nlohmann/json/issues/733) +- Move test blobs to a submodule? [\#732](https://github.com/nlohmann/json/issues/732) +- Support \n symbols in json string. [\#731](https://github.com/nlohmann/json/issues/731) +- Project's name is too generic and hard to search for [\#730](https://github.com/nlohmann/json/issues/730) +- Visual Studio 2015 IntelliTrace problems [\#729](https://github.com/nlohmann/json/issues/729) +- How to erase nested objects inside other objects? [\#728](https://github.com/nlohmann/json/issues/728) +- How to prevent alphabetical sorting of data? [\#727](https://github.com/nlohmann/json/issues/727) +- Serialization for CBOR [\#726](https://github.com/nlohmann/json/issues/726) +- Using json Object as value in a map [\#725](https://github.com/nlohmann/json/issues/725) +- std::regex and nlohmann::json value [\#724](https://github.com/nlohmann/json/issues/724) +- Warnings when compiling with VisualStudio 2015 [\#723](https://github.com/nlohmann/json/issues/723) +- Has this lib the unicode \(wstring\) support? [\#722](https://github.com/nlohmann/json/issues/722) +- When will be 3.0 in master? [\#721](https://github.com/nlohmann/json/issues/721) +- Determine the type from error message. [\#720](https://github.com/nlohmann/json/issues/720) +- Compile-Error C2100 \(MS VS2015\) in line 887 json.hpp [\#719](https://github.com/nlohmann/json/issues/719) +- from\_json not working for boost::optional example [\#718](https://github.com/nlohmann/json/issues/718) +- about from\_json and to\_json function [\#717](https://github.com/nlohmann/json/issues/717) +- How to deserialize array with derived objects [\#716](https://github.com/nlohmann/json/issues/716) +- How to detect parse failure? [\#715](https://github.com/nlohmann/json/issues/715) +- Parse throw std::ios\_base::failure exception when failbit set to true [\#714](https://github.com/nlohmann/json/issues/714) +- Is there a way of format just making a pretty print without changing the key's orders ? [\#713](https://github.com/nlohmann/json/issues/713) +- Serialization of array of not same model items [\#712](https://github.com/nlohmann/json/issues/712) +- pointer to json parse vector [\#711](https://github.com/nlohmann/json/issues/711) +- Gtest SEH Exception [\#709](https://github.com/nlohmann/json/issues/709) +- broken from\_json implementation for pair and tuple [\#707](https://github.com/nlohmann/json/issues/707) +- Unevaluated lambda in assert breaks gcc 7 build [\#705](https://github.com/nlohmann/json/issues/705) +- Issues when adding values to firebase database [\#704](https://github.com/nlohmann/json/issues/704) +- Floating point equality - revisited [\#703](https://github.com/nlohmann/json/issues/703) +- Conversion from valarray\ to json fails to build [\#702](https://github.com/nlohmann/json/issues/702) +- internal compiler error \(gcc7\) [\#701](https://github.com/nlohmann/json/issues/701) +- One build system to rule them all [\#698](https://github.com/nlohmann/json/issues/698) +- Generated nlohmann\_jsonConfig.cmake does not set JSON\_INCLUDE\_DIR [\#695](https://github.com/nlohmann/json/issues/695) +- support the Chinese language in json string [\#694](https://github.com/nlohmann/json/issues/694) +- NaN problem within develop branch [\#693](https://github.com/nlohmann/json/issues/693) +- Please post example of specialization for boost::filesystem [\#692](https://github.com/nlohmann/json/issues/692) +- Impossible to do an array of composite objects [\#691](https://github.com/nlohmann/json/issues/691) +- How to save json to file? [\#690](https://github.com/nlohmann/json/issues/690) +- my simple json parser [\#689](https://github.com/nlohmann/json/issues/689) +- problem with new struct parsing syntax [\#688](https://github.com/nlohmann/json/issues/688) +- Parse error while parse the json string contains UTF 8 encoded document bytes string [\#684](https://github.com/nlohmann/json/issues/684) +- \[question\] how to get a string value by pointer [\#683](https://github.com/nlohmann/json/issues/683) +- create json object from string variable [\#681](https://github.com/nlohmann/json/issues/681) +- adl\_serializer and CRTP [\#680](https://github.com/nlohmann/json/issues/680) +- Is there a way to control the precision of serialized floating point numbers? [\#677](https://github.com/nlohmann/json/issues/677) +- Is there a way to get the path of a value? [\#676](https://github.com/nlohmann/json/issues/676) +- Could the parser locate errors to line? [\#675](https://github.com/nlohmann/json/issues/675) +- There is performance inefficiency found by coverity tool json2.1.1/include/nlohmann/json.hpp [\#673](https://github.com/nlohmann/json/issues/673) +- include problem, when cmake on osx [\#672](https://github.com/nlohmann/json/issues/672) +- Operator= ambiguous in C++1z and GCC 7.1.1 [\#670](https://github.com/nlohmann/json/issues/670) +- should't the cmake install target be to nlohman/json.hpp [\#668](https://github.com/nlohmann/json/issues/668) +- deserialise from `std::vector` [\#667](https://github.com/nlohmann/json/issues/667) +- How to iterate? [\#665](https://github.com/nlohmann/json/issues/665) +- could this json lib work on windows? [\#664](https://github.com/nlohmann/json/issues/664) +- How does from\_json work? [\#662](https://github.com/nlohmann/json/issues/662) +- insert\(or merge\) object should replace same key , not ignore [\#661](https://github.com/nlohmann/json/issues/661) +- Why is an object ordering values by Alphabetical Order? [\#660](https://github.com/nlohmann/json/issues/660) +- Parse method doesn't handle newlines. [\#659](https://github.com/nlohmann/json/issues/659) +- Compilation "note" on GCC 6 ARM [\#658](https://github.com/nlohmann/json/issues/658) +- Adding additional push\_back/operator+= rvalue overloads for JSON object [\#657](https://github.com/nlohmann/json/issues/657) +- dump's parameter "ensure\_ascii" creates too long sequences [\#656](https://github.com/nlohmann/json/issues/656) +- Question: parsing `void \*` [\#655](https://github.com/nlohmann/json/issues/655) +- how should I check a string is valid JSON string ? [\#653](https://github.com/nlohmann/json/issues/653) +- Question: thread safety of read only accesses [\#651](https://github.com/nlohmann/json/issues/651) +- Eclipse: Method 'size' could not be resolved [\#649](https://github.com/nlohmann/json/issues/649) +- Update/Add object fields [\#648](https://github.com/nlohmann/json/issues/648) +- No exception raised for Out Of Range input of numbers [\#647](https://github.com/nlohmann/json/issues/647) +- Package Name [\#646](https://github.com/nlohmann/json/issues/646) +- What is the meaning of operator\[\]\(T\* key\) [\#645](https://github.com/nlohmann/json/issues/645) +- Which is the correct way to json objects as parameters to functions? [\#644](https://github.com/nlohmann/json/issues/644) +- Method to get string representations of values [\#642](https://github.com/nlohmann/json/issues/642) +- CBOR serialization of a given JSON value does not serialize [\#641](https://github.com/nlohmann/json/issues/641) +- Are we forced to use "-fexceptions" flag in android ndk project [\#640](https://github.com/nlohmann/json/issues/640) +- Comparison of objects containing floats [\#639](https://github.com/nlohmann/json/issues/639) +- 'localeconv' is not supported by NDK for SDK \<=20 [\#638](https://github.com/nlohmann/json/issues/638) +- \[Question\] cLion integration [\#637](https://github.com/nlohmann/json/issues/637) +- How to construct an iteratable usage in nlohmann json? [\#636](https://github.com/nlohmann/json/issues/636) +- \[Question\] copy assign json-container to vector [\#635](https://github.com/nlohmann/json/issues/635) +- Get size without .dump\(\) [\#634](https://github.com/nlohmann/json/issues/634) +- Segmentation fault when parsing invalid json file [\#633](https://github.com/nlohmann/json/issues/633) +- How to serialize from json to vector\? [\#632](https://github.com/nlohmann/json/issues/632) +- no member named 'thousands\_sep' in 'lconv' [\#631](https://github.com/nlohmann/json/issues/631) +- \[Question\] Any fork for \(the unsupported\) Visual Studio 2012 version? [\#628](https://github.com/nlohmann/json/issues/628) +- Dependency injection in serializer [\#627](https://github.com/nlohmann/json/issues/627) +- from\_json for std::array [\#625](https://github.com/nlohmann/json/issues/625) +- Discussion: How to structure the parsing function families [\#623](https://github.com/nlohmann/json/issues/623) +- Question: How to erase subtree [\#622](https://github.com/nlohmann/json/issues/622) +- Insertion into nested json field [\#621](https://github.com/nlohmann/json/issues/621) +- \[Question\] When using this as git submodule, will it clone the whole thing include test data and benchmark? [\#620](https://github.com/nlohmann/json/issues/620) +- Question: return static json object from function [\#618](https://github.com/nlohmann/json/issues/618) +- icc16 error [\#617](https://github.com/nlohmann/json/issues/617) +- \[-Wdeprecated-declarations\] in row `j \>\> ss;` in file `json.hpp:7405:26` and FAILED unit tests with MinGWx64! [\#616](https://github.com/nlohmann/json/issues/616) +- to\_json for pairs, tuples [\#614](https://github.com/nlohmann/json/issues/614) +- Using uninitialized memory 'buf' in line 11173 v2.1.1? [\#613](https://github.com/nlohmann/json/issues/613) +- How to parse multiple same Keys of JSON and save them? [\#612](https://github.com/nlohmann/json/issues/612) +- "Multiple declarations" error when using types defined with `typedef` [\#611](https://github.com/nlohmann/json/issues/611) +- 2.1.1+ breaks compilation of shared\_ptr\ == 0 [\#610](https://github.com/nlohmann/json/issues/610) +- a bug of inheritance ? [\#608](https://github.com/nlohmann/json/issues/608) +- std::map key conversion with to\_json [\#607](https://github.com/nlohmann/json/issues/607) +- json.hpp:6384:62: error: wrong number of template arguments \(1, should be 2\) [\#606](https://github.com/nlohmann/json/issues/606) +- Incremental parsing: Where's the push version? [\#605](https://github.com/nlohmann/json/issues/605) +- Is there a way to validate the structure of a json object ? [\#604](https://github.com/nlohmann/json/issues/604) +- \[Question\] Issue when using Appveyor when compiling library [\#603](https://github.com/nlohmann/json/issues/603) +- BOM not skipped when using json:parse\(iterator\) [\#602](https://github.com/nlohmann/json/issues/602) +- Use of the binary type in CBOR and Message Pack [\#601](https://github.com/nlohmann/json/issues/601) +- Newbie issue: how does one convert a map in Json back to std::map? [\#600](https://github.com/nlohmann/json/issues/600) +- Plugin system [\#599](https://github.com/nlohmann/json/issues/599) +- Feature request: Comments [\#597](https://github.com/nlohmann/json/issues/597) +- Using custom types for scalars? [\#596](https://github.com/nlohmann/json/issues/596) +- Issues with the arithmetic in iterator and reverse iterator [\#593](https://github.com/nlohmann/json/issues/593) +- not enough examples [\#592](https://github.com/nlohmann/json/issues/592) +- in-class initialization for type 'const T' is not yet implemented [\#591](https://github.com/nlohmann/json/issues/591) +- compiling with gcc 7 -\> error on bool operator \< [\#590](https://github.com/nlohmann/json/issues/590) +- Parsing from stream leads to an array [\#589](https://github.com/nlohmann/json/issues/589) +- Buggy support for binary string data [\#587](https://github.com/nlohmann/json/issues/587) +- C++17's ambiguous conversion [\#586](https://github.com/nlohmann/json/issues/586) +- How does the messagepack encoding/decoding compare to msgpack-cpp in terms of performance? [\#585](https://github.com/nlohmann/json/issues/585) +- is it possible to check existence of a value deep in hierarchy? [\#584](https://github.com/nlohmann/json/issues/584) +- loading from a stream and exceptions [\#582](https://github.com/nlohmann/json/issues/582) +- Visual Studio seems not to have all min\(\) function versions [\#581](https://github.com/nlohmann/json/issues/581) +- Supporting of the json schema [\#580](https://github.com/nlohmann/json/issues/580) +- Stack-overflow \(OSS-Fuzz 1444\) [\#577](https://github.com/nlohmann/json/issues/577) +- Heap-buffer-overflow \(OSS-Fuzz 1400\) [\#575](https://github.com/nlohmann/json/issues/575) +- JSON escape quotes [\#574](https://github.com/nlohmann/json/issues/574) +- error: static\_assert failed [\#573](https://github.com/nlohmann/json/issues/573) +- Storing floats, and round trip serialisation/deserialisation diffs [\#572](https://github.com/nlohmann/json/issues/572) +- JSON.getLong produces inconsistent results [\#571](https://github.com/nlohmann/json/issues/571) +- Request: Object.at\(\) with default return value [\#570](https://github.com/nlohmann/json/issues/570) +- Internal structure gets corrupted while parsing [\#569](https://github.com/nlohmann/json/issues/569) +- create template \ basic\_json from\_cbor\(Iter begin, Iter end\) [\#568](https://github.com/nlohmann/json/issues/568) +- Need to improve ignores.. [\#567](https://github.com/nlohmann/json/issues/567) +- Conan.io [\#566](https://github.com/nlohmann/json/issues/566) +- contradictory documentation regarding json::find [\#565](https://github.com/nlohmann/json/issues/565) +- Unexpected '\"' in middle of array [\#564](https://github.com/nlohmann/json/issues/564) +- Support parse std::pair to Json object [\#563](https://github.com/nlohmann/json/issues/563) +- json and Microsoft Visual c++ Compiler Nov 2012 CTP [\#562](https://github.com/nlohmann/json/issues/562) +- from\_json declaration order and exceptions [\#561](https://github.com/nlohmann/json/issues/561) +- Tip: Don't upgrade to VS2017 if using json initializer list constructs [\#559](https://github.com/nlohmann/json/issues/559) +- parse error - unexpected end of input [\#558](https://github.com/nlohmann/json/issues/558) +- Cant modify existing numbers inside a json object [\#557](https://github.com/nlohmann/json/issues/557) +- Minimal repository \(current size very large\) [\#556](https://github.com/nlohmann/json/issues/556) +- Better support for SAX style serialize and deserialize in new version? [\#554](https://github.com/nlohmann/json/issues/554) +- Cannot convert from json array to std::array [\#553](https://github.com/nlohmann/json/issues/553) +- Do not define an unnamed namespace in a header file \(DCL59-CPP\) [\#552](https://github.com/nlohmann/json/issues/552) +- Parse error on known good json file [\#551](https://github.com/nlohmann/json/issues/551) +- Warning on Intel compiler \(icc 17\) [\#550](https://github.com/nlohmann/json/issues/550) +- multiple versions of 'vsnprintf' [\#549](https://github.com/nlohmann/json/issues/549) +- illegal indirection [\#548](https://github.com/nlohmann/json/issues/548) +- Ambiguous compare operators with clang-5.0 [\#547](https://github.com/nlohmann/json/issues/547) +- Using tsl::ordered\_map [\#546](https://github.com/nlohmann/json/issues/546) +- Compiler support errors are inconvenient [\#544](https://github.com/nlohmann/json/issues/544) +- Head Elements Sorting [\#543](https://github.com/nlohmann/json/issues/543) +- Duplicate symbols error happens while to\_json/from\_json method implemented inside entity definition header file [\#542](https://github.com/nlohmann/json/issues/542) +- consider adding a bool json::is\_valid\(std::string const&\) non-member function [\#541](https://github.com/nlohmann/json/issues/541) +- Help request [\#539](https://github.com/nlohmann/json/issues/539) +- How to deal with missing keys in `from\_json`? [\#538](https://github.com/nlohmann/json/issues/538) +- recursive from\_msgpack implementation will stack overflow [\#537](https://github.com/nlohmann/json/issues/537) +- Exception objects must be nothrow copy constructible \(ERR60-CPP\) [\#531](https://github.com/nlohmann/json/issues/531) +- Support for multiple root elements [\#529](https://github.com/nlohmann/json/issues/529) +- Port has\_shape from dropbox/json11 [\#528](https://github.com/nlohmann/json/issues/528) +- dump\_float: truncation from ptrdiff\_t to long [\#527](https://github.com/nlohmann/json/issues/527) +- Make exception base class visible in basic\_json [\#525](https://github.com/nlohmann/json/issues/525) +- msgpack unit test failures on ppc64 arch [\#524](https://github.com/nlohmann/json/issues/524) +- How about split the implementation out, and only leave the interface? [\#523](https://github.com/nlohmann/json/issues/523) +- VC++2017 not enough actual parameters for macro 'max' [\#522](https://github.com/nlohmann/json/issues/522) +- crash on empty ifstream [\#521](https://github.com/nlohmann/json/issues/521) +- Suggestion: Support tabs for indentation when serializing to stream. [\#520](https://github.com/nlohmann/json/issues/520) +- Abrt in get\_number \(OSS-Fuzz 885\) [\#519](https://github.com/nlohmann/json/issues/519) +- Abrt on unknown address \(OSS-Fuzz 884\) [\#518](https://github.com/nlohmann/json/issues/518) +- Stack-overflow \(OSS-Fuzz 869\) [\#517](https://github.com/nlohmann/json/issues/517) +- Assertion error \(OSS-Fuzz 868\) [\#516](https://github.com/nlohmann/json/issues/516) +- NaN to json and back [\#515](https://github.com/nlohmann/json/issues/515) +- Comparison of NaN [\#514](https://github.com/nlohmann/json/issues/514) +- why it's not possible to serialize c++11 enums directly [\#513](https://github.com/nlohmann/json/issues/513) +- clang compile error: use of overloaded operator '\<=' is ambiguous with \(nlohmann::json{{"a", 5}}\)\["a"\] \<= 10 [\#512](https://github.com/nlohmann/json/issues/512) +- Why not also look inside the type for \(static\) to\_json and from\_json funtions? [\#511](https://github.com/nlohmann/json/issues/511) +- Parser issues [\#509](https://github.com/nlohmann/json/issues/509) +- I may not understand [\#507](https://github.com/nlohmann/json/issues/507) +- VS2017 min / max problem for 2.1.1 [\#506](https://github.com/nlohmann/json/issues/506) +- CBOR/MessagePack is not read until the end [\#505](https://github.com/nlohmann/json/issues/505) +- Assertion error \(OSS-Fuzz 856\) [\#504](https://github.com/nlohmann/json/issues/504) +- Return position in parse error exceptions [\#503](https://github.com/nlohmann/json/issues/503) +- conversion from/to C array is not supported [\#502](https://github.com/nlohmann/json/issues/502) +- error C2338: could not find to\_json\(\) method in T's namespace [\#501](https://github.com/nlohmann/json/issues/501) +- Test suite fails in en\_GB.UTF-8 [\#500](https://github.com/nlohmann/json/issues/500) +- cannot use operator\[\] with number [\#499](https://github.com/nlohmann/json/issues/499) +- consider using \_\_cpp\_exceptions and/or \_\_EXCEPTIONS to disable/enable exception support [\#498](https://github.com/nlohmann/json/issues/498) +- Stack-overflow \(OSS-Fuzz issue 814\) [\#497](https://github.com/nlohmann/json/issues/497) +- Using in Unreal Engine - handling custom types conversion [\#495](https://github.com/nlohmann/json/issues/495) +- Conversion from vector\ to json fails to build [\#494](https://github.com/nlohmann/json/issues/494) +- fill\_line\_buffer incorrectly tests m\_stream for eof but not fail or bad bits [\#493](https://github.com/nlohmann/json/issues/493) +- Compiling with \_GLIBCXX\_DEBUG yields iterator-comparison warnings during tests [\#492](https://github.com/nlohmann/json/issues/492) +- crapy interface [\#491](https://github.com/nlohmann/json/issues/491) +- Fix Visual Studo 2013 builds. [\#490](https://github.com/nlohmann/json/issues/490) +- Failed to compile with -D\_GLIBCXX\_PARALLEL [\#489](https://github.com/nlohmann/json/issues/489) +- Input several field with the same name [\#488](https://github.com/nlohmann/json/issues/488) +- read in .json file yields strange sizes [\#487](https://github.com/nlohmann/json/issues/487) +- json::value\_t can't be a map's key type in VC++ 2015 [\#486](https://github.com/nlohmann/json/issues/486) +- Using fifo\_map [\#485](https://github.com/nlohmann/json/issues/485) +- Cannot get float pointer for value stored as `0` [\#484](https://github.com/nlohmann/json/issues/484) +- byte string support [\#483](https://github.com/nlohmann/json/issues/483) +- For a header-only library you have to clone 214MB [\#482](https://github.com/nlohmann/json/issues/482) +- https://github.com/nlohmann/json\#execute-unit-tests [\#481](https://github.com/nlohmann/json/issues/481) +- Remove deprecated constructor basic\_json\(std::istream&\) [\#480](https://github.com/nlohmann/json/issues/480) +- writing the binary json file? [\#479](https://github.com/nlohmann/json/issues/479) +- CBOR/MessagePack from uint8\_t \* and size [\#478](https://github.com/nlohmann/json/issues/478) +- Streaming binary representations [\#477](https://github.com/nlohmann/json/issues/477) +- Reuse memory in to\_cbor and to\_msgpack functions [\#476](https://github.com/nlohmann/json/issues/476) +- Error Using JSON Library with arrays C++ [\#475](https://github.com/nlohmann/json/issues/475) +- Moving forward to version 3.0.0 [\#474](https://github.com/nlohmann/json/issues/474) +- Inconsistent behavior in conversion to array type [\#473](https://github.com/nlohmann/json/issues/473) +- Create a \[key:member\_pointer\] map to ease parsing custom types [\#471](https://github.com/nlohmann/json/issues/471) +- MSVC 2015 update 2 [\#469](https://github.com/nlohmann/json/issues/469) +- VS2017 implicit to std::string conversion fix. [\#464](https://github.com/nlohmann/json/issues/464) +- How to make sure a string or string literal is a valid JSON? [\#458](https://github.com/nlohmann/json/issues/458) +- basic\_json templated on a "policy" class [\#456](https://github.com/nlohmann/json/issues/456) +- json::value\(const json\_pointer&, ValueType\) requires exceptions to return the default value. [\#440](https://github.com/nlohmann/json/issues/440) +- is it possible merge two json object [\#428](https://github.com/nlohmann/json/issues/428) +- Is it possible to turn this into a shared library? [\#420](https://github.com/nlohmann/json/issues/420) +- Further thoughts on performance improvements [\#418](https://github.com/nlohmann/json/issues/418) +- nan number stored as null [\#388](https://github.com/nlohmann/json/issues/388) +- Behavior of operator\>\> should more closely resemble that of built-in overloads. [\#367](https://github.com/nlohmann/json/issues/367) +- Request: range-based-for over a json-object to expose .first/.second [\#350](https://github.com/nlohmann/json/issues/350) +- feature wish: JSONPath [\#343](https://github.com/nlohmann/json/issues/343) +- UTF-8/Unicode escape and dump [\#330](https://github.com/nlohmann/json/issues/330) +- Serialized value not always can be parsed. [\#329](https://github.com/nlohmann/json/issues/329) +- Is there a way to forward declare nlohmann::json? [\#314](https://github.com/nlohmann/json/issues/314) +- Exception line [\#301](https://github.com/nlohmann/json/issues/301) +- Do not throw exception when default\_value's type does not match the actual type [\#278](https://github.com/nlohmann/json/issues/278) +- dump\(\) method doesn't work with a custom allocator [\#268](https://github.com/nlohmann/json/issues/268) +- Readme documentation enhancements [\#248](https://github.com/nlohmann/json/issues/248) +- Use user-defined exceptions [\#244](https://github.com/nlohmann/json/issues/244) +- Incorrect C++11 allocator model support [\#161](https://github.com/nlohmann/json/issues/161) + +- :white\_check\_mark: re-added tests for algorithms [\#879](https://github.com/nlohmann/json/pull/879) ([nlohmann](https://github.com/nlohmann)) +- Overworked library toward 3.0.0 release [\#875](https://github.com/nlohmann/json/pull/875) ([nlohmann](https://github.com/nlohmann)) +- :rotating\_light: remove C4996 warnings \#872 [\#873](https://github.com/nlohmann/json/pull/873) ([nlohmann](https://github.com/nlohmann)) +- :boom: throwing an exception in case dump encounters a non-UTF-8 string \#838 [\#870](https://github.com/nlohmann/json/pull/870) ([nlohmann](https://github.com/nlohmann)) +- :memo: fixing documentation \#867 [\#868](https://github.com/nlohmann/json/pull/868) ([nlohmann](https://github.com/nlohmann)) +- iter\_impl template conformance with C++17 [\#860](https://github.com/nlohmann/json/pull/860) ([bogemic](https://github.com/bogemic)) +- Std allocator conformance cpp17 [\#856](https://github.com/nlohmann/json/pull/856) ([bogemic](https://github.com/bogemic)) +- cmake: use BUILD\_INTERFACE/INSTALL\_INTERFACE [\#855](https://github.com/nlohmann/json/pull/855) ([theodelrieu](https://github.com/theodelrieu)) +- to/from\_json: add a MSVC-specific static\_assert to force a stacktrace [\#854](https://github.com/nlohmann/json/pull/854) ([theodelrieu](https://github.com/theodelrieu)) +- Add .natvis for MSVC debug view [\#844](https://github.com/nlohmann/json/pull/844) ([TinyTinni](https://github.com/TinyTinni)) +- Updated hunter package links [\#829](https://github.com/nlohmann/json/pull/829) ([jowr](https://github.com/jowr)) +- Typos README [\#811](https://github.com/nlohmann/json/pull/811) ([Itja](https://github.com/Itja)) +- add forwarding references to json\_ref constructor [\#807](https://github.com/nlohmann/json/pull/807) ([theodelrieu](https://github.com/theodelrieu)) +- Add transparent comparator and perfect forwarding support to find\(\) and count\(\) [\#795](https://github.com/nlohmann/json/pull/795) ([jseward](https://github.com/jseward)) +- Error : 'identifier "size\_t" is undefined' in linux [\#793](https://github.com/nlohmann/json/pull/793) ([sonulohani](https://github.com/sonulohani)) +- Fix Visual Studio 2017 warnings [\#788](https://github.com/nlohmann/json/pull/788) ([jseward](https://github.com/jseward)) +- Fix warning C4706 on Visual Studio 2017 [\#785](https://github.com/nlohmann/json/pull/785) ([jseward](https://github.com/jseward)) +- Set GENERATE\_TAGFILE in Doxyfile [\#783](https://github.com/nlohmann/json/pull/783) ([eld00d](https://github.com/eld00d)) +- using more CMake [\#765](https://github.com/nlohmann/json/pull/765) ([nlohmann](https://github.com/nlohmann)) +- Simplified istream handing \#367 [\#764](https://github.com/nlohmann/json/pull/764) ([pjkundert](https://github.com/pjkundert)) +- Add info for the vcpkg package. [\#753](https://github.com/nlohmann/json/pull/753) ([gregmarr](https://github.com/gregmarr)) +- fix from\_json implementation for pair/tuple [\#708](https://github.com/nlohmann/json/pull/708) ([theodelrieu](https://github.com/theodelrieu)) +- Update json.hpp [\#686](https://github.com/nlohmann/json/pull/686) ([GoWebProd](https://github.com/GoWebProd)) +- Remove duplicate word [\#685](https://github.com/nlohmann/json/pull/685) ([daixtrose](https://github.com/daixtrose)) +- To fix compilation issue for intel OSX compiler [\#682](https://github.com/nlohmann/json/pull/682) ([kbthomp1](https://github.com/kbthomp1)) +- Digraph warning [\#679](https://github.com/nlohmann/json/pull/679) ([traits](https://github.com/traits)) +- massage -\> message [\#678](https://github.com/nlohmann/json/pull/678) ([DmitryKuk](https://github.com/DmitryKuk)) +- Fix "not constraint" grammar in docs [\#674](https://github.com/nlohmann/json/pull/674) ([wincent](https://github.com/wincent)) +- Add documentation for integration with CMake and hunter [\#671](https://github.com/nlohmann/json/pull/671) ([dan-42](https://github.com/dan-42)) +- REFACTOR: rewrite CMakeLists.txt for better inlcude and reuse [\#669](https://github.com/nlohmann/json/pull/669) ([dan-42](https://github.com/dan-42)) +- enable\_testing only if the JSON\_BuildTests is ON [\#666](https://github.com/nlohmann/json/pull/666) ([effolkronium](https://github.com/effolkronium)) +- Support moving from rvalues in std::initializer\_list [\#663](https://github.com/nlohmann/json/pull/663) ([himikof](https://github.com/himikof)) +- add ensure\_ascii parameter to dump. \#330 [\#654](https://github.com/nlohmann/json/pull/654) ([ryanjmulder](https://github.com/ryanjmulder)) +- Rename BuildTests to JSON\_BuildTests [\#652](https://github.com/nlohmann/json/pull/652) ([olegendo](https://github.com/olegendo)) +- Don't include \, use std::make\_shared [\#650](https://github.com/nlohmann/json/pull/650) ([olegendo](https://github.com/olegendo)) +- Refacto/split basic json [\#643](https://github.com/nlohmann/json/pull/643) ([theodelrieu](https://github.com/theodelrieu)) +- fix typo in operator\_\_notequal example [\#630](https://github.com/nlohmann/json/pull/630) ([Chocobo1](https://github.com/Chocobo1)) +- Fix MSVC warning C4819 [\#629](https://github.com/nlohmann/json/pull/629) ([Chocobo1](https://github.com/Chocobo1)) +- \[BugFix\] Add parentheses around std::min [\#626](https://github.com/nlohmann/json/pull/626) ([koemeet](https://github.com/koemeet)) +- add pair/tuple conversions [\#624](https://github.com/nlohmann/json/pull/624) ([theodelrieu](https://github.com/theodelrieu)) +- remove std::pair support [\#615](https://github.com/nlohmann/json/pull/615) ([theodelrieu](https://github.com/theodelrieu)) +- Add pair support, fix CompatibleObject conversions \(fixes \#600\) [\#609](https://github.com/nlohmann/json/pull/609) ([theodelrieu](https://github.com/theodelrieu)) +- \#550 Fix iterator related compiling issues for Intel icc [\#598](https://github.com/nlohmann/json/pull/598) ([HenryRLee](https://github.com/HenryRLee)) +- Issue \#593 Fix the arithmetic operators in the iterator and reverse iterator [\#595](https://github.com/nlohmann/json/pull/595) ([HenryRLee](https://github.com/HenryRLee)) +- fix doxygen error of basic\_json::get\(\) [\#583](https://github.com/nlohmann/json/pull/583) ([zhaohuaxishi](https://github.com/zhaohuaxishi)) +- Fixing assignement for iterator wrapper second, and adding unit test [\#579](https://github.com/nlohmann/json/pull/579) ([Type1J](https://github.com/Type1J)) +- Adding first and second properties to iteration\_proxy\_internal [\#578](https://github.com/nlohmann/json/pull/578) ([Type1J](https://github.com/Type1J)) +- Adding support for Meson. [\#576](https://github.com/nlohmann/json/pull/576) ([Type1J](https://github.com/Type1J)) +- add enum class default conversions [\#545](https://github.com/nlohmann/json/pull/545) ([theodelrieu](https://github.com/theodelrieu)) +- Properly pop diagnostics [\#540](https://github.com/nlohmann/json/pull/540) ([tinloaf](https://github.com/tinloaf)) +- Add Visual Studio 17 image to appveyor build matrix [\#536](https://github.com/nlohmann/json/pull/536) ([vpetrigo](https://github.com/vpetrigo)) +- UTF8 encoding enhancement [\#534](https://github.com/nlohmann/json/pull/534) ([TedLyngmo](https://github.com/TedLyngmo)) +- Fix typo [\#530](https://github.com/nlohmann/json/pull/530) ([berkus](https://github.com/berkus)) +- Make exception base class visible in basic\_json [\#526](https://github.com/nlohmann/json/pull/526) ([krzysztofwos](https://github.com/krzysztofwos)) +- :art: Namespace `uint8\_t` from the C++ stdlib [\#510](https://github.com/nlohmann/json/pull/510) ([alex-weej](https://github.com/alex-weej)) +- add to\_json method for C arrays [\#508](https://github.com/nlohmann/json/pull/508) ([theodelrieu](https://github.com/theodelrieu)) +- Fix -Weffc++ warnings \(GNU 6.3.1\) [\#496](https://github.com/nlohmann/json/pull/496) ([TedLyngmo](https://github.com/TedLyngmo)) + +## [v2.1.1](https://github.com/nlohmann/json/releases/tag/v2.1.1) (2017-02-25) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.0...v2.1.1) + +- warning in the library [\#472](https://github.com/nlohmann/json/issues/472) +- How to create an array of Objects? [\#470](https://github.com/nlohmann/json/issues/470) +- \[Bug?\] Cannot get int pointer, but int64\_t works [\#468](https://github.com/nlohmann/json/issues/468) +- Illegal indirection [\#467](https://github.com/nlohmann/json/issues/467) +- in vs can't find linkageId [\#466](https://github.com/nlohmann/json/issues/466) +- Roundtrip error while parsing "1000000000000000010E5" [\#465](https://github.com/nlohmann/json/issues/465) +- C4996 error and warning with Visual Studio [\#463](https://github.com/nlohmann/json/issues/463) +- Support startIndex for from\_cbor/from\_msgpack [\#462](https://github.com/nlohmann/json/issues/462) +- question: monospace font used in feature slideshow? [\#460](https://github.com/nlohmann/json/issues/460) +- Object.keys\(\) [\#459](https://github.com/nlohmann/json/issues/459) +- Use “, “ as delimiter for json-objects. [\#457](https://github.com/nlohmann/json/issues/457) +- Enum -\> string during serialization and vice versa [\#455](https://github.com/nlohmann/json/issues/455) +- doubles are printed as integers [\#454](https://github.com/nlohmann/json/issues/454) +- Warnings with Visual Studio c++ \(VS2015 Update 3\) [\#453](https://github.com/nlohmann/json/issues/453) +- Heap-buffer-overflow \(OSS-Fuzz issue 585\) [\#452](https://github.com/nlohmann/json/issues/452) +- use of undeclared identifier 'UINT8\_MAX' [\#451](https://github.com/nlohmann/json/issues/451) +- Question on the lifetime managment of objects at the lower levels [\#449](https://github.com/nlohmann/json/issues/449) +- Json should not be constructible with 'json\*' [\#448](https://github.com/nlohmann/json/issues/448) +- Move value\_t to namespace scope [\#447](https://github.com/nlohmann/json/issues/447) +- Typo in README.md [\#446](https://github.com/nlohmann/json/issues/446) +- make check compilation is unneccesarily slow [\#445](https://github.com/nlohmann/json/issues/445) +- Problem in dump\(\) in json.h caused by ss.imbue [\#444](https://github.com/nlohmann/json/issues/444) +- I want to create Windows Application in Visual Studio 2015 c++, and i have a problem [\#443](https://github.com/nlohmann/json/issues/443) +- Implicit conversion issues [\#442](https://github.com/nlohmann/json/issues/442) +- Parsing of floats locale dependent [\#302](https://github.com/nlohmann/json/issues/302) + +- Speedup CI builds using cotire [\#461](https://github.com/nlohmann/json/pull/461) ([tusharpm](https://github.com/tusharpm)) +- TurpentineDistillery feature/locale independent str to num [\#450](https://github.com/nlohmann/json/pull/450) ([nlohmann](https://github.com/nlohmann)) +- README: adjust boost::optional example [\#439](https://github.com/nlohmann/json/pull/439) ([jaredgrubb](https://github.com/jaredgrubb)) +- fix \#414 - comparing to 0 literal [\#415](https://github.com/nlohmann/json/pull/415) ([stanmihai4](https://github.com/stanmihai4)) + +## [v2.1.0](https://github.com/nlohmann/json/releases/tag/v2.1.0) (2017-01-28) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.10...v2.1.0) + +- Parsing multiple JSON objects from a string or stream [\#438](https://github.com/nlohmann/json/issues/438) +- Use-of-uninitialized-value \(OSS-Fuzz issue 477\) [\#437](https://github.com/nlohmann/json/issues/437) +- add `reserve` function for array to reserve memory before adding json values into it [\#436](https://github.com/nlohmann/json/issues/436) +- Typo in examples page [\#434](https://github.com/nlohmann/json/issues/434) +- avoid malformed json [\#433](https://github.com/nlohmann/json/issues/433) +- How to add json objects to a map? [\#432](https://github.com/nlohmann/json/issues/432) +- create json instance from raw json \(unsigned char\*\) [\#431](https://github.com/nlohmann/json/issues/431) +- Getting std::invalid\_argument: stream error when following example [\#429](https://github.com/nlohmann/json/issues/429) +- Forward declare-only header? [\#427](https://github.com/nlohmann/json/issues/427) +- Implicit conversion from array to object [\#425](https://github.com/nlohmann/json/issues/425) +- Automatic ordered JSON [\#424](https://github.com/nlohmann/json/issues/424) +- error C4996: 'strerror' when reading file [\#422](https://github.com/nlohmann/json/issues/422) +- Get an error - JSON pointer must be empty or begin with '/' [\#421](https://github.com/nlohmann/json/issues/421) +- size parameter for parse\(\) [\#419](https://github.com/nlohmann/json/issues/419) +- json.hpp forcibly defines GCC\_VERSION [\#417](https://github.com/nlohmann/json/issues/417) +- Use-of-uninitialized-value \(OSS-Fuzz issue 377\) [\#416](https://github.com/nlohmann/json/issues/416) +- comparing to 0 literal [\#414](https://github.com/nlohmann/json/issues/414) +- Single char converted to ASCII code instead of string [\#413](https://github.com/nlohmann/json/issues/413) +- How to know if a string was parsed as utf-8? [\#406](https://github.com/nlohmann/json/issues/406) +- Overloaded += to add objects to an array makes no sense? [\#404](https://github.com/nlohmann/json/issues/404) +- Finding a value in an array [\#399](https://github.com/nlohmann/json/issues/399) +- add release information in static function [\#397](https://github.com/nlohmann/json/issues/397) +- Optimize memory usage of json objects in combination with binary serialization [\#373](https://github.com/nlohmann/json/issues/373) +- Conversion operators not considered [\#369](https://github.com/nlohmann/json/issues/369) +- Append ".0" to serialized floating\_point values that are digits-only. [\#362](https://github.com/nlohmann/json/issues/362) +- Add a customization point for user-defined types [\#328](https://github.com/nlohmann/json/issues/328) +- Conformance report for reference [\#307](https://github.com/nlohmann/json/issues/307) +- Document the best way to serialize/deserialize user defined types to json [\#298](https://github.com/nlohmann/json/issues/298) +- Add StringView template typename to basic\_json [\#297](https://github.com/nlohmann/json/issues/297) +- \[Improvement\] Add option to remove exceptions [\#296](https://github.com/nlohmann/json/issues/296) +- Performance in miloyip/nativejson-benchmark [\#202](https://github.com/nlohmann/json/issues/202) + +- conversion from/to user-defined types [\#435](https://github.com/nlohmann/json/pull/435) ([nlohmann](https://github.com/nlohmann)) +- Fix documentation error [\#430](https://github.com/nlohmann/json/pull/430) ([vjon](https://github.com/vjon)) +- locale-independent num-to-str [\#378](https://github.com/nlohmann/json/pull/378) ([TurpentineDistillery](https://github.com/TurpentineDistillery)) + +## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10) + +- Heap-buffer-overflow \(OSS-Fuzz issue 367\) [\#412](https://github.com/nlohmann/json/issues/412) +- Heap-buffer-overflow \(OSS-Fuzz issue 366\) [\#411](https://github.com/nlohmann/json/issues/411) +- Use-of-uninitialized-value \(OSS-Fuzz issue 347\) [\#409](https://github.com/nlohmann/json/issues/409) +- Heap-buffer-overflow \(OSS-Fuzz issue 344\) [\#408](https://github.com/nlohmann/json/issues/408) +- Heap-buffer-overflow \(OSS-Fuzz issue 343\) [\#407](https://github.com/nlohmann/json/issues/407) +- Heap-buffer-overflow \(OSS-Fuzz issue 342\) [\#405](https://github.com/nlohmann/json/issues/405) +- strerror throwing error in compiler VS2015 [\#403](https://github.com/nlohmann/json/issues/403) +- json::parse of std::string being underlined by Visual Studio [\#402](https://github.com/nlohmann/json/issues/402) +- Explicitly getting string without .dump\(\) [\#401](https://github.com/nlohmann/json/issues/401) +- Possible to speed up json::parse? [\#398](https://github.com/nlohmann/json/issues/398) +- the alphabetic order in the code influence console\_output. [\#396](https://github.com/nlohmann/json/issues/396) +- Execute tests with clang sanitizers [\#394](https://github.com/nlohmann/json/issues/394) +- Check if library can be used with ETL [\#361](https://github.com/nlohmann/json/issues/361) + +- Feature/clang sanitize [\#410](https://github.com/nlohmann/json/pull/410) ([Daniel599](https://github.com/Daniel599)) +- Add Doozer build badge [\#400](https://github.com/nlohmann/json/pull/400) ([andoma](https://github.com/andoma)) + +## [v2.0.9](https://github.com/nlohmann/json/releases/tag/v2.0.9) (2016-12-16) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.8...v2.0.9) + +- \#pragma GCC diagnostic ignored "-Wdocumentation" [\#393](https://github.com/nlohmann/json/issues/393) +- How to parse this json file and write separate sub object as json files? [\#392](https://github.com/nlohmann/json/issues/392) +- Integer-overflow \(OSS-Fuzz issue 267\) [\#389](https://github.com/nlohmann/json/issues/389) +- Implement indefinite-length types from RFC 7049 [\#387](https://github.com/nlohmann/json/issues/387) +- template parameter "T" is not used in declaring the parameter types of function template [\#386](https://github.com/nlohmann/json/issues/386) +- Serializing json instances containing already serialized string values without escaping [\#385](https://github.com/nlohmann/json/issues/385) +- Add test cases from RFC 7049 [\#384](https://github.com/nlohmann/json/issues/384) +- Add a table of contents to the README file [\#383](https://github.com/nlohmann/json/issues/383) +- Update FAQ section in the guidelines for contributing [\#382](https://github.com/nlohmann/json/issues/382) +- Allow for forward declaring nlohmann::json [\#381](https://github.com/nlohmann/json/issues/381) +- Bug in overflow detection when parsing integers [\#380](https://github.com/nlohmann/json/issues/380) +- A unique name to mention the library? [\#377](https://github.com/nlohmann/json/issues/377) +- Support for comments. [\#376](https://github.com/nlohmann/json/issues/376) +- Non-unique keys in objects. [\#375](https://github.com/nlohmann/json/issues/375) +- Request: binary serialization/deserialization [\#358](https://github.com/nlohmann/json/issues/358) + +- Replace class iterator and const\_iterator by using a single template class to reduce code. [\#395](https://github.com/nlohmann/json/pull/395) ([Bosswestfalen](https://github.com/Bosswestfalen)) +- Clang: quiet a warning [\#391](https://github.com/nlohmann/json/pull/391) ([jaredgrubb](https://github.com/jaredgrubb)) +- Fix issue \#380: Signed integer overflow check [\#390](https://github.com/nlohmann/json/pull/390) ([qwename](https://github.com/qwename)) + +## [v2.0.8](https://github.com/nlohmann/json/releases/tag/v2.0.8) (2016-12-02) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.7...v2.0.8) + +- Reading from file [\#374](https://github.com/nlohmann/json/issues/374) +- Compiler warnings? [\#372](https://github.com/nlohmann/json/issues/372) +- docs: how to release a json object in memory? [\#371](https://github.com/nlohmann/json/issues/371) +- crash in dump [\#370](https://github.com/nlohmann/json/issues/370) +- Coverity issue \(FORWARD\_NULL\) in lexer\(std::istream& s\) [\#368](https://github.com/nlohmann/json/issues/368) +- json::parse on failed stream gets stuck [\#366](https://github.com/nlohmann/json/issues/366) +- Performance improvements [\#365](https://github.com/nlohmann/json/issues/365) +- 'to\_string' is not a member of 'std' [\#364](https://github.com/nlohmann/json/issues/364) +- Optional comment support. [\#363](https://github.com/nlohmann/json/issues/363) +- Crash in dump\(\) from a static object [\#359](https://github.com/nlohmann/json/issues/359) +- json::parse\(...\) vs json j; j.parse\(...\) [\#357](https://github.com/nlohmann/json/issues/357) +- Hi, is there any method to dump json to string with the insert order rather than alphabets [\#356](https://github.com/nlohmann/json/issues/356) +- Provide an example of reading from an json with only a key that has an array of strings. [\#354](https://github.com/nlohmann/json/issues/354) +- Request: access with default value. [\#353](https://github.com/nlohmann/json/issues/353) +- {} and \[\] causes parser error. [\#352](https://github.com/nlohmann/json/issues/352) +- Reading a JSON file into a JSON object [\#351](https://github.com/nlohmann/json/issues/351) +- Request: 'emplace\_back' [\#349](https://github.com/nlohmann/json/issues/349) +- Is it possible to stream data through the json parser without storing everything in memory? [\#347](https://github.com/nlohmann/json/issues/347) +- pure virtual conversion operator [\#346](https://github.com/nlohmann/json/issues/346) +- Floating point precision lost [\#345](https://github.com/nlohmann/json/issues/345) +- unit-conversions SIGSEGV on armv7hl [\#303](https://github.com/nlohmann/json/issues/303) +- Coverity scan fails [\#299](https://github.com/nlohmann/json/issues/299) +- Using QString as string type [\#274](https://github.com/nlohmann/json/issues/274) + +## [v2.0.7](https://github.com/nlohmann/json/releases/tag/v2.0.7) (2016-11-02) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.6...v2.0.7) + +- JSON5 [\#348](https://github.com/nlohmann/json/issues/348) +- Check "Parsing JSON is a Minefield" [\#344](https://github.com/nlohmann/json/issues/344) +- Allow hex numbers [\#342](https://github.com/nlohmann/json/issues/342) +- Convert strings to numbers [\#341](https://github.com/nlohmann/json/issues/341) +- ""-operators ignore the length parameter [\#340](https://github.com/nlohmann/json/issues/340) +- JSON into std::tuple [\#339](https://github.com/nlohmann/json/issues/339) +- JSON into vector [\#335](https://github.com/nlohmann/json/issues/335) +- Installing with Homebrew on Mac Errors \(El Capitan\) [\#331](https://github.com/nlohmann/json/issues/331) +- g++ make check results in error [\#312](https://github.com/nlohmann/json/issues/312) +- Cannot convert from 'json' to 'char' [\#276](https://github.com/nlohmann/json/issues/276) +- Please add a Pretty-Print option for arrays to stay always in one line [\#229](https://github.com/nlohmann/json/issues/229) +- Conversion to STL map\\> gives error [\#220](https://github.com/nlohmann/json/issues/220) +- std::unorderd\_map cannot be used as ObjectType [\#164](https://github.com/nlohmann/json/issues/164) + +- fix minor grammar/style issue in README.md [\#336](https://github.com/nlohmann/json/pull/336) ([seeekr](https://github.com/seeekr)) + +## [v2.0.6](https://github.com/nlohmann/json/releases/tag/v2.0.6) (2016-10-15) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.5...v2.0.6) + +- How to handle json files? [\#333](https://github.com/nlohmann/json/issues/333) +- This file requires compiler and library support .... [\#332](https://github.com/nlohmann/json/issues/332) +- Segmentation fault on saving json to file [\#326](https://github.com/nlohmann/json/issues/326) +- parse error - unexpected \ with 2.0.5 [\#325](https://github.com/nlohmann/json/issues/325) +- Add nested object capability to pointers [\#323](https://github.com/nlohmann/json/issues/323) +- Fix usage examples' comments for std::multiset [\#322](https://github.com/nlohmann/json/issues/322) +- json\_unit runs forever when executed in build directory [\#319](https://github.com/nlohmann/json/issues/319) +- Visual studio 2015 update3 true != TRUE [\#317](https://github.com/nlohmann/json/issues/317) +- releasing single header file in compressed format [\#316](https://github.com/nlohmann/json/issues/316) +- json object from std::ifstream [\#315](https://github.com/nlohmann/json/issues/315) + +- make has\_mapped\_type struct friendly [\#324](https://github.com/nlohmann/json/pull/324) ([vpetrigo](https://github.com/vpetrigo)) +- Fix usage examples' comments for std::multiset [\#321](https://github.com/nlohmann/json/pull/321) ([vasild](https://github.com/vasild)) +- Include dir relocation [\#318](https://github.com/nlohmann/json/pull/318) ([ChristophJud](https://github.com/ChristophJud)) +- trivial documentation fix [\#313](https://github.com/nlohmann/json/pull/313) ([5tefan](https://github.com/5tefan)) + +## [v2.0.5](https://github.com/nlohmann/json/releases/tag/v2.0.5) (2016-09-14) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.4...v2.0.5) + +- \[feature request\]: schema validator and comments [\#311](https://github.com/nlohmann/json/issues/311) +- make json\_benchmarks no longer working in 2.0.4 [\#310](https://github.com/nlohmann/json/issues/310) +- Segmentation fault \(core dumped\) [\#309](https://github.com/nlohmann/json/issues/309) +- No matching member function for call to 'get\_impl' [\#308](https://github.com/nlohmann/json/issues/308) + +## [v2.0.4](https://github.com/nlohmann/json/releases/tag/v2.0.4) (2016-09-11) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.3...v2.0.4) + +- Parsing fails without space at end of file [\#306](https://github.com/nlohmann/json/issues/306) +- json schema validator [\#305](https://github.com/nlohmann/json/issues/305) +- Unused variable warning [\#304](https://github.com/nlohmann/json/issues/304) + +## [v2.0.3](https://github.com/nlohmann/json/releases/tag/v2.0.3) (2016-08-31) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.2...v2.0.3) + +- warning C4706: assignment within conditional expression [\#295](https://github.com/nlohmann/json/issues/295) +- Strip comments / Minify [\#294](https://github.com/nlohmann/json/issues/294) +- Q: Is it possible to build json tree from already UTF8 encoded values? [\#293](https://github.com/nlohmann/json/issues/293) +- Equality operator results in array when assigned object [\#292](https://github.com/nlohmann/json/issues/292) +- Support for integers not from the range \[-\(2\*\*53\)+1, \(2\*\*53\)-1\] in parser [\#291](https://github.com/nlohmann/json/issues/291) +- Support for iterator-range parsing [\#290](https://github.com/nlohmann/json/issues/290) +- Horribly inconsistent behavior between const/non-const reference in operator \[\] \(\) [\#289](https://github.com/nlohmann/json/issues/289) +- Silently get numbers into smaller types [\#288](https://github.com/nlohmann/json/issues/288) +- Incorrect parsing of large int64\_t numbers [\#287](https://github.com/nlohmann/json/issues/287) +- \[question\]: macro to disable floating point support [\#284](https://github.com/nlohmann/json/issues/284) + +- unit-constructor1.cpp: Fix floating point truncation warning [\#300](https://github.com/nlohmann/json/pull/300) ([t-b](https://github.com/t-b)) + +## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-31) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2) + +- can function dump\(\) return string in the order I push in the json object ? [\#286](https://github.com/nlohmann/json/issues/286) +- Error on the Mac: Undefined symbols for architecture x86\_64 [\#285](https://github.com/nlohmann/json/issues/285) +- value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283) +- Build error for std::int64 [\#282](https://github.com/nlohmann/json/issues/282) +- strings can't be accessed after dump\(\)-\>parse\(\) - type is lost [\#281](https://github.com/nlohmann/json/issues/281) +- Easy serialization of classes [\#280](https://github.com/nlohmann/json/issues/280) +- recursive data structures [\#277](https://github.com/nlohmann/json/issues/277) +- hexify\(\) function emits conversion warning [\#270](https://github.com/nlohmann/json/issues/270) + +- let the makefile choose the correct sed [\#279](https://github.com/nlohmann/json/pull/279) ([murinicanor](https://github.com/murinicanor)) +- Update hexify to use array lookup instead of ternary \(\#270\) [\#275](https://github.com/nlohmann/json/pull/275) ([dtoma](https://github.com/dtoma)) + +## [v2.0.1](https://github.com/nlohmann/json/releases/tag/v2.0.1) (2016-06-28) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.0...v2.0.1) + +- Compilation error. [\#273](https://github.com/nlohmann/json/issues/273) +- dump\(\) performance degradation in v2 [\#272](https://github.com/nlohmann/json/issues/272) + +- fixed a tiny typo [\#271](https://github.com/nlohmann/json/pull/271) ([feroldi](https://github.com/feroldi)) + +## [v2.0.0](https://github.com/nlohmann/json/releases/tag/v2.0.0) (2016-06-23) +[Full Changelog](https://github.com/nlohmann/json/compare/v1.1.0...v2.0.0) + +- json::diff generates incorrect patch when removing multiple array elements. [\#269](https://github.com/nlohmann/json/issues/269) +- Docs - What does Json\[key\] return? [\#267](https://github.com/nlohmann/json/issues/267) +- Compiler Errors With JSON.hpp [\#265](https://github.com/nlohmann/json/issues/265) +- Ambiguous push\_back and operator+= overloads [\#263](https://github.com/nlohmann/json/issues/263) +- Preseving order of items in json [\#262](https://github.com/nlohmann/json/issues/262) +- '\' char problem in strings [\#261](https://github.com/nlohmann/json/issues/261) +- VS2015 compile fail [\#260](https://github.com/nlohmann/json/issues/260) +- -Wconversion warning [\#259](https://github.com/nlohmann/json/issues/259) +- Maybe a bug [\#258](https://github.com/nlohmann/json/issues/258) +- Few tests failed on Visual C++ 2015 [\#257](https://github.com/nlohmann/json/issues/257) +- Access keys when iteration with new for loop C++11 [\#256](https://github.com/nlohmann/json/issues/256) +- multiline text values [\#255](https://github.com/nlohmann/json/issues/255) +- Error when using json in g++ [\#254](https://github.com/nlohmann/json/issues/254) +- is the release 2.0? [\#253](https://github.com/nlohmann/json/issues/253) +- concatenate objects [\#252](https://github.com/nlohmann/json/issues/252) +- Encoding [\#251](https://github.com/nlohmann/json/issues/251) +- Unable to build example for constructing json object with stringstreams [\#250](https://github.com/nlohmann/json/issues/250) +- Hexadecimal support [\#249](https://github.com/nlohmann/json/issues/249) +- Update long-term goals [\#246](https://github.com/nlohmann/json/issues/246) +- Contribution To This Json Project [\#245](https://github.com/nlohmann/json/issues/245) +- Trouble using parser with initial dictionary [\#243](https://github.com/nlohmann/json/issues/243) +- Unit test fails when doing a CMake out-of-tree build [\#241](https://github.com/nlohmann/json/issues/241) +- -Wconversion warnings [\#239](https://github.com/nlohmann/json/issues/239) +- Additional integration options [\#237](https://github.com/nlohmann/json/issues/237) +- .get\\(\) works for non spaced string but returns as array for spaced/longer strings [\#236](https://github.com/nlohmann/json/issues/236) +- ambiguous overload for 'push\_back' and 'operator+=' [\#235](https://github.com/nlohmann/json/issues/235) +- Can't use basic\_json::iterator as a base iterator for std::move\_iterator [\#233](https://github.com/nlohmann/json/issues/233) +- json object's creation can freezes execution [\#231](https://github.com/nlohmann/json/issues/231) +- Incorrect dumping of parsed numbers with exponents, but without decimal places [\#230](https://github.com/nlohmann/json/issues/230) +- double values are serialized with commas as decimal points [\#228](https://github.com/nlohmann/json/issues/228) +- Move semantics with std::initializer\_list [\#225](https://github.com/nlohmann/json/issues/225) +- replace emplace [\#224](https://github.com/nlohmann/json/issues/224) +- abort during getline in yyfill [\#223](https://github.com/nlohmann/json/issues/223) +- free\(\): invalid pointer error in GCC 5.2.1 [\#221](https://github.com/nlohmann/json/issues/221) +- Error compile Android NDK error: 'strtof' is not a member of 'std' [\#219](https://github.com/nlohmann/json/issues/219) +- Wrong link in the README.md [\#217](https://github.com/nlohmann/json/issues/217) +- Wide character strings not supported [\#216](https://github.com/nlohmann/json/issues/216) +- Memory allocations using range-based for loops [\#214](https://github.com/nlohmann/json/issues/214) +- would you like to support gcc 4.8.1? [\#211](https://github.com/nlohmann/json/issues/211) +- Reading concatenated json's from an istream [\#210](https://github.com/nlohmann/json/issues/210) +- Conflicting typedef of ssize\_t on Windows 32 bit when using Boost.Python [\#204](https://github.com/nlohmann/json/issues/204) +- Inconsistency between operator\[\] and push\_back [\#203](https://github.com/nlohmann/json/issues/203) +- Small bugs in json.hpp \(get\_number\) and unit.cpp \(non-standard integer type test\) [\#199](https://github.com/nlohmann/json/issues/199) +- GCC/clang floating point parsing bug in strtod\(\) [\#195](https://github.com/nlohmann/json/issues/195) +- What is within scope? [\#192](https://github.com/nlohmann/json/issues/192) +- Bugs in miloyip/nativejson-benchmark: roundtrips [\#187](https://github.com/nlohmann/json/issues/187) +- Floating point exceptions [\#181](https://github.com/nlohmann/json/issues/181) +- Integer conversion to unsigned [\#178](https://github.com/nlohmann/json/issues/178) +- map string string fails to compile [\#176](https://github.com/nlohmann/json/issues/176) +- In basic\_json::basic\_json\(const CompatibleArrayType& val\), the requirement of CompatibleArrayType is not strict enough. [\#174](https://github.com/nlohmann/json/issues/174) +- Provide a FAQ [\#163](https://github.com/nlohmann/json/issues/163) +- Implicit assignment to std::string fails [\#144](https://github.com/nlohmann/json/issues/144) + +- Fix Issue \#265 [\#266](https://github.com/nlohmann/json/pull/266) ([06needhamt](https://github.com/06needhamt)) +- Define CMake/CTest tests [\#247](https://github.com/nlohmann/json/pull/247) ([robertmrk](https://github.com/robertmrk)) +- Out of tree builds and a few other miscellaneous CMake cleanups. [\#242](https://github.com/nlohmann/json/pull/242) ([ChrisKitching](https://github.com/ChrisKitching)) +- Implement additional integration options [\#238](https://github.com/nlohmann/json/pull/238) ([robertmrk](https://github.com/robertmrk)) +- make serialization locale-independent [\#232](https://github.com/nlohmann/json/pull/232) ([nlohmann](https://github.com/nlohmann)) +- fixes \#223 by updating README.md [\#227](https://github.com/nlohmann/json/pull/227) ([kevin--](https://github.com/kevin--)) +- Use namespace std for int64\_t and uint64\_t [\#226](https://github.com/nlohmann/json/pull/226) ([lv-zheng](https://github.com/lv-zheng)) +- Added missing cerrno header to fix ERANGE compile error on android [\#222](https://github.com/nlohmann/json/pull/222) ([Teemperor](https://github.com/Teemperor)) +- Corrected readme [\#218](https://github.com/nlohmann/json/pull/218) ([Annihil](https://github.com/Annihil)) +- Create PULL\_REQUEST\_TEMPLATE.md [\#213](https://github.com/nlohmann/json/pull/213) ([whackashoe](https://github.com/whackashoe)) +- fixed noexcept; added constexpr [\#208](https://github.com/nlohmann/json/pull/208) ([nlohmann](https://github.com/nlohmann)) +- Add support for afl-fuzz testing [\#207](https://github.com/nlohmann/json/pull/207) ([mykter](https://github.com/mykter)) +- replaced ssize\_t occurrences with auto \(addresses \#204\) [\#205](https://github.com/nlohmann/json/pull/205) ([nlohmann](https://github.com/nlohmann)) +- Fixed issue \#199 - Small bugs in json.hpp \(get\_number\) and unit.cpp \(non-standard integer type test\) [\#200](https://github.com/nlohmann/json/pull/200) ([twelsby](https://github.com/twelsby)) +- Fix broken link [\#197](https://github.com/nlohmann/json/pull/197) ([vog](https://github.com/vog)) +- Issue \#195 - update Travis to Trusty due to gcc/clang strtod\(\) bug [\#196](https://github.com/nlohmann/json/pull/196) ([twelsby](https://github.com/twelsby)) +- Issue \#178 - Extending support to full uint64\_t/int64\_t range and unsigned type \(updated\) [\#193](https://github.com/nlohmann/json/pull/193) ([twelsby](https://github.com/twelsby)) + +## [v1.1.0](https://github.com/nlohmann/json/releases/tag/v1.1.0) (2016-01-24) +[Full Changelog](https://github.com/nlohmann/json/compare/v1.0.0...v1.1.0) + +- Small error in pull \#185 [\#194](https://github.com/nlohmann/json/issues/194) +- Bugs in miloyip/nativejson-benchmark: floating-point parsing [\#186](https://github.com/nlohmann/json/issues/186) +- Floating point equality [\#185](https://github.com/nlohmann/json/issues/185) +- Unused variables in catch [\#180](https://github.com/nlohmann/json/issues/180) +- Typo in documentation [\#179](https://github.com/nlohmann/json/issues/179) +- JSON performance benchmark comparision [\#177](https://github.com/nlohmann/json/issues/177) +- Since re2c is often ignored in pull requests, it may make sense to make a contributing.md file [\#175](https://github.com/nlohmann/json/issues/175) +- Question about exceptions [\#173](https://github.com/nlohmann/json/issues/173) +- Android? [\#172](https://github.com/nlohmann/json/issues/172) +- Cannot index by key of type static constexpr const char\* [\#171](https://github.com/nlohmann/json/issues/171) +- Add assertions [\#168](https://github.com/nlohmann/json/issues/168) +- MSVC 2015 build fails when attempting to compare object\_t [\#167](https://github.com/nlohmann/json/issues/167) +- Member detector is not portable [\#166](https://github.com/nlohmann/json/issues/166) +- Unnecessary const\_cast [\#162](https://github.com/nlohmann/json/issues/162) +- Question about get\_ref\(\) [\#128](https://github.com/nlohmann/json/issues/128) +- range based for loop for objects [\#83](https://github.com/nlohmann/json/issues/83) +- Consider submitting this to the Boost Library Incubator [\#66](https://github.com/nlohmann/json/issues/66) + +- Fixed Issue \#186 - add strto\(f|d|ld\) overload wrappers, "-0.0" special case and FP trailing zero [\#191](https://github.com/nlohmann/json/pull/191) ([twelsby](https://github.com/twelsby)) +- Issue \#185 - remove approx\(\) and use \#pragma to kill warnings [\#190](https://github.com/nlohmann/json/pull/190) ([twelsby](https://github.com/twelsby)) +- Fixed Issue \#171 - added two extra template overloads of operator\[\] for T\* arguments [\#189](https://github.com/nlohmann/json/pull/189) ([twelsby](https://github.com/twelsby)) +- Fixed issue \#167 - removed operator ValueType\(\) condition for VS2015 [\#188](https://github.com/nlohmann/json/pull/188) ([twelsby](https://github.com/twelsby)) +- Implementation of get\_ref\(\) [\#184](https://github.com/nlohmann/json/pull/184) ([dariomt](https://github.com/dariomt)) +- Fixed some typos in CONTRIBUTING.md [\#182](https://github.com/nlohmann/json/pull/182) ([nibroc](https://github.com/nibroc)) + +## [v1.0.0](https://github.com/nlohmann/json/releases/tag/v1.0.0) (2015-12-27) +[Full Changelog](https://github.com/nlohmann/json/compare/v1.0.0-rc1...v1.0.0) + +- add key name to exception [\#160](https://github.com/nlohmann/json/issues/160) +- Getting member discarding qualifyer [\#159](https://github.com/nlohmann/json/issues/159) +- basic\_json::iterator::value\(\) output includes quotes while basic\_json::iterator::key\(\) doesn't [\#158](https://github.com/nlohmann/json/issues/158) +- Indexing `const basic\_json\<\>` with `const basic\_string\` [\#157](https://github.com/nlohmann/json/issues/157) +- token\_type\_name\(token\_type t\): not all control paths return a value [\#156](https://github.com/nlohmann/json/issues/156) +- prevent json.hpp from emitting compiler warnings [\#154](https://github.com/nlohmann/json/issues/154) +- json::parse\(string\) does not check utf8 bom [\#152](https://github.com/nlohmann/json/issues/152) +- unsigned 64bit values output as signed [\#151](https://github.com/nlohmann/json/issues/151) +- Wish feature: json5 [\#150](https://github.com/nlohmann/json/issues/150) +- Unable to compile on MSVC 2015 with SDL checking enabled: This function or variable may be unsafe. [\#149](https://github.com/nlohmann/json/issues/149) +- "Json Object" type does not keep object order [\#148](https://github.com/nlohmann/json/issues/148) +- dump\(\) convert strings encoded by utf-8 to shift-jis on windows 10. [\#147](https://github.com/nlohmann/json/issues/147) +- Unable to get field names in a json object [\#145](https://github.com/nlohmann/json/issues/145) +- Question: Is the use of incomplete type correct? [\#138](https://github.com/nlohmann/json/issues/138) +- json.hpp:5746:32: error: 'to\_string' is not a member of 'std' [\#136](https://github.com/nlohmann/json/issues/136) +- Bug in basic\_json::operator\[\] const overload [\#135](https://github.com/nlohmann/json/issues/135) +- wrong enable\_if for const pointer \(instead of pointer-to-const\) [\#134](https://github.com/nlohmann/json/issues/134) +- overload of at\(\) with default value [\#133](https://github.com/nlohmann/json/issues/133) +- Splitting source [\#132](https://github.com/nlohmann/json/issues/132) +- Question about get\_ptr\(\) [\#127](https://github.com/nlohmann/json/issues/127) +- Visual Studio 14 Debug assertion failed [\#125](https://github.com/nlohmann/json/issues/125) +- Memory leak in face of exceptions [\#118](https://github.com/nlohmann/json/issues/118) +- Find and Count for arrays [\#117](https://github.com/nlohmann/json/issues/117) +- dynamically constructing an arbitrarily nested object [\#114](https://github.com/nlohmann/json/issues/114) +- Returning any data type [\#113](https://github.com/nlohmann/json/issues/113) +- Compile error with g++ 4.9.3 cygwin 64-bit [\#112](https://github.com/nlohmann/json/issues/112) +- insert json array issue with gcc4.8.2 [\#110](https://github.com/nlohmann/json/issues/110) +- error: unterminated raw string [\#109](https://github.com/nlohmann/json/issues/109) +- vector\ copy constructor really weird [\#108](https://github.com/nlohmann/json/issues/108) +- \[clang-3.6.2\] string/sstream with number to json issue [\#107](https://github.com/nlohmann/json/issues/107) +- maintaining order of keys during iteration [\#106](https://github.com/nlohmann/json/issues/106) +- object field accessors [\#103](https://github.com/nlohmann/json/issues/103) +- v8pp and json [\#95](https://github.com/nlohmann/json/issues/95) +- Wishlist [\#65](https://github.com/nlohmann/json/issues/65) +- Windows/Visual Studio \(through 2013\) is unsupported [\#62](https://github.com/nlohmann/json/issues/62) + +- Replace sprintf with hex function, this fixes \#149 [\#153](https://github.com/nlohmann/json/pull/153) ([whackashoe](https://github.com/whackashoe)) +- Fix character skipping after a surrogate pair [\#146](https://github.com/nlohmann/json/pull/146) ([robertmrk](https://github.com/robertmrk)) +- Detect correctly pointer-to-const [\#137](https://github.com/nlohmann/json/pull/137) ([dariomt](https://github.com/dariomt)) +- disabled "CopyAssignable" test for MSVC in Debug mode, see \#125 [\#131](https://github.com/nlohmann/json/pull/131) ([dariomt](https://github.com/dariomt)) +- removed stream operator for iterator, resolution for \#125 [\#130](https://github.com/nlohmann/json/pull/130) ([dariomt](https://github.com/dariomt)) +- fixed typos in comments for examples [\#129](https://github.com/nlohmann/json/pull/129) ([dariomt](https://github.com/dariomt)) +- Remove superfluous inefficiency [\#126](https://github.com/nlohmann/json/pull/126) ([d-frey](https://github.com/d-frey)) +- remove invalid parameter '-stdlib=libc++' in CMakeLists.txt [\#124](https://github.com/nlohmann/json/pull/124) ([emvivre](https://github.com/emvivre)) +- exception-safe object creation, fixes \#118 [\#122](https://github.com/nlohmann/json/pull/122) ([d-frey](https://github.com/d-frey)) +- Fix small oversight. [\#121](https://github.com/nlohmann/json/pull/121) ([ColinH](https://github.com/ColinH)) +- Overload parse\(\) to accept an rvalue reference [\#120](https://github.com/nlohmann/json/pull/120) ([silverweed](https://github.com/silverweed)) +- Use the right variable name in doc string [\#115](https://github.com/nlohmann/json/pull/115) ([whoshuu](https://github.com/whoshuu)) + +## [v1.0.0-rc1](https://github.com/nlohmann/json/releases/tag/v1.0.0-rc1) (2015-07-26) +- Finish documenting the public interface in Doxygen [\#102](https://github.com/nlohmann/json/issues/102) +- Binary string causes numbers to be dumped as hex [\#101](https://github.com/nlohmann/json/issues/101) +- failed to iterator json object with reverse\_iterator [\#100](https://github.com/nlohmann/json/issues/100) +- 'noexcept' : unknown override specifier [\#99](https://github.com/nlohmann/json/issues/99) +- json float parsing problem [\#98](https://github.com/nlohmann/json/issues/98) +- Adjust wording to JSON RFC [\#97](https://github.com/nlohmann/json/issues/97) +- 17 MB / 90 MB repo size!? [\#96](https://github.com/nlohmann/json/issues/96) +- static analysis warnings [\#94](https://github.com/nlohmann/json/issues/94) +- reverse\_iterator operator inheritance problem [\#93](https://github.com/nlohmann/json/issues/93) +- init error [\#92](https://github.com/nlohmann/json/issues/92) +- access by \(const\) reference [\#91](https://github.com/nlohmann/json/issues/91) +- is\_integer and is\_float tests [\#90](https://github.com/nlohmann/json/issues/90) +- Nonstandard integer type [\#89](https://github.com/nlohmann/json/issues/89) +- static library build [\#84](https://github.com/nlohmann/json/issues/84) +- lexer::get\_number return NAN [\#82](https://github.com/nlohmann/json/issues/82) +- MinGW have no std::to\_string [\#80](https://github.com/nlohmann/json/issues/80) +- Incorrect behaviour of basic\_json::count method [\#78](https://github.com/nlohmann/json/issues/78) +- Invoking is\_array\(\) function creates "null" value [\#77](https://github.com/nlohmann/json/issues/77) +- dump\(\) / parse\(\) not idempotent [\#76](https://github.com/nlohmann/json/issues/76) +- Handle infinity and NaN cases [\#70](https://github.com/nlohmann/json/issues/70) +- errors in g++-4.8.1 [\#68](https://github.com/nlohmann/json/issues/68) +- Keys when iterating over objects [\#67](https://github.com/nlohmann/json/issues/67) +- Compilation results in tons of warnings [\#64](https://github.com/nlohmann/json/issues/64) +- Complete brief documentation [\#61](https://github.com/nlohmann/json/issues/61) +- Double quotation mark is not parsed correctly [\#60](https://github.com/nlohmann/json/issues/60) +- Get coverage back to 100% [\#58](https://github.com/nlohmann/json/issues/58) +- erase elements using iterators [\#57](https://github.com/nlohmann/json/issues/57) +- Removing item from array [\#56](https://github.com/nlohmann/json/issues/56) +- Serialize/Deserialize like PHP? [\#55](https://github.com/nlohmann/json/issues/55) +- Numbers as keys [\#54](https://github.com/nlohmann/json/issues/54) +- Why are elements alphabetized on key while iterating? [\#53](https://github.com/nlohmann/json/issues/53) +- Document erase, count, and iterators key and value [\#52](https://github.com/nlohmann/json/issues/52) +- Do not use std::to\_string [\#51](https://github.com/nlohmann/json/issues/51) +- Supported compilers [\#50](https://github.com/nlohmann/json/issues/50) +- Confused about iterating through json objects [\#49](https://github.com/nlohmann/json/issues/49) +- Use non-member begin/end [\#48](https://github.com/nlohmann/json/issues/48) +- Erase key [\#47](https://github.com/nlohmann/json/issues/47) +- Key iterator [\#46](https://github.com/nlohmann/json/issues/46) +- Add count member function [\#45](https://github.com/nlohmann/json/issues/45) +- Problem getting vector \(array\) of strings [\#44](https://github.com/nlohmann/json/issues/44) +- Compilation error due to assuming that private=public [\#43](https://github.com/nlohmann/json/issues/43) +- Use of deprecated implicit copy constructor [\#42](https://github.com/nlohmann/json/issues/42) +- Printing attribute names [\#39](https://github.com/nlohmann/json/issues/39) +- dumping a small number\_float just outputs 0.000000 [\#37](https://github.com/nlohmann/json/issues/37) +- find is error [\#32](https://github.com/nlohmann/json/issues/32) +- Avoid using spaces when encoding without pretty print [\#31](https://github.com/nlohmann/json/issues/31) +- Cannot encode long numbers [\#30](https://github.com/nlohmann/json/issues/30) +- segmentation fault when iterating over empty arrays/objects [\#28](https://github.com/nlohmann/json/issues/28) +- Creating an empty array [\#27](https://github.com/nlohmann/json/issues/27) +- Custom allocator support [\#25](https://github.com/nlohmann/json/issues/25) +- make the type of the used string container customizable [\#20](https://github.com/nlohmann/json/issues/20) +- Improper parsing of JSON string "\\" [\#17](https://github.com/nlohmann/json/issues/17) +- create a header-only version [\#16](https://github.com/nlohmann/json/issues/16) +- Don't return "const values" [\#15](https://github.com/nlohmann/json/issues/15) +- Add to\_string overload for indentation [\#13](https://github.com/nlohmann/json/issues/13) +- string parser does not recognize uncompliant strings [\#12](https://github.com/nlohmann/json/issues/12) +- possible double-free in find function [\#11](https://github.com/nlohmann/json/issues/11) +- UTF-8 encoding/deconding/testing [\#10](https://github.com/nlohmann/json/issues/10) +- move code into namespace [\#9](https://github.com/nlohmann/json/issues/9) +- free functions for explicit objects and arrays in initializer lists [\#8](https://github.com/nlohmann/json/issues/8) +- unique\_ptr for ownership [\#7](https://github.com/nlohmann/json/issues/7) +- Add unit tests [\#4](https://github.com/nlohmann/json/issues/4) +- Drop C++98 support [\#3](https://github.com/nlohmann/json/issues/3) +- Test case coverage [\#2](https://github.com/nlohmann/json/issues/2) +- Runtime error in Travis job [\#1](https://github.com/nlohmann/json/issues/1) + +- Keyword 'inline' is useless when member functions are defined in headers [\#87](https://github.com/nlohmann/json/pull/87) ([ahamez](https://github.com/ahamez)) +- Remove useless typename [\#86](https://github.com/nlohmann/json/pull/86) ([ahamez](https://github.com/ahamez)) +- Avoid warning with Xcode's clang [\#85](https://github.com/nlohmann/json/pull/85) ([ahamez](https://github.com/ahamez)) +- Fix typos [\#73](https://github.com/nlohmann/json/pull/73) ([aqnouch](https://github.com/aqnouch)) +- Replace `default\_callback` function with `nullptr` and check for null… [\#72](https://github.com/nlohmann/json/pull/72) ([aburgh](https://github.com/aburgh)) +- support enum [\#71](https://github.com/nlohmann/json/pull/71) ([likebeta](https://github.com/likebeta)) +- Fix performance regression introduced with the parsing callback feature. [\#69](https://github.com/nlohmann/json/pull/69) ([aburgh](https://github.com/aburgh)) +- Improve the implementations of the comparission-operators [\#63](https://github.com/nlohmann/json/pull/63) ([Florianjw](https://github.com/Florianjw)) +- Fix compilation of json\_unit with GCC 5 [\#59](https://github.com/nlohmann/json/pull/59) ([dkopecek](https://github.com/dkopecek)) +- Parse streams incrementally. [\#40](https://github.com/nlohmann/json/pull/40) ([aburgh](https://github.com/aburgh)) +- Feature/small float serialization [\#38](https://github.com/nlohmann/json/pull/38) ([jrandall](https://github.com/jrandall)) +- template version with re2c scanner [\#36](https://github.com/nlohmann/json/pull/36) ([nlohmann](https://github.com/nlohmann)) +- more descriptive documentation in example [\#33](https://github.com/nlohmann/json/pull/33) ([luxe](https://github.com/luxe)) +- Fix string conversion under Clang [\#26](https://github.com/nlohmann/json/pull/26) ([wancw](https://github.com/wancw)) +- Fixed dumping of strings [\#24](https://github.com/nlohmann/json/pull/24) ([Teemperor](https://github.com/Teemperor)) +- Added a remark to the readme that coverage is GCC only for now [\#23](https://github.com/nlohmann/json/pull/23) ([Teemperor](https://github.com/Teemperor)) +- Unicode escaping [\#22](https://github.com/nlohmann/json/pull/22) ([Teemperor](https://github.com/Teemperor)) +- Implemented the JSON spec for string parsing for everything but the \uXXXX escaping [\#21](https://github.com/nlohmann/json/pull/21) ([Teemperor](https://github.com/Teemperor)) +- add the std iterator typedefs to iterator and const\_iterator [\#19](https://github.com/nlohmann/json/pull/19) ([kirkshoop](https://github.com/kirkshoop)) +- Fixed escaped quotes [\#18](https://github.com/nlohmann/json/pull/18) ([Teemperor](https://github.com/Teemperor)) +- Fix double delete on std::bad\_alloc exception [\#14](https://github.com/nlohmann/json/pull/14) ([elliotgoodrich](https://github.com/elliotgoodrich)) +- Added CMake and lcov [\#6](https://github.com/nlohmann/json/pull/6) ([Teemperor](https://github.com/Teemperor)) +- Version 2.0 [\#5](https://github.com/nlohmann/json/pull/5) ([nlohmann](https://github.com/nlohmann)) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/libraries/cppdap/third_party/json/LICENSE.MIT b/libraries/cppdap/third_party/json/LICENSE.MIT new file mode 100644 index 000000000..db73c5f7d --- /dev/null +++ b/libraries/cppdap/third_party/json/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2019 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libraries/cppdap/third_party/json/Makefile b/libraries/cppdap/third_party/json/Makefile new file mode 100644 index 000000000..4fb303fcb --- /dev/null +++ b/libraries/cppdap/third_party/json/Makefile @@ -0,0 +1,629 @@ +.PHONY: pretty clean ChangeLog.md release + +########################################################################## +# configuration +########################################################################## + +# directory to recent compiler binaries +COMPILER_DIR=/Users/niels/Documents/projects/compilers/local/bin + +# find GNU sed to use `-i` parameter +SED:=$(shell command -v gsed || which sed) + + +########################################################################## +# source files +########################################################################## + +# the list of sources in the include folder +SRCS=$(shell find include -type f | sort) + +# the single header (amalgamated from the source files) +AMALGAMATED_FILE=single_include/nlohmann/json.hpp + + +########################################################################## +# documentation of the Makefile's targets +########################################################################## + +# main target +all: + @echo "amalgamate - amalgamate file single_include/nlohmann/json.hpp from the include/nlohmann sources" + @echo "ChangeLog.md - generate ChangeLog file" + @echo "check - compile and execute test suite" + @echo "check-amalgamation - check whether sources have been amalgamated" + @echo "check-fast - compile and execute test suite (skip long-running tests)" + @echo "clean - remove built files" + @echo "coverage - create coverage information with lcov" + @echo "coverage-fast - create coverage information with fastcov" + @echo "cppcheck - analyze code with cppcheck" + @echo "cpplint - analyze code with cpplint" + @echo "clang_tidy - analyze code with Clang-Tidy" + @echo "clang_analyze - analyze code with Clang-Analyzer" + @echo "doctest - compile example files and check their output" + @echo "fuzz_testing - prepare fuzz testing of the JSON parser" + @echo "fuzz_testing_bson - prepare fuzz testing of the BSON parser" + @echo "fuzz_testing_cbor - prepare fuzz testing of the CBOR parser" + @echo "fuzz_testing_msgpack - prepare fuzz testing of the MessagePack parser" + @echo "fuzz_testing_ubjson - prepare fuzz testing of the UBJSON parser" + @echo "json_unit - create single-file test executable" + @echo "pedantic_clang - run Clang with maximal warning flags" + @echo "pedantic_gcc - run GCC with maximal warning flags" + @echo "pretty - beautify code with Artistic Style" + @echo "run_benchmarks - build and run benchmarks" + + +########################################################################## +# unit tests +########################################################################## + +# build unit tests +json_unit: + @$(MAKE) json_unit -C test + +# run unit tests +check: + $(MAKE) check -C test + +# run unit tests and skip expensive tests +check-fast: + $(MAKE) check -C test TEST_PATTERN="" + + +########################################################################## +# coverage +########################################################################## + +coverage: + rm -fr build_coverage + mkdir build_coverage + cd build_coverage ; CXX=g++-8 cmake .. -GNinja -DJSON_Coverage=ON -DJSON_MultipleHeaders=ON + cd build_coverage ; ninja + cd build_coverage ; ctest -E '.*_default' -j10 + cd build_coverage ; ninja lcov_html + open build_coverage/test/html/index.html + +coverage-fast: + rm -fr build_coverage + mkdir build_coverage + cd build_coverage ; CXX=g++-9 cmake .. -GNinja -DJSON_Coverage=ON -DJSON_MultipleHeaders=ON + cd build_coverage ; ninja + cd build_coverage ; ctest -E '.*_default' -j10 + cd build_coverage ; ninja fastcov_html + open build_coverage/test/html/index.html + +########################################################################## +# documentation tests +########################################################################## + +# compile example files and check output +doctest: + $(MAKE) check_output -C doc + + +########################################################################## +# warning detector +########################################################################## + +# calling Clang with all warnings, except: +# -Wno-c++2a-compat: u8 literals will behave differently in C++20... +# -Wno-deprecated-declarations: the library deprecated some functions +# -Wno-documentation-unknown-command: code uses user-defined commands like @complexity +# -Wno-exit-time-destructors: warning in json code triggered by NLOHMANN_JSON_SERIALIZE_ENUM +# -Wno-float-equal: not all comparisons in the tests can be replaced by Approx +# -Wno-keyword-macro: unit-tests use "#define private public" +# -Wno-padded: padding is nothing to warn about +# -Wno-range-loop-analysis: items tests "for(const auto i...)" +# -Wno-switch-enum -Wno-covered-switch-default: pedantic/contradicting warnings about switches +# -Wno-weak-vtables: exception class is defined inline, but has virtual method +pedantic_clang: + $(MAKE) json_unit CXX=c++ CXXFLAGS=" \ + -std=c++11 -Wno-c++98-compat -Wno-c++98-compat-pedantic \ + -Werror \ + -Weverything \ + -Wno-c++2a-compat \ + -Wno-deprecated-declarations \ + -Wno-documentation-unknown-command \ + -Wno-exit-time-destructors \ + -Wno-float-equal \ + -Wno-keyword-macro \ + -Wno-padded \ + -Wno-range-loop-analysis \ + -Wno-switch-enum -Wno-covered-switch-default \ + -Wno-weak-vtables" + +# calling GCC with most warnings +pedantic_gcc: + $(MAKE) json_unit CXX=/usr/local/bin/g++-9 CXXFLAGS=" \ + -std=c++11 \ + -Waddress \ + -Waddress-of-packed-member \ + -Waggressive-loop-optimizations \ + -Waligned-new=all \ + -Wall \ + -Walloc-zero \ + -Walloca \ + -Warray-bounds \ + -Warray-bounds=2 \ + -Wattribute-alias=2 \ + -Wattribute-warning \ + -Wattributes \ + -Wbool-compare \ + -Wbool-operation \ + -Wbuiltin-declaration-mismatch \ + -Wbuiltin-macro-redefined \ + -Wcannot-profile \ + -Wcast-align \ + -Wcast-function-type \ + -Wcast-qual \ + -Wcatch-value=3 \ + -Wchar-subscripts \ + -Wclass-conversion \ + -Wclass-memaccess \ + -Wclobbered \ + -Wcomment \ + -Wcomments \ + -Wconditionally-supported \ + -Wconversion \ + -Wconversion-null \ + -Wcoverage-mismatch \ + -Wcpp \ + -Wctor-dtor-privacy \ + -Wdangling-else \ + -Wdate-time \ + -Wdelete-incomplete \ + -Wdelete-non-virtual-dtor \ + -Wdeprecated \ + -Wdeprecated-copy \ + -Wdeprecated-copy-dtor \ + -Wdeprecated-declarations \ + -Wdisabled-optimization \ + -Wdiv-by-zero \ + -Wdouble-promotion \ + -Wduplicated-branches \ + -Wduplicated-cond \ + -Weffc++ \ + -Wempty-body \ + -Wendif-labels \ + -Wenum-compare \ + -Wexpansion-to-defined \ + -Werror \ + -Wextra \ + -Wextra-semi \ + -Wfloat-conversion \ + -Wformat \ + -Wformat-contains-nul \ + -Wformat-extra-args \ + -Wformat-nonliteral \ + -Wformat-overflow=2 \ + -Wformat-security \ + -Wformat-signedness \ + -Wformat-truncation=2 \ + -Wformat-y2k \ + -Wformat-zero-length \ + -Wformat=2 \ + -Wframe-address \ + -Wfree-nonheap-object \ + -Whsa \ + -Wif-not-aligned \ + -Wignored-attributes \ + -Wignored-qualifiers \ + -Wimplicit-fallthrough=5 \ + -Winherited-variadic-ctor \ + -Winit-list-lifetime \ + -Winit-self \ + -Winline \ + -Wint-in-bool-context \ + -Wint-to-pointer-cast \ + -Winvalid-memory-model \ + -Winvalid-offsetof \ + -Winvalid-pch \ + -Wliteral-suffix \ + -Wlogical-not-parentheses \ + -Wlogical-op \ + -Wlto-type-mismatch \ + -Wmain \ + -Wmaybe-uninitialized \ + -Wmemset-elt-size \ + -Wmemset-transposed-args \ + -Wmisleading-indentation \ + -Wmissing-attributes \ + -Wmissing-braces \ + -Wmissing-declarations \ + -Wmissing-field-initializers \ + -Wmissing-format-attribute \ + -Wmissing-include-dirs \ + -Wmissing-noreturn \ + -Wmissing-profile \ + -Wmultichar \ + -Wmultiple-inheritance \ + -Wmultistatement-macros \ + -Wnarrowing \ + -Wno-deprecated-declarations \ + -Wno-float-equal \ + -Wno-long-long \ + -Wno-namespaces \ + -Wno-padded \ + -Wno-switch-enum \ + -Wno-system-headers \ + -Wno-templates \ + -Wno-undef \ + -Wnoexcept \ + -Wnoexcept-type \ + -Wnon-template-friend \ + -Wnon-virtual-dtor \ + -Wnonnull \ + -Wnonnull-compare \ + -Wnonportable-cfstrings \ + -Wnormalized \ + -Wnull-dereference \ + -Wodr \ + -Wold-style-cast \ + -Wopenmp-simd \ + -Woverflow \ + -Woverlength-strings \ + -Woverloaded-virtual \ + -Wpacked \ + -Wpacked-bitfield-compat \ + -Wpacked-not-aligned \ + -Wparentheses \ + -Wpedantic \ + -Wpessimizing-move \ + -Wplacement-new=2 \ + -Wpmf-conversions \ + -Wpointer-arith \ + -Wpointer-compare \ + -Wpragmas \ + -Wprio-ctor-dtor \ + -Wpsabi \ + -Wredundant-decls \ + -Wredundant-move \ + -Wregister \ + -Wreorder \ + -Wrestrict \ + -Wreturn-local-addr \ + -Wreturn-type \ + -Wscalar-storage-order \ + -Wsequence-point \ + -Wshadow \ + -Wshadow-compatible-local \ + -Wshadow-local \ + -Wshadow=compatible-local \ + -Wshadow=global \ + -Wshadow=local \ + -Wshift-count-negative \ + -Wshift-count-overflow \ + -Wshift-negative-value \ + -Wshift-overflow=2 \ + -Wsign-compare \ + -Wsign-conversion \ + -Wsign-promo \ + -Wsized-deallocation \ + -Wsizeof-array-argument \ + -Wsizeof-pointer-div \ + -Wsizeof-pointer-memaccess \ + -Wstack-protector \ + -Wstrict-aliasing=3 \ + -Wstrict-null-sentinel \ + -Wstrict-overflow=5 \ + -Wstringop-overflow=4 \ + -Wstringop-truncation \ + -Wsubobject-linkage \ + -Wsuggest-attribute=cold \ + -Wsuggest-attribute=const \ + -Wsuggest-attribute=format \ + -Wsuggest-attribute=malloc \ + -Wsuggest-attribute=noreturn \ + -Wsuggest-attribute=pure \ + -Wsuggest-final-methods \ + -Wsuggest-final-types \ + -Wsuggest-override \ + -Wswitch \ + -Wswitch-bool \ + -Wswitch-default \ + -Wswitch-unreachable \ + -Wsync-nand \ + -Wsynth \ + -Wtautological-compare \ + -Wterminate \ + -Wtrampolines \ + -Wtrigraphs \ + -Wtype-limits \ + -Wuninitialized \ + -Wunknown-pragmas \ + -Wunreachable-code \ + -Wunsafe-loop-optimizations \ + -Wunused \ + -Wunused-but-set-parameter \ + -Wunused-but-set-variable \ + -Wunused-const-variable=2 \ + -Wunused-function \ + -Wunused-label \ + -Wunused-local-typedefs \ + -Wunused-macros \ + -Wunused-parameter \ + -Wunused-result \ + -Wunused-value \ + -Wunused-variable \ + -Wuseless-cast \ + -Wvarargs \ + -Wvariadic-macros \ + -Wvector-operation-performance \ + -Wvirtual-inheritance \ + -Wvirtual-move-assign \ + -Wvla \ + -Wvolatile-register-var \ + -Wwrite-strings \ + -Wzero-as-null-pointer-constant \ + " + +########################################################################## +# benchmarks +########################################################################## + +run_benchmarks: + rm -fr build_benchmarks + mkdir build_benchmarks + cd build_benchmarks ; cmake ../benchmarks -GNinja -DCMAKE_BUILD_TYPE=Release + cd build_benchmarks ; ninja + cd build_benchmarks ; ./json_benchmarks + +########################################################################## +# fuzzing +########################################################################## + +# the overall fuzz testing target +fuzz_testing: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++ + mv test/parse_afl_fuzzer fuzz-testing/fuzzer + find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + +fuzz_testing_bson: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_bson_fuzzer -C test CXX=afl-clang++ + mv test/parse_bson_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.bson | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + +fuzz_testing_cbor: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++ + mv test/parse_cbor_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + +fuzz_testing_msgpack: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_msgpack_fuzzer -C test CXX=afl-clang++ + mv test/parse_msgpack_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + +fuzz_testing_ubjson: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_ubjson_fuzzer -C test CXX=afl-clang++ + mv test/parse_ubjson_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.ubjson | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + +fuzzing-start: + afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer + +fuzzing-stop: + -killall fuzzer + -killall afl-fuzz + + +########################################################################## +# Static analysis +########################################################################## + +# call cppcheck +# Note: this target is called by Travis +cppcheck: + cppcheck --enable=warning --inline-suppr --inconclusive --force --std=c++11 $(AMALGAMATED_FILE) --error-exitcode=1 + +# call Clang Static Analyzer +clang_analyze: + rm -fr clang_analyze_build + mkdir clang_analyze_build + cd clang_analyze_build ; CCC_CXX=$(COMPILER_DIR)/clang++ CXX=$(COMPILER_DIR)/clang++ $(COMPILER_DIR)/scan-build cmake .. -GNinja + cd clang_analyze_build ; \ + $(COMPILER_DIR)/scan-build \ + -enable-checker alpha.core.BoolAssignment,alpha.core.CallAndMessageUnInitRefArg,alpha.core.CastSize,alpha.core.CastToStruct,alpha.core.Conversion,alpha.core.DynamicTypeChecker,alpha.core.FixedAddr,alpha.core.PointerArithm,alpha.core.PointerSub,alpha.core.SizeofPtr,alpha.core.StackAddressAsyncEscape,alpha.core.TestAfterDivZero,alpha.deadcode.UnreachableCode,core.builtin.BuiltinFunctions,core.builtin.NoReturnFunctions,core.CallAndMessage,core.DivideZero,core.DynamicTypePropagation,core.NonnilStringConstants,core.NonNullParamChecker,core.NullDereference,core.StackAddressEscape,core.UndefinedBinaryOperatorResult,core.uninitialized.ArraySubscript,core.uninitialized.Assign,core.uninitialized.Branch,core.uninitialized.CapturedBlockVariable,core.uninitialized.UndefReturn,core.VLASize,cplusplus.InnerPointer,cplusplus.Move,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,cplusplus.SelfAssignment,deadcode.DeadStores,nullability.NullableDereferenced,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull \ + --use-c++=$(COMPILER_DIR)/clang++ -analyze-headers -o report ninja + open clang_analyze_build/report/*/index.html + +# call cpplint +# Note: some errors expected due to false positives +cpplint: + third_party/cpplint/cpplint.py \ + --filter=-whitespace,-legal,-readability/alt_tokens,-runtime/references,-runtime/explicit \ + --quiet --recursive $(SRCS) + +# call Clang-Tidy +clang_tidy: + $(COMPILER_DIR)/clang-tidy $(AMALGAMATED_FILE) -- -Iinclude -std=c++11 + +# call PVS-Studio Analyzer +pvs_studio: + rm -fr pvs_studio_build + mkdir pvs_studio_build + cd pvs_studio_build ; cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=On + cd pvs_studio_build ; pvs-studio-analyzer analyze -j 10 + cd pvs_studio_build ; plog-converter -a'GA:1,2;64:1;CS' -t fullhtml PVS-Studio.log -o pvs + open pvs_studio_build/pvs/index.html + +# call Infer static analyzer +infer: + rm -fr infer_build + mkdir infer_build + cd infer_build ; infer compile -- cmake .. ; infer run -- make -j 4 + +# call OCLint static analyzer +oclint: + oclint $(SRCS) -report-type html -enable-global-analysis -o oclint_report.html -max-priority-1=10000 -max-priority-2=10000 -max-priority-3=10000 -- -std=c++11 -Iinclude + open oclint_report.html + +# execute the test suite with Clang sanitizers (address and undefined behavior) +clang_sanitize: + rm -fr clang_sanitize_build + mkdir clang_sanitize_build + cd clang_sanitize_build ; CXX=$(COMPILER_DIR)/clang++ cmake .. -DJSON_Sanitizer=On -DJSON_MultipleHeaders=ON -GNinja + cd clang_sanitize_build ; ninja + cd clang_sanitize_build ; ctest -E '.*_default' -j10 + + +########################################################################## +# Code format and source amalgamation +########################################################################## + +# call the Artistic Style pretty printer on all source files +pretty: + astyle \ + --style=allman \ + --indent=spaces=4 \ + --indent-modifiers \ + --indent-switches \ + --indent-preproc-block \ + --indent-preproc-define \ + --indent-col1-comments \ + --pad-oper \ + --pad-header \ + --align-pointer=type \ + --align-reference=type \ + --add-brackets \ + --convert-tabs \ + --close-templates \ + --lineend=linux \ + --preserve-date \ + --suffix=none \ + --formatted \ + $(SRCS) $(AMALGAMATED_FILE) test/src/*.cpp benchmarks/src/benchmarks.cpp doc/examples/*.cpp + +# create single header file +amalgamate: $(AMALGAMATED_FILE) + +# call the amalgamation tool and pretty print +$(AMALGAMATED_FILE): $(SRCS) + third_party/amalgamate/amalgamate.py -c third_party/amalgamate/config.json -s . --verbose=yes + $(MAKE) pretty + +# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources +# Note: this target is called by Travis +check-amalgamation: + @mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ + @$(MAKE) amalgamate + @diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false) + @mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) + +# check if every header in nlohmann includes sufficient headers to be compiled individually +check-single-includes: + @for x in $(SRCS); do \ + echo "Checking self-sufficiency of $$x..." ; \ + echo "#include <$$x>\nint main() {}\n" | sed 's|include/||' > single_include_test.cpp; \ + $(CXX) $(CXXFLAGS) -Iinclude -std=c++11 single_include_test.cpp -o single_include_test; \ + rm -f single_include_test.cpp single_include_test; \ + done + + +########################################################################## +# CMake +########################################################################## + +# grep "^option" CMakeLists.txt test/CMakeLists.txt | sed 's/(/ /' | awk '{print $2}' | xargs + +# check if all flags of our CMake files work +check_cmake_flags_do: + $(CMAKE_BINARY) --version + for flag in '' JSON_BuildTests JSON_Install JSON_MultipleHeaders JSON_Sanitizer JSON_Valgrind JSON_NoExceptions JSON_Coverage; do \ + rm -fr cmake_build; \ + mkdir cmake_build; \ + echo "$(CMAKE_BINARY) .. -D$$flag=On" ; \ + cd cmake_build ; \ + CXX=g++-8 $(CMAKE_BINARY) .. -D$$flag=On -DCMAKE_CXX_COMPILE_FEATURES="cxx_std_11;cxx_range_for" -DCMAKE_CXX_FLAGS="-std=gnu++11" ; \ + test -f Makefile || exit 1 ; \ + cd .. ; \ + done; + +# call target `check_cmake_flags_do` twice: once for minimal required CMake version 3.1.0 and once for the installed version +check_cmake_flags: + wget https://github.com/Kitware/CMake/releases/download/v3.1.0/cmake-3.1.0-Darwin64.tar.gz + tar xfz cmake-3.1.0-Darwin64.tar.gz + CMAKE_BINARY=$(abspath cmake-3.1.0-Darwin64/CMake.app/Contents/bin/cmake) $(MAKE) check_cmake_flags_do + CMAKE_BINARY=$(shell which cmake) $(MAKE) check_cmake_flags_do + + +########################################################################## +# ChangeLog +########################################################################## + +# Create a ChangeLog based on the git log using the GitHub Changelog Generator +# (). + +# variable to control the diffs between the last released version and the current repository state +NEXT_VERSION ?= "unreleased" + +ChangeLog.md: + github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION) + $(SED) -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md + $(SED) -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md + + +########################################################################## +# Release files +########################################################################## + +# Create the files for a release and add signatures and hashes. We use `-X` to make the resulting ZIP file +# reproducible, see . + +release: + rm -fr release_files + mkdir release_files + zip -9 --recurse-paths -X include.zip $(SRCS) + gpg --armor --detach-sig include.zip + mv include.zip include.zip.asc release_files + gpg --armor --detach-sig $(AMALGAMATED_FILE) + cp $(AMALGAMATED_FILE) release_files + mv $(AMALGAMATED_FILE).asc release_files + cd release_files ; shasum -a 256 json.hpp > hashes.txt + cd release_files ; shasum -a 256 include.zip >> hashes.txt + + +########################################################################## +# Maintenance +########################################################################## + +# clean up +clean: + rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM test/*.dSYM oclint_report.html + rm -fr benchmarks/files/numbers/*.json + rm -fr cmake-3.1.0-Darwin64.tar.gz cmake-3.1.0-Darwin64 + rm -fr build_coverage build_benchmarks fuzz-testing clang_analyze_build pvs_studio_build infer_build clang_sanitize_build cmake_build + $(MAKE) clean -Cdoc + $(MAKE) clean -Ctest + +########################################################################## +# Thirdparty code +########################################################################## + +update_hedley: + rm -f include/nlohmann/thirdparty/hedley/hedley.hpp include/nlohmann/thirdparty/hedley/hedley_undef.hpp + curl https://raw.githubusercontent.com/nemequ/hedley/master/hedley.h -o include/nlohmann/thirdparty/hedley/hedley.hpp + gsed -i 's/HEDLEY_/JSON_HEDLEY_/g' include/nlohmann/thirdparty/hedley/hedley.hpp + grep "[[:blank:]]*#[[:blank:]]*undef" include/nlohmann/thirdparty/hedley/hedley.hpp | grep -v "__" | sort | uniq | gsed 's/ //g' | gsed 's/undef/undef /g' > include/nlohmann/thirdparty/hedley/hedley_undef.hpp + $(MAKE) amalgamate diff --git a/libraries/cppdap/third_party/json/README.md b/libraries/cppdap/third_party/json/README.md new file mode 100644 index 000000000..9d395c064 --- /dev/null +++ b/libraries/cppdap/third_party/json/README.md @@ -0,0 +1,1371 @@ +[![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases) + +[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) +[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) +[![Build Status](https://circleci.com/gh/nlohmann/json.svg?style=svg)](https://circleci.com/gh/nlohmann/json) +[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade) +[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/nlohmann/json.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nlohmann/json/context:cpp) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/json.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/TarF5pPn9NtHQjhf) +[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) +[![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) +[![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/nlohmann/json.svg)](http://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue") +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289) + +- [Design goals](#design-goals) +- [Integration](#integration) + - [CMake](#cmake) + - [Package Managers](#package-managers) +- [Examples](#examples) + - [JSON as first-class data type](#json-as-first-class-data-type) + - [Serialization / Deserialization](#serialization--deserialization) + - [STL-like access](#stl-like-access) + - [Conversion from STL containers](#conversion-from-stl-containers) + - [JSON Pointer and JSON Patch](#json-pointer-and-json-patch) + - [JSON Merge Patch](#json-merge-patch) + - [Implicit conversions](#implicit-conversions) + - [Conversions to/from arbitrary types](#arbitrary-types-conversions) + - [Specializing enum conversion](#specializing-enum-conversion) + - [Binary formats (BSON, CBOR, MessagePack, and UBJSON)](#binary-formats-bson-cbor-messagepack-and-ubjson) +- [Supported compilers](#supported-compilers) +- [License](#license) +- [Contact](#contact) +- [Thanks](#thanks) +- [Used third-party tools](#used-third-party-tools) +- [Projects using JSON for Modern C++](#projects-using-json-for-modern-c) +- [Notes](#notes) +- [Execute unit tests](#execute-unit-tests) + +## Design goals + +There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals: + +- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean. + +- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. + +- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/test/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). + +Other aspects were not so important to us: + +- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs. + +- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. + +See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information. + + +## Integration + +[`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is the single required file in `single_include/nlohmann` or [released here](https://github.com/nlohmann/json/releases). You need to add + +```cpp +#include + +// for convenience +using json = nlohmann::json; +``` + +to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). + +You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of json_fwd.hpp (as part of cmake's install step), can be achieved by setting `-DJSON_MultipleHeaders=ON`. + +### CMake + +You can also use the `nlohmann_json::nlohmann_json` interface target in CMake. This target populates the appropriate usage requirements for `INTERFACE_INCLUDE_DIRECTORIES` to point to the appropriate include directories and `INTERFACE_COMPILE_FEATURES` for the necessary C++11 flags. + +#### External + +To use this library from a CMake project, you can locate it directly with `find_package()` and use the namespaced imported target from the generated package configuration: + +```cmake +# CMakeLists.txt +find_package(nlohmann_json 3.2.0 REQUIRED) +... +add_library(foo ...) +... +target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) +``` + +The package configuration file, `nlohmann_jsonConfig.cmake`, can be used either from an install tree or directly out of the build tree. + +#### Embedded + +To embed the library directly into an existing CMake project, place the entire source tree in a subdirectory and call `add_subdirectory()` in your `CMakeLists.txt` file: + +```cmake +# Typically you don't care so much for a third party library's tests to be +# run from your own project's code. +set(JSON_BuildTests OFF CACHE INTERNAL "") + +# If you only include this third party in PRIVATE source files, you do not +# need to install it when your main project gets installed. +# set(JSON_Install OFF CACHE INTERNAL "") + +# Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it +# unintended consequences that will break the build. It's generally +# discouraged (although not necessarily well documented as such) to use +# include(...) for pulling in other CMake projects anyways. +add_subdirectory(nlohmann_json) +... +add_library(foo ...) +... +target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) +``` + +#### Supporting Both + +To allow your project to support either an externally supplied or an embedded JSON library, you can use a pattern akin to the following: + +``` cmake +# Top level CMakeLists.txt +project(FOO) +... +option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF) +... +add_subdirectory(thirdparty) +... +add_library(foo ...) +... +# Note that the namespaced target will always be available regardless of the +# import method +target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) +``` +```cmake +# thirdparty/CMakeLists.txt +... +if(FOO_USE_EXTERNAL_JSON) + find_package(nlohmann_json 3.2.0 REQUIRED) +else() + set(JSON_BuildTests OFF CACHE INTERNAL "") + add_subdirectory(nlohmann_json) +endif() +... +``` + +`thirdparty/nlohmann_json` is then a complete copy of this source tree. + +### Package Managers + +:beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann-json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann-json --HEAD`. + +If you are using the [Meson Build System](http://mesonbuild.com), then you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/nlohmann_json), or simply use `meson wrap install nlohmann_json`. + +If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `jsonformoderncpp/x.y.z@vthiery/stable` to your `conanfile.py`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/vthiery/conan-jsonformoderncpp/issues) if you experience problems with the packages. + +If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. + +If you are using [hunter](https://github.com/ruslo/hunter/) on your project for external dependencies, then you can use the [nlohmann_json package](https://docs.hunter.sh/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging. + +If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo add github.com/buckaroo-pm/nlohmann-json`. Please file issues [here](https://github.com/buckaroo-pm/nlohmann-json). There is a demo repo [here](https://github.com/njlr/buckaroo-nholmann-json-example). + +If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging. + +If you are using [cget](http://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/json@v3.1.0`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). + +If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). + +If you are using [NuGet](https://www.nuget.org), you can use the package [nlohmann.json](https://www.nuget.org/packages/nlohmann.json/). Please check [this extensive description](https://github.com/nlohmann/json/issues/1132#issuecomment-452250255) on how to use the package. Please files issues [here](https://github.com/hnkb/nlohmann-json-nuget/issues). + +If you are using [conda](https://conda.io/), you can use the package [nlohmann_json](https://github.com/conda-forge/nlohmann_json-feedstock) from [conda-forge](https://conda-forge.org) executing `conda install -c conda-forge nlohmann_json`. Please file issues [here](https://github.com/conda-forge/nlohmann_json-feedstock/issues). + +If you are using [MSYS2](http://www.msys2.org/), your can use the [mingw-w64-nlohmann_json](https://packages.msys2.org/base/mingw-w64-nlohmann_json) package, just type `pacman -S mingw-w64-i686-nlohmann_json` or `pacman -S mingw-w64-x86_64-nlohmann_json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann_json%5D) if you experience problems with the packages. + +## Examples + +Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)). + +### JSON as first-class data type + +Here are some examples to give you an idea how to use the class. + +Assume you want to create the JSON object + +```json +{ + "pi": 3.141, + "happy": true, + "name": "Niels", + "nothing": null, + "answer": { + "everything": 42 + }, + "list": [1, 0, 2], + "object": { + "currency": "USD", + "value": 42.99 + } +} +``` + +With this library, you could write: + +```cpp +// create an empty structure (null) +json j; + +// add a number that is stored as double (note the implicit conversion of j to an object) +j["pi"] = 3.141; + +// add a Boolean that is stored as bool +j["happy"] = true; + +// add a string that is stored as std::string +j["name"] = "Niels"; + +// add another null object by passing nullptr +j["nothing"] = nullptr; + +// add an object inside the object +j["answer"]["everything"] = 42; + +// add an array that is stored as std::vector (using an initializer list) +j["list"] = { 1, 0, 2 }; + +// add another object (using an initializer list of pairs) +j["object"] = { {"currency", "USD"}, {"value", 42.99} }; + +// instead, you could also write (which looks very similar to the JSON above) +json j2 = { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + {"answer", { + {"everything", 42} + }}, + {"list", {1, 0, 2}}, + {"object", { + {"currency", "USD"}, + {"value", 42.99} + }} +}; +``` + +Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a9ad7ec0bc1082ed09d10900fbb20a21f.html#a9ad7ec0bc1082ed09d10900fbb20a21f) and [`json::object()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aaf509a7c029100d292187068f61c99b8.html#aaf509a7c029100d292187068f61c99b8) will help: + +```cpp +// a way to express the empty array [] +json empty_array_explicit = json::array(); + +// ways to express the empty object {} +json empty_object_implicit = json({}); +json empty_object_explicit = json::object(); + +// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] +json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} }); +``` + +### Serialization / Deserialization + +#### To/from strings + +You can create a JSON value (deserialization) by appending `_json` to a string literal: + +```cpp +// create object from string literal +json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; + +// or even nicer with a raw string literal +auto j2 = R"( + { + "happy": true, + "pi": 3.141 + } +)"_json; +``` + +Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object. + +The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_afd4ef1ac8ad50a5894a9afebca69140a.html#afd4ef1ac8ad50a5894a9afebca69140a): + +```cpp +// parse explicitly +auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); +``` + +You can also get a string representation of a JSON value (serialize): + +```cpp +// explicit conversion to string +std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} + +// serialization with pretty printing +// pass in the amount of spaces to indent +std::cout << j.dump(4) << std::endl; +// { +// "happy": true, +// "pi": 3.141 +// } +``` + +Note the difference between serialization and assignment: + +```cpp +// store a string in a JSON value +json j_string = "this is a string"; + +// retrieve the string value +auto cpp_string = j_string.get(); +// retrieve the string value (alternative when an variable already exists) +std::string cpp_string2; +j_string.get_to(cpp_string2); + +// retrieve the serialized value (explicit JSON serialization) +std::string serialized_string = j_string.dump(); + +// output of original string +std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get() << '\n'; +// output of serialized value +std::cout << j_string << " == " << serialized_string << std::endl; +``` + +[`.dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) always returns the serialized value, and [`.get()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aa6602bb24022183ab989439e19345d08.html#aa6602bb24022183ab989439e19345d08) returns the originally stored string value. + +Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. + +#### To/from streams (e.g. files, string streams) + +You can also use streams to serialize and deserialize: + +```cpp +// deserialize from standard input +json j; +std::cin >> j; + +// serialize to standard output +std::cout << j; + +// the setw manipulator was overloaded to set the indentation for pretty printing +std::cout << std::setw(4) << j << std::endl; +``` + +These operators work for any subclasses of `std::istream` or `std::ostream`. Here is the same example with files: + +```cpp +// read a JSON file +std::ifstream i("file.json"); +json j; +i >> j; + +// write prettified JSON to another file +std::ofstream o("pretty.json"); +o << std::setw(4) << j << std::endl; +``` + +Please note that setting the exception bit for `failbit` is inappropriate for this use case. It will result in program termination due to the `noexcept` specifier in use. + +#### Read from iterator range + +You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector`: + +```cpp +std::vector v = {'t', 'r', 'u', 'e'}; +json j = json::parse(v.begin(), v.end()); +``` + +You may leave the iterators for the range [begin, end): + +```cpp +std::vector v = {'t', 'r', 'u', 'e'}; +json j = json::parse(v); +``` + +#### SAX interface + +The library uses a SAX-like interface with the following functions: + +```cpp +// called when null is parsed +bool null(); + +// called when a boolean is parsed; value is passed +bool boolean(bool val); + +// called when a signed or unsigned integer number is parsed; value is passed +bool number_integer(number_integer_t val); +bool number_unsigned(number_unsigned_t val); + +// called when a floating-point number is parsed; value and original string is passed +bool number_float(number_float_t val, const string_t& s); + +// called when a string is parsed; value is passed and can be safely moved away +bool string(string_t& val); + +// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known) +bool start_object(std::size_t elements); +bool end_object(); +bool start_array(std::size_t elements); +bool end_array(); +// called when an object key is parsed; value is passed and can be safely moved away +bool key(string_t& val); + +// called when a parse error occurs; byte position, the last token, and an exception is passed +bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex); +``` + +The return value of each function determines whether parsing should proceed. + +To implement your own SAX handler, proceed as follows: + +1. Implement the SAX interface in a class. You can use class `nlohmann::json_sax` as base class, but you can also use any class where the functions described above are implemented and public. +2. Create an object of your SAX interface class, e.g. `my_sax`. +3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface. + +Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp). + +### STL-like access + +We designed the JSON class to behave just like an STL container. In fact, it satisfies the [**ReversibleContainer**](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirement. + +```cpp +// create an array using push_back +json j; +j.push_back("foo"); +j.push_back(1); +j.push_back(true); + +// also use emplace_back +j.emplace_back(1.78); + +// iterate the array +for (json::iterator it = j.begin(); it != j.end(); ++it) { + std::cout << *it << '\n'; +} + +// range-based for +for (auto& element : j) { + std::cout << element << '\n'; +} + +// getter/setter +const auto tmp = j[0].get(); +j[1] = 42; +bool foo = j.at(2); + +// comparison +j == "[\"foo\", 1, true]"_json; // true + +// other stuff +j.size(); // 3 entries +j.empty(); // false +j.type(); // json::value_t::array +j.clear(); // the array is empty again + +// convenience type checkers +j.is_null(); +j.is_boolean(); +j.is_number(); +j.is_object(); +j.is_array(); +j.is_string(); + +// create an object +json o; +o["foo"] = 23; +o["bar"] = false; +o["baz"] = 3.141; + +// also use emplace +o.emplace("weather", "sunny"); + +// special iterator member functions for objects +for (json::iterator it = o.begin(); it != o.end(); ++it) { + std::cout << it.key() << " : " << it.value() << "\n"; +} + +// the same code as range for +for (auto& el : o.items()) { + std::cout << el.key() << " : " << el.value() << "\n"; +} + +// even easier with structured bindings (C++17) +for (auto& [key, value] : o.items()) { + std::cout << key << " : " << value << "\n"; +} + +// find an entry +if (o.find("foo") != o.end()) { + // there is an entry with key "foo" +} + +// or simpler using count() +int foo_present = o.count("foo"); // 1 +int fob_present = o.count("fob"); // 0 + +// delete an entry +o.erase("foo"); +``` + + +### Conversion from STL containers + +Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON values (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container. + +```cpp +std::vector c_vector {1, 2, 3, 4}; +json j_vec(c_vector); +// [1, 2, 3, 4] + +std::deque c_deque {1.2, 2.3, 3.4, 5.6}; +json j_deque(c_deque); +// [1.2, 2.3, 3.4, 5.6] + +std::list c_list {true, true, false, true}; +json j_list(c_list); +// [true, true, false, true] + +std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; +json j_flist(c_flist); +// [12345678909876, 23456789098765, 34567890987654, 45678909876543] + +std::array c_array {{1, 2, 3, 4}}; +json j_array(c_array); +// [1, 2, 3, 4] + +std::set c_set {"one", "two", "three", "four", "one"}; +json j_set(c_set); // only one entry for "one" is used +// ["four", "one", "three", "two"] + +std::unordered_set c_uset {"one", "two", "three", "four", "one"}; +json j_uset(c_uset); // only one entry for "one" is used +// maybe ["two", "three", "four", "one"] + +std::multiset c_mset {"one", "two", "one", "four"}; +json j_mset(c_mset); // both entries for "one" are used +// maybe ["one", "two", "one", "four"] + +std::unordered_multiset c_umset {"one", "two", "one", "four"}; +json j_umset(c_umset); // both entries for "one" are used +// maybe ["one", "two", "one", "four"] +``` + +Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON values (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. + +```cpp +std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; +json j_map(c_map); +// {"one": 1, "three": 3, "two": 2 } + +std::unordered_map c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} }; +json j_umap(c_umap); +// {"one": 1.2, "two": 2.3, "three": 3.4} + +std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; +json j_mmap(c_mmap); // only one entry for key "three" is used +// maybe {"one": true, "two": true, "three": true} + +std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; +json j_ummap(c_ummap); // only one entry for key "three" is used +// maybe {"one": true, "two": true, "three": true} +``` + +### JSON Pointer and JSON Patch + +The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address structured values. On top of this, **JSON Patch** ([RFC 6902](https://tools.ietf.org/html/rfc6902)) allows to describe differences between two JSON values - effectively allowing patch and diff operations known from Unix. + +```cpp +// a JSON value +json j_original = R"({ + "baz": ["one", "two", "three"], + "foo": "bar" +})"_json; + +// access members with a JSON pointer (RFC 6901) +j_original["/baz/1"_json_pointer]; +// "two" + +// a JSON patch (RFC 6902) +json j_patch = R"([ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} +])"_json; + +// apply the patch +json j_result = j_original.patch(j_patch); +// { +// "baz": "boo", +// "hello": ["world"] +// } + +// calculate a JSON patch from two JSON values +json::diff(j_result, j_original); +// [ +// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, +// { "op": "remove","path": "/hello" }, +// { "op": "add", "path": "/foo", "value": "bar" } +// ] +``` + +### JSON Merge Patch + +The library supports **JSON Merge Patch** ([RFC 7386](https://tools.ietf.org/html/rfc7386)) as a patch format. Instead of using JSON Pointer (see above) to specify values to be manipulated, it describes the changes using a syntax that closely mimics the document being modified. + +```cpp +// a JSON value +json j_document = R"({ + "a": "b", + "c": { + "d": "e", + "f": "g" + } +})"_json; + +// a patch +json j_patch = R"({ + "a":"z", + "c": { + "f": null + } +})"_json; + +// apply the patch +j_document.merge_patch(j_patch); +// { +// "a": "z", +// "c": { +// "d": "e" +// } +// } +``` + +### Implicit conversions + +Supported types can be implicitly converted to JSON values. + +It is recommended to **NOT USE** implicit conversions **FROM** a JSON value. +You can find more details about this recommendation [here](https://www.github.com/nlohmann/json/issues/958). + +```cpp +// strings +std::string s1 = "Hello, world!"; +json js = s1; +auto s2 = js.get(); +// NOT RECOMMENDED +std::string s3 = js; +std::string s4; +s4 = js; + +// Booleans +bool b1 = true; +json jb = b1; +auto b2 = jb.get(); +// NOT RECOMMENDED +bool b3 = jb; +bool b4; +b4 = jb; + +// numbers +int i = 42; +json jn = i; +auto f = jn.get(); +// NOT RECOMMENDED +double f2 = jb; +double f3; +f3 = jb; + +// etc. +``` + +Note that `char` types are not automatically converted to JSON strings, but to integer numbers. A conversion to a string must be specified explicitly: + +```cpp +char ch = 'A'; // ASCII value 65 +json j_default = ch; // stores integer number 65 +json j_string = std::string(1, ch); // stores string "A" +``` + +### Arbitrary types conversions + +Every type can be serialized in JSON, not just STL containers and scalar types. Usually, you would do something along those lines: + +```cpp +namespace ns { + // a simple struct to model a person + struct person { + std::string name; + std::string address; + int age; + }; +} + +ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + +// convert to JSON: copy each value into the JSON object +json j; +j["name"] = p.name; +j["address"] = p.address; +j["age"] = p.age; + +// ... + +// convert from JSON: copy each value from the JSON object +ns::person p { + j["name"].get(), + j["address"].get(), + j["age"].get() +}; +``` + +It works, but that's quite a lot of boilerplate... Fortunately, there's a better way: + +```cpp +// create a person +ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60}; + +// conversion: person -> json +json j = p; + +std::cout << j << std::endl; +// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} + +// conversion: json -> person +auto p2 = j.get(); + +// that's it +assert(p == p2); +``` + +#### Basic usage + +To make this work with one of your types, you only need to provide two functions: + +```cpp +using nlohmann::json; + +namespace ns { + void to_json(json& j, const person& p) { + j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}}; + } + + void from_json(const json& j, person& p) { + j.at("name").get_to(p.name); + j.at("address").get_to(p.address); + j.at("age").get_to(p.age); + } +} // namespace ns +``` + +That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called. +Likewise, when calling `get()` or `get_to(your_type&)`, the `from_json` method will be called. + +Some important things: + +* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). +* Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. +* When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) +* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. +* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. + + +#### How do I convert third-party types? + +This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: + +The library uses **JSON Serializers** to convert types to json. +The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)). + +It is implemented like this (simplified): + +```cpp +template +struct adl_serializer { + static void to_json(json& j, const T& value) { + // calls the "to_json" method in T's namespace + } + + static void from_json(const json& j, T& value) { + // same thing, but with the "from_json" method + } +}; +``` + +This serializer works fine when you have control over the type's namespace. However, what about `boost::optional` or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`... + +To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example: + +```cpp +// partial specialization (full specialization works too) +namespace nlohmann { + template + struct adl_serializer> { + static void to_json(json& j, const boost::optional& opt) { + if (opt == boost::none) { + j = nullptr; + } else { + j = *opt; // this will call adl_serializer::to_json which will + // find the free function to_json in T's namespace! + } + } + + static void from_json(const json& j, boost::optional& opt) { + if (j.is_null()) { + opt = boost::none; + } else { + opt = j.get(); // same as above, but with + // adl_serializer::from_json + } + } + }; +} +``` + +#### How can I use `get()` for non-default constructible/non-copyable types? + +There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: + +```cpp +struct move_only_type { + move_only_type() = delete; + move_only_type(int ii): i(ii) {} + move_only_type(const move_only_type&) = delete; + move_only_type(move_only_type&&) = default; + + int i; +}; + +namespace nlohmann { + template <> + struct adl_serializer { + // note: the return type is no longer 'void', and the method only takes + // one argument + static move_only_type from_json(const json& j) { + return {j.get()}; + } + + // Here's the catch! You must provide a to_json method! Otherwise you + // will not be able to convert move_only_type to json, since you fully + // specialized adl_serializer on that type + static void to_json(json& j, move_only_type t) { + j = t.i; + } + }; +} +``` + +#### Can I write my own serializer? (Advanced use) + +Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples. + +If you write your own serializer, you'll need to do a few things: + +- use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`) +- use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods +- use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL + +Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL. + +```cpp +// You should use void as a second template argument +// if you don't need compile-time checks on T +template::type> +struct less_than_32_serializer { + template + static void to_json(BasicJsonType& j, T value) { + // we want to use ADL, and call the correct to_json overload + using nlohmann::to_json; // this method is called by adl_serializer, + // this is where the magic happens + to_json(j, value); + } + + template + static void from_json(const BasicJsonType& j, T& value) { + // same thing here + using nlohmann::from_json; + from_json(j, value); + } +}; +``` + +Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention: + +```cpp +template +struct bad_serializer +{ + template + static void to_json(BasicJsonType& j, const T& value) { + // this calls BasicJsonType::json_serializer::to_json(j, value); + // if BasicJsonType::json_serializer == bad_serializer ... oops! + j = value; + } + + template + static void to_json(const BasicJsonType& j, T& value) { + // this calls BasicJsonType::json_serializer::from_json(j, value); + // if BasicJsonType::json_serializer == bad_serializer ... oops! + value = j.template get(); // oops! + } +}; +``` + +### Specializing enum conversion + +By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be undefined or a different enum value than was originally intended. + +It is possible to more precisely specify how a given enum is mapped to and from JSON as shown below: + +```cpp +// example enum type declaration +enum TaskState { + TS_STOPPED, + TS_RUNNING, + TS_COMPLETED, + TS_INVALID=-1, +}; + +// map TaskState values to JSON as strings +NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { + {TS_INVALID, nullptr}, + {TS_STOPPED, "stopped"}, + {TS_RUNNING, "running"}, + {TS_COMPLETED, "completed"}, +}) +``` + +The `NLOHMANN_JSON_SERIALIZE_ENUM()` macro declares a set of `to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. + +**Usage:** + +```cpp +// enum to JSON as string +json j = TS_STOPPED; +assert(j == "stopped"); + +// json string to enum +json j3 = "running"; +assert(j3.get() == TS_RUNNING); + +// undefined json value to enum (where the first map entry above is the default) +json jPi = 3.14; +assert(jPi.get() == TS_INVALID ); +``` + +Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above, +- `NLOHMANN_JSON_SERIALIZE_ENUM()` MUST be declared in your enum type's namespace (which can be the global namespace), or the library will not be able to locate it and it will default to integer serialization. +- It MUST be available (e.g., proper headers must be included) everywhere you use the conversions. + +Other Important points: +- When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. +- If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. + +### Binary formats (BSON, CBOR, MessagePack, and UBJSON) + +Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](http://bsonspec.org) (Binary JSON), [CBOR](http://cbor.io) (Concise Binary Object Representation), [MessagePack](http://msgpack.org), and [UBJSON](http://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors. + +```cpp +// create a JSON value +json j = R"({"compact": true, "schema": 0})"_json; + +// serialize to BSON +std::vector v_bson = json::to_bson(j); + +// 0x1B, 0x00, 0x00, 0x00, 0x08, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x00, 0x01, 0x10, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +// roundtrip +json j_from_bson = json::from_bson(v_bson); + +// serialize to CBOR +std::vector v_cbor = json::to_cbor(j); + +// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00 + +// roundtrip +json j_from_cbor = json::from_cbor(v_cbor); + +// serialize to MessagePack +std::vector v_msgpack = json::to_msgpack(j); + +// 0x82, 0xA7, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xC3, 0xA6, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00 + +// roundtrip +json j_from_msgpack = json::from_msgpack(v_msgpack); + +// serialize to UBJSON +std::vector v_ubjson = json::to_ubjson(j); + +// 0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D + +// roundtrip +json j_from_ubjson = json::from_ubjson(v_ubjson); +``` + + +## Supported compilers + +Though it's 2019 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: + +- GCC 4.8 - 9.0 (and possibly later) +- Clang 3.4 - 8.0 (and possibly later) +- Intel C++ Compiler 17.0.2 (and possibly later) +- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) +- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later) + +I would be happy to learn about other compilers/versions. + +Please note: + +- GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. +- Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default. + + ``` + APP_STL := c++_shared + NDK_TOOLCHAIN_VERSION := clang3.6 + APP_CPPFLAGS += -frtti -fexceptions + ``` + + The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10. + +- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). + +- Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. + +The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), and [Doozer](https://doozer.io): + +| Compiler | Operating System | Version String | +|-----------------------|------------------------------|----------------| +| GCC 4.8.5 | Ubuntu 14.04.5 LTS | g++-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.2) 4.8.5 | +| GCC 4.8.5 | CentOS Release-7-6.1810.2.el7.centos.x86_64 | g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) | +| GCC 4.9.2 (armv7l) | Raspbian GNU/Linux 8 (jessie) | g++ (Raspbian 4.9.2-10+deb8u2) 4.9.2 | +| GCC 4.9.4 | Ubuntu 14.04.1 LTS | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 | +| GCC 5.3.1 (armv7l) | Ubuntu 16.04 LTS | g++ (Ubuntu/Linaro 5.3.1-14ubuntu2) 5.3.1 20160413 | +| GCC 5.5.0 | Ubuntu 14.04.1 LTS | g++-5 (Ubuntu 5.5.0-12ubuntu1~14.04) 5.5.0 20171010 | +| GCC 6.3.1 | Fedora release 24 (Twenty Four) | g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1) | +| GCC 6.4.0 | Ubuntu 14.04.1 LTS | g++-6 (Ubuntu 6.4.0-17ubuntu1~14.04) 6.4.0 20180424 | +| GCC 7.3.0 | Ubuntu 14.04.1 LTS | g++-7 (Ubuntu 7.3.0-21ubuntu1~14.04) 7.3.0 | +| GCC 7.3.0 | Windows Server 2012 R2 (x64) | g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0 | +| GCC 8.1.0 | Ubuntu 14.04.1 LTS | g++-8 (Ubuntu 8.1.0-5ubuntu1~14.04) 8.1.0 | +| Clang 3.5.0 | Ubuntu 14.04.1 LTS | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0) | +| Clang 3.6.2 | Ubuntu 14.04.1 LTS | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) (based on LLVM 3.6.2) | +| Clang 3.7.1 | Ubuntu 14.04.1 LTS | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) (based on LLVM 3.7.1) | +| Clang 3.8.0 | Ubuntu 14.04.1 LTS | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) | +| Clang 3.9.1 | Ubuntu 14.04.1 LTS | clang version 3.9.1-4ubuntu3~14.04.3 (tags/RELEASE_391/rc2) | +| Clang 4.0.1 | Ubuntu 14.04.1 LTS | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) | +| Clang 5.0.2 | Ubuntu 14.04.1 LTS | clang version 5.0.2-svn328729-1~exp1~20180509123505.100 (branches/release_50) | +| Clang 6.0.1 | Ubuntu 14.04.1 LTS | clang version 6.0.1-svn334776-1~exp1~20180726133705.85 (branches/release_60) | +| Clang 7.0.1 | Ubuntu 14.04.1 LTS | clang version 7.0.1-svn348686-1~exp1~20181213084532.54 (branches/release_70) | +| Clang Xcode 8.3 | OSX 10.11.6 | Apple LLVM version 8.1.0 (clang-802.0.38) | +| Clang Xcode 9.0 | OSX 10.12.6 | Apple LLVM version 9.0.0 (clang-900.0.37) | +| Clang Xcode 9.1 | OSX 10.12.6 | Apple LLVM version 9.0.0 (clang-900.0.38) | +| Clang Xcode 9.2 | OSX 10.13.3 | Apple LLVM version 9.1.0 (clang-902.0.39.1) | +| Clang Xcode 9.3 | OSX 10.13.3 | Apple LLVM version 9.1.0 (clang-902.0.39.2) | +| Clang Xcode 10.0 | OSX 10.13.3 | Apple LLVM version 10.0.0 (clang-1000.11.45.2) | +| Clang Xcode 10.1 | OSX 10.13.3 | Apple LLVM version 10.0.0 (clang-1000.11.45.5) | +| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 | +| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.7.180.61344, MSVC 19.14.26433.0 | + +## License + + + +The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): + +Copyright © 2013-2019 [Niels Lohmann](http://nlohmann.me) + +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. + +* * * + +The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) + +The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](http://florian.loitsch.com/) + +The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](http://creativecommons.org/publicdomain/zero/1.0/). + +## Contact + +If you have questions regarding the library, I would like to invite you to [open an issue at GitHub](https://github.com/nlohmann/json/issues/new/choose). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at GitHub allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases. + +Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc). + +## Security + +[Commits by Niels Lohmann](https://github.com/nlohmann/json/commits) and [releases](https://github.com/nlohmann/json/releases) are signed with this [PGP Key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69). + +## Thanks + +I deeply appreciate the help of the following people. + + + +- [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization. +- [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes. +- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries. +- [wancw](https://github.com/wanwc) fixed a bug that hindered the class to compile with Clang. +- Tomas Åblad found a bug in the iterator implementation. +- [Joshua C. Randall](https://github.com/jrandall) fixed a bug in the floating-point serialization. +- [Aaron Burghardt](https://github.com/aburgh) implemented code to parse streams incrementally. Furthermore, he greatly improved the parser class by allowing the definition of a filter function to discard undesired elements while parsing. +- [Daniel Kopeček](https://github.com/dkopecek) fixed a bug in the compilation with GCC 5.0. +- [Florian Weber](https://github.com/Florianjw) fixed a bug in and improved the performance of the comparison operators. +- [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping. +- [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums. +- [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio. +- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types. +- [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling. +- [dariomt](https://github.com/dariomt) fixed some typos in the examples. +- [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation. +- [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue. +- [Huu Nguyen](https://github.com/whoshuu) correct a variable name in the documentation. +- [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference. +- [dariomt](https://github.com/dariomt) fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values. +- [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK. +- [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio. +- [406345](https://github.com/406345) fixed two small warnings. +- [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. +- [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. +- [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers. +- [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. +- [msm-](https://github.com/msm-) added support for American Fuzzy Lop. +- [Annihil](https://github.com/Annihil) fixed an example in the README file. +- [Themercee](https://github.com/Themercee) noted a wrong URL in the README file. +- [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`. +- [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212). +- [zewt](https://github.com/zewt) added useful notes to the README file about Android. +- [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. +- [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. +- [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). +- [Mário Feroldi](https://github.com/thelostt) fixed a small typo. +- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. +- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. +- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case. +- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks. +- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. +- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. +- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. +- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix. +- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. +- [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function. +- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing. +- [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. +- [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. +- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. +- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. +- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers. +- [Jonathan Lee](https://github.com/vjon) fixed an example in the README file. +- [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types. +- [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio. +- [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types. +- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example. +- [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite. +- [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section. +- [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README. +- [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s. +- [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation. +- [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings. +- [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible. +- [ftillier](https://github.com/ftillier) fixed a compiler warning. +- [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped. +- [Fytch](https://github.com/Fytch) found a bug in the documentation. +- [Jay Sistar](https://github.com/Type1J) implemented a Meson build description. +- [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation. +- [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager. +- [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`. +- [Mike Tzou](https://github.com/Chocobo1) fixed some typos. +- [amrcode](https://github.com/amrcode) noted a misleading documentation about comparison of floats. +- [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `` with ``. +- [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library. +- [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists. +- [Greg Hurrell](https://github.com/wincent) fixed a typo. +- [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo. +- [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler. +- [Markus Werle](https://github.com/daixtrose) fixed a typo. +- [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check. +- [Alex](https://github.com/leha-bot) noted an error in a code sample. +- [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped fixing them. +- [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams. +- [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error. +- [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings. +- [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file. +- [pvleuven](https://github.com/pvleuven) helped fixing a warning in ICC. +- [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC. +- [Jamie Seward](https://github.com/jseward) avoided unnecessary string copies in `find()` and `count()`. +- [Mitja](https://github.com/Itja) fixed some typos. +- [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links. +- [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view. +- [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings. +- [Eren Okka](https://github.com/erengy) fixed some MSVC warnings. +- [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed. +- [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README. +- [zerodefect](https://github.com/zerodefect) fixed a compiler warning. +- [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior. +- [mark-99](https://github.com/mark-99) helped fixing an ICC error. +- [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file. +- [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings. +- [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. +- [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. +- [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. +- [agrianius](https://github.com/agrianius) allowed to dump JSON to an alternative string type. +- [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake. +- [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io). +- [Carlos O'Ryan](https://github.com/coryan) fixed a typo. +- [James Upjohn](https://github.com/jammehcow) fixed a version number in the compilers section. +- [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration. +- [Jan Schöppach](https://github.com/dns13) fixed a typo. +- [martin-mfg](https://github.com/martin-mfg) fixed a typo. +- [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`. +- [agrianius](https://github.com/agrianius) added code to use alternative string implementations. +- [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function. +- [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](cppreference.com). +- [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode. +- [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases. +- [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library. +- [thyu](https://github.com/thyu) fixed a compiler warning. +- [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2. +- [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library. +- [Hyeon Kim](https://github.com/simnalamburt) fixed an issue with a double macro definition. +- [Ben Berman](https://github.com/rivertam) made some error messages more understandable. +- [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler. +- [mandreyel](https://github.com/mandreyel) fixed a compilation problem. +- [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file. +- [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8. +- [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory. +- [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning. +- [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define a enum/JSON mapping. +- [efp](https://github.com/efp) added line and column information to parse errors. +- [julian-becker](https://github.com/julian-becker) added BSON support. +- [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings. +- [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version). +- [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`. +- [kjpus](https://github.com/kjpus) fixed a link in the documentation. +- [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation. +- [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning. +- [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC. +- [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream. +- [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson. +- [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace. +- [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12. +- [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion. +- [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC. +- [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers. +- [John-Mark](https://github.com/johnmarkwayve) noted a missing header. +- [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0. +- [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0. +- [Ivor Wanders](https://github.com/iwanders) helped reducing the CMake requirement to version 3.1. +- [njlr](https://github.com/njlr) updated the Buckaroo instructions. +- [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS. +- [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. +- [past-due](https://github.com/past-due) suppressed an unfixable warning. +- [Elvis Oric](https://github.com/elvisoric) improved Meson support. +- [Matěj Plch](https://github.com/Afforix) fixed an example in the README. +- [Mark Beckwith](https://github.com/wythe) fixed a typo. +- [scinart](https://github.com/scinart) fixed bug in the serializer. +- [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers. +- [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda. +- [Michele Caini](https://github.com/skypjack) fixed links in the README. +- [Hani](https://github.com/hnkb) documented how to install the library with NuGet. +- [Mark Beckwith](https://github.com/wythe) fixed a typo. +- [yann-morin-1998](https://github.com/yann-morin-1998) helped reducing the CMake requirement to version 3.1. +- [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro. +- [remyabel](https://github.com/remyabel) added GNUInstallDirs to the CMake files. +- [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test. +- [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method. +- [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning. +- [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/onqtam/doctest) + +Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. + + +## Used third-party tools + +The library itself consists of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot! + +- [**amalgamate.py - Amalgamate C source and header files**](https://github.com/edlund/amalgamate) to create a single header file +- [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing +- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows +- [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code indentation +- [**CircleCI**](http://circleci.com) for [continuous integration](https://circleci.com/gh/nlohmann/json). +- [**Clang**](http://clang.llvm.org) for compilation with code sanitizers +- [**CMake**](https://cmake.org) for build automation +- [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json) +- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json) +- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) +- [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis +- [**doctest**](https://github.com/onqtam/doctest) for the unit tests +- [**Doozer**](https://doozer.io) for [continuous integration](https://doozer.io/nlohmann/json) on Linux (CentOS, Raspbian, Fedora) +- [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/) +- [**fastcov**](https://github.com/RPGillespie6/fastcov) to process coverage information +- [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages +- [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) +- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks +- [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros +- [**lcov**](http://ltp.sourceforge.net/coverage/lcov.php) to process coverage information and create a HTML view +- [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz +- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) +- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. +- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox) +- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS +- [**Valgrind**](http://valgrind.org) to check for correct memory management +- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/TarF5pPn9NtHQjhf) + + +## Projects using JSON for Modern C++ + +The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices. + + +## Notes + +### Character encoding + +The library supports **Unicode input** as follows: + +- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). +- `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers. +- Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors. +- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. +- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. +- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. +- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. + +### Comments in JSON + +This library does not support comments. It does so for three reasons: + +1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. +2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: + + > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. + + > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. + +3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. + +This library will not support comments in the future. If you wish to use comments, I see three options: + +1. Strip comments before using this library. +2. Use a different JSON library with comment support. +3. Use a format that natively supports comments (e.g., YAML or JSON5). + +### Order of object keys + +By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). + +### Further notes + +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8). +- As the exact type of a number is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. +- The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. +- **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER´` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. + +## Execute unit tests + +To compile and run the tests, you need to execute + +```sh +$ mkdir build +$ cd build +$ cmake .. +$ cmake --build . +$ ctest --output-on-failure +``` + +For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/libraries/cppdap/third_party/json/appveyor.yml b/libraries/cppdap/third_party/json/appveyor.yml new file mode 100644 index 000000000..0a92a6c9a --- /dev/null +++ b/libraries/cppdap/third_party/json/appveyor.yml @@ -0,0 +1,111 @@ +version: '{build}' + +environment: + matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Debug + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 14 2015 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + configuration: Debug + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 15 2017 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Debug + COMPILER: mingw + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Ninja + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Release + COMPILER: mingw + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Ninja + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Release + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 14 2015 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Release + platform: x86 + name: with_win_header + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 14 2015 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + configuration: Release + platform: x86 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 15 2017 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + configuration: Release + platform: x86 + CXX_FLAGS: "/permissive- /std:c++latest /utf-8" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 15 2017 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + configuration: Release + platform: x64 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 14 2015 Win64 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + configuration: Release + platform: x64 + CXX_FLAGS: "" + LINKER_FLAGS: "" + GENERATOR: Visual Studio 15 2017 Win64 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + configuration: Release + platform: x64 + CXX_FLAGS: "/permissive- /std:c++latest /utf-8 /F4000000" + LINKER_FLAGS: "/STACK:4000000" + GENERATOR: Visual Studio 15 2017 Win64 + +init: + - cmake --version + - msbuild /version + +install: + - if "%COMPILER%"=="mingw" appveyor DownloadFile https://github.com/ninja-build/ninja/releases/download/v1.6.0/ninja-win.zip -FileName ninja.zip + - if "%COMPILER%"=="mingw" 7z x ninja.zip -oC:\projects\deps\ninja > nul + - if "%COMPILER%"=="mingw" set PATH=C:\projects\deps\ninja;%PATH% + - if "%COMPILER%"=="mingw" set PATH=C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64\bin;%PATH% + - if "%COMPILER%"=="mingw" g++ --version + +before_build: + # for with_win_header build, inject the inclusion of Windows.h to the single-header library + - ps: if ($env:name -Eq "with_win_header") { $header_path = "single_include\nlohmann\json.hpp" } + - ps: if ($env:name -Eq "with_win_header") { "#include `n" + (Get-Content $header_path | Out-String) | Set-Content $header_path } + - cmake . -G "%GENERATOR%" -DCMAKE_BUILD_TYPE="%configuration%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" + +build_script: + - cmake --build . --config "%configuration%" + +test_script: + - if "%configuration%"=="Release" ctest -C "%configuration%" -V -j + # On Debug builds, skip test-unicode_all + # as it is extremely slow to run and cause + # occasional timeouts on AppVeyor. + # More info: https://github.com/nlohmann/json/pull/1570 + - if "%configuration%"=="Debug" ctest --exclude-regex "test-unicode_all" -C "%configuration%" -V -j diff --git a/libraries/cppdap/third_party/json/cmake/config.cmake.in b/libraries/cppdap/third_party/json/cmake/config.cmake.in new file mode 100644 index 000000000..9a17a7d7b --- /dev/null +++ b/libraries/cppdap/third_party/json/cmake/config.cmake.in @@ -0,0 +1,15 @@ +include(FindPackageHandleStandardArgs) +set(${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE}) +find_package_handle_standard_args(@PROJECT_NAME@ CONFIG_MODE) + +if(NOT TARGET @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@) + include("${CMAKE_CURRENT_LIST_DIR}/@NLOHMANN_JSON_TARGETS_EXPORT_NAME@.cmake") + if((NOT TARGET @NLOHMANN_JSON_TARGET_NAME@) AND + (NOT @PROJECT_NAME@_FIND_VERSION OR + @PROJECT_NAME@_FIND_VERSION VERSION_LESS 3.2.0)) + add_library(@NLOHMANN_JSON_TARGET_NAME@ INTERFACE IMPORTED) + set_target_properties(@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES + INTERFACE_LINK_LIBRARIES @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ + ) + endif() +endif() diff --git a/libraries/cppdap/third_party/json/include/nlohmann/adl_serializer.hpp b/libraries/cppdap/third_party/json/include/nlohmann/adl_serializer.hpp new file mode 100644 index 000000000..eeaa14257 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/adl_serializer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/from_json.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/from_json.hpp new file mode 100644 index 000000000..224fb33f9 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/from_json.hpp @@ -0,0 +1,389 @@ +#pragma once + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value and + not std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template ::value and + not is_constructible_object_type::value and + not is_constructible_string_type::value and + not is_basic_json::value, + int > = 0 > + +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +template ::value>> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template ::value>> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_chars.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_chars.hpp new file mode 100644 index 000000000..d99703a54 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_chars.hpp @@ -0,0 +1,1106 @@ +#pragma once + +#include // array +#include // assert +#include // or, and, not +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional +#include + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + assert(x.e == y.e); + assert(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + assert(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + assert(std::isfinite(value)); + assert(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 and E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + assert(kAlpha <= cached.e + e + 64); + assert(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + and delta - rest >= ten_k + and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + { + assert(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + assert(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + assert(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + assert(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + assert(e > -1000); + assert(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n and n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n - k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (n + 2); + } + + if (0 < n and n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + assert(k > n); + + std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + buf[n] = '.'; + return buf + (k + 1); + } + + if (min_exp < n and n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2 + (-n) + k); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + buf[1] = '.'; + buf += 1 + k; + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + assert(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last - first >= kMaxExp + 2); + assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + assert(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_json.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_json.hpp new file mode 100644 index 000000000..c3ac5aa8f --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/conversions/to_json.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include // copy +#include // or, and, not +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template ::value and + not is_compatible_object_type< + BasicJsonType, CompatibleArrayType>::value and + not is_compatible_string_type::value and + not is_basic_json::value, + int> = 0> +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template::value and not is_basic_json::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t::value, + int> = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template < typename BasicJsonType, typename T, + enable_if_t>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template +void to_json(BasicJsonType& j, const std::tuple& t) +{ + to_json_tuple_impl(j, t, index_sequence_for {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/exceptions.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/exceptions.hpp new file mode 100644 index 000000000..ed836188c --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/exceptions.hpp @@ -0,0 +1,356 @@ +#pragma once + +#include // exception +#include // runtime_error +#include // to_string + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/binary_reader.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/binary_reader.hpp new file mode 100644 index 000000000..1b6e0f9b7 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/binary_reader.hpp @@ -0,0 +1,1983 @@ +#pragma once + +#include // generate_n +#include // array +#include // assert +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move + +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using json_sax_t = SAX; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + assert(ia); + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result and strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } + + return result; + } + + /*! + @brief determine system byte order + + @return true if and only if system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static constexpr bool little_endianess(int num = 1) noexcept + { + return *reinterpret_cast(&num) == 1; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + + return true; + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const int element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number; + return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len; + string_t value; + return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + while (int element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) + { + return false; + } + + if (not is_array and not sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) and sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu)); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1)); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu)); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1)); + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const int byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + assert(0 <= exp and exp <= 32); + assert(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (not get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) and sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xCA: // float 32 + { + float number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'I': + { + std::int16_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const int prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'I': + { + std::int16_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'l': + { + std::int32_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'L': + { + std::int64_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'd': + { + float number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) and sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return current = ia->get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + int get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string"))) + { + success = false; + } + return static_cast(current); + }); + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/input_adapters.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/input_adapters.hpp new file mode 100644 index 000000000..9512a771e --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/input_adapters.hpp @@ -0,0 +1,442 @@ +#pragma once + +#include // array +#include // assert +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +/*! +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of +exactly one non-EOF character for future input. The int_type characters +returned consist of all valid char values as positive values (typically +unsigned char), plus an EOF value outside that range, specified by the value +of the function std::char_traits::eof(). This value is typically -1, but +could be any arbitrary value which is not a valid char value. +*/ +struct input_adapter_protocol +{ + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + virtual ~input_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; + +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter : public input_adapter_protocol +{ + public: + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = default; + ~file_input_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter : public input_adapter_protocol +{ + public: + ~input_stream_adapter() override + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + is.clear(is.rdstate() & std::ios::eofbit); + } + + explicit input_stream_adapter(std::istream& i) + : is(i), sb(*i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() override + { + auto res = sb.sbumpc(); + // set eof manually, as we don't use the istream interface. + if (res == EOF) + { + is.clear(is.rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream& is; + std::streambuf& sb; +}; + +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol +{ + public: + input_buffer_adapter(const char* b, const std::size_t l) noexcept + : cursor(b), limit(b == nullptr ? nullptr : (b + l)) + {} + + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + ~input_buffer_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + if (JSON_HEDLEY_LIKELY(cursor < limit)) + { + assert(cursor != nullptr and limit != nullptr); + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + private: + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* const limit; +}; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc or wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (current_wchar < str.size()) + { + const auto wc2 = static_cast(str[current_wchar++]); + const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + ++current_wchar; + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +template +class wide_string_input_adapter : public input_adapter_protocol +{ + public: + explicit wide_string_input_adapter(const WideStringType& w) noexcept + : str(w) + {} + + std::char_traits::int_type get_character() noexcept override + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index == 0); + } + + // use buffer + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// the wstring to process + const WideStringType& str; + + /// index of the current wchar in str + std::size_t current_wchar = 0; + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + +class input_adapter +{ + public: + // native support + JSON_HEDLEY_NON_NULL(2) + input_adapter(std::FILE* file) + : ia(std::make_shared(file)) {} + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + input_adapter(const std::wstring& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u16string& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u32string& ws) + : ia(std::make_shared>(ws)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) + { +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; + } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/json_sax.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/json_sax.hpp new file mode 100644 index 000000000..606b7862e --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/json_sax.hpp @@ -0,0 +1,701 @@ +#pragma once + +#include // assert +#include +#include // string +#include // move +#include // vector + +#include +#include + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + /// type for (signed) integers + using number_integer_t = typename BasicJsonType::number_integer_t; + /// type for unsigned integers + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + /// type for floating-point numbers + using number_float_t = typename BasicJsonType::number_float_t; + /// type for strings + using string_t = typename BasicJsonType::string_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + assert(ref_stack.back()->is_object()); + assert(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep and ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() and not callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (not keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + assert(not keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (not keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback or callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (not keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (not ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + assert(ref_stack.back()->is_object()); + // check if we should store an element for the current key + assert(not key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (not store_element) + { + return {false, nullptr}; + } + + assert(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/lexer.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/lexer.hpp new file mode 100644 index 000000000..0843d749d --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/lexer.hpp @@ -0,0 +1,1512 @@ +#pragma once + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } + + explicit lexer(detail::input_adapter_t&& adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = delete; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = delete; + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' and current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + assert(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char* literal_text, const std::size_t length, + token_type return_type) + { + assert(current == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + std::char_traits::int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia->get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + assert(not token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(int c) + { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if ('\x00' <= c and c <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(c); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB and get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 and not skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + detail::input_adapter_t ia = nullptr; + + /// the current character + std::char_traits::int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/parser.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/parser.hpp new file mode 100644 index 000000000..8d4febcbf --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/parser.hpp @@ -0,0 +1,498 @@ +#pragma once + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +/*! +@brief syntax analysis + +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + using parser_callback_t = + std::function; + + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result and strict and (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (not skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(not sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (not context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/input/position_t.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/position_t.hpp new file mode 100644 index 000000000..14e9649fb --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/input/position_t.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/internal_iterator.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/internal_iterator.hpp new file mode 100644 index 000000000..2c81f723f --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iter_impl.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iter_impl.hpp new file mode 100644 index 000000000..3a3629719 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iter_impl.hpp @@ -0,0 +1,638 @@ +#pragma once + +#include // not +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + assert(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iteration_proxy.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iteration_proxy.hpp new file mode 100644 index 000000000..c61d96296 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + target = std::to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iterator_traits.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iterator_traits.hpp new file mode 100644 index 000000000..4cced80ca --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/iterator_traits.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include // random_access_iterator_tag + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp new file mode 100644 index 000000000..f3b5b5db6 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/primitive_iterator.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/primitive_iterator.hpp new file mode 100644 index 000000000..28d6f1a65 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/iterators/primitive_iterator.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/json_pointer.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/json_pointer.hpp new file mode 100644 index 000000000..87af34233 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/json_pointer.hpp @@ -0,0 +1,1011 @@ +#pragma once + +#include // all_of +#include // assert +#include // isdigit +#include // accumulate +#include // string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_index array index to append + @return JSON pointer with @a array_index appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_index) + { + return *this /= std::to_string(array_index); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_index array index + @return a new JSON pointer with @a array_index appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) + { + return json_pointer(ptr) /= array_index; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) + { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; + } + + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + JSON_TRY + { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // use unchecked array access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (not ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + JSON_TRY + { + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(not value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return not (lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/json_ref.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/json_ref.hpp new file mode 100644 index 000000000..c8dec7330 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/json_ref.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)), is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), + is_rvalue(true) {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue; +}; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_scope.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_scope.hpp new file mode 100644 index 000000000..2be7581d1 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_scope.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include // pair +#include + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_unscope.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_unscope.hpp new file mode 100644 index 000000000..80b293e7d --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/macro_unscope.hpp @@ -0,0 +1,21 @@ +#pragma once + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL + +#include diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/cpp_future.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/cpp_future.hpp new file mode 100644 index 000000000..948cd4fb0 --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/cpp_future.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann diff --git a/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/detected.hpp b/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/detected.hpp new file mode 100644 index 000000000..5b52460ac --- /dev/null +++ b/libraries/cppdap/third_party/json/include/nlohmann/detail/meta/detected.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template