diff --git a/README.md b/README.md index 0c9b9d82c75b83e8cc4362abc2061efc5f47821d..c7a35d417f849aa170a5e98ff97bb549370d01ea 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ include(FetchContent) FetchContent_Declare(json GIT_REPOSITORY https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent - GIT_TAG v3.8.0) + GIT_TAG v3.9.0) FetchContent_GetProperties(json) if(NOT json_POPULATED) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index cc822a543be87cc493c223011b303297af5d34ad..6a0f47ab44ad734e39523e7681b1712dd3dc4f12 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.8.0 +| | |__ | | | | | | version 3.9.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -31,11 +31,10 @@ SOFTWARE. #define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 8 +#define NLOHMANN_JSON_VERSION_MINOR 9 #define NLOHMANN_JSON_VERSION_PATCH 0 #include <algorithm> // all_of, find, for_each -#include <cassert> // assert #include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <functional> // hash, less #include <initializer_list> // initializer_list @@ -67,16 +66,6 @@ SOFTWARE. #include <utility> // pair, declval #include <valarray> // valarray -// #include <nlohmann/detail/boolean_operators.hpp> - - -// Header <ciso646> is removed in C++20. -// See <https://github.com/nlohmann/json/issues/2089> for more information. - -#if __cplusplus <= 201703L - #include <ciso646> // and, not, or -#endif - // #include <nlohmann/detail/exceptions.hpp> @@ -2048,7 +2037,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) @@ -2101,6 +2094,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif +// allow to override assert +#if !defined(JSON_ASSERT) + #include <cassert> // assert + #define JSON_ASSERT(x) assert(x) +#endif + /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM @@ -2149,6 +2148,170 @@ JSON_HEDLEY_DIAGNOSTIC_POP NumberIntegerType, NumberUnsignedType, NumberFloatType, \ AllocatorType, JSONSerializer, BinaryType> +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + namespace nlohmann { @@ -2240,6 +2403,7 @@ json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vect json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also @@ -2428,7 +2592,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not 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.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | @@ -2506,9 +2670,6 @@ class other_error : public exception #include <cstddef> // size_t #include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -// #include <nlohmann/detail/boolean_operators.hpp> - - namespace nlohmann { namespace detail @@ -2574,8 +2735,6 @@ constexpr T static_const<T>::value; #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type #include <utility> // declval -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/iterators/iterator_traits.hpp> @@ -2588,11 +2747,11 @@ namespace nlohmann { namespace detail { -template <typename ...Ts> struct make_void +template<typename ...Ts> struct make_void { using type = void; }; -template <typename ...Ts> using void_t = typename make_void<Ts...>::type; +template<typename ...Ts> using void_t = typename make_void<Ts...>::type; } // namespace detail } // namespace nlohmann @@ -2603,10 +2762,10 @@ namespace nlohmann { namespace detail { -template <typename It, typename = void> +template<typename It, typename = void> struct iterator_types {}; -template <typename It> +template<typename It> struct iterator_types < It, void_t<typename It::difference_type, typename It::value_type, typename It::pointer, @@ -2621,18 +2780,18 @@ struct iterator_types < // 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 <typename T, typename = void> +template<typename T, typename = void> struct iterator_traits { }; -template <typename T> +template<typename T> struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >> : iterator_types<T> { }; -template <typename T> +template<typename T> struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>> { using iterator_category = std::random_access_iterator_tag; @@ -2671,39 +2830,39 @@ struct nonesuch void operator=(nonesuch&&) = delete; }; -template <class Default, - class AlwaysVoid, - template <class...> class Op, - class... Args> +template<class Default, + class AlwaysVoid, + template<class...> class Op, + class... Args> struct detector { using value_t = std::false_type; using type = Default; }; -template <class Default, template <class...> class Op, class... Args> +template<class Default, template<class...> class Op, class... Args> struct detector<Default, void_t<Op<Args...>>, Op, Args...> { using value_t = std::true_type; using type = Op<Args...>; }; -template <template <class...> class Op, class... Args> +template<template<class...> class Op, class... Args> using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t; -template <template <class...> class Op, class... Args> +template<template<class...> class Op, class... Args> using detected_t = typename detector<nonesuch, void, Op, Args...>::type; -template <class Default, template <class...> class Op, class... Args> +template<class Default, template<class...> class Op, class... Args> using detected_or = detector<Default, void, Op, Args...>; -template <class Default, template <class...> class Op, class... Args> +template<class Default, template<class...> class Op, class... Args> using detected_or_t = typename detected_or<Default, Op, Args...>::type; -template <class Expected, template <class...> class Op, class... Args> +template<class Expected, template<class...> class Op, class... Args> using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>; -template <class To, template <class...> class Op, class... Args> +template<class To, template<class...> class Op, class... Args> using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>; } // namespace detail @@ -2772,6 +2931,19 @@ uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; + +template<class Key, class T, class IgnoredLess, class Allocator> +struct ordered_map; + +/*! +@brief ordered JSON class + +This type preserves the insertion order of object keys. + +@since version 3.9.0 +*/ +using ordered_json = basic_json<nlohmann::ordered_map>; + } // namespace nlohmann #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -2811,59 +2983,69 @@ struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {}; // json_ref helpers // ////////////////////// -template <typename> +template<typename> class json_ref; template<typename> struct is_json_ref : std::false_type {}; -template <typename T> +template<typename T> struct is_json_ref<json_ref<T>> : std::true_type {}; ////////////////////////// // aliases for detected // ////////////////////////// -template <typename T> +template<typename T> using mapped_type_t = typename T::mapped_type; -template <typename T> +template<typename T> using key_type_t = typename T::key_type; -template <typename T> +template<typename T> using value_type_t = typename T::value_type; -template <typename T> +template<typename T> using difference_type_t = typename T::difference_type; -template <typename T> +template<typename T> using pointer_t = typename T::pointer; -template <typename T> +template<typename T> using reference_t = typename T::reference; -template <typename T> +template<typename T> using iterator_category_t = typename T::iterator_category; -template <typename T> +template<typename T> using iterator_t = typename T::iterator; -template <typename T, typename... Args> +template<typename T, typename... Args> using to_json_function = decltype(T::to_json(std::declval<Args>()...)); -template <typename T, typename... Args> +template<typename T, typename... Args> using from_json_function = decltype(T::from_json(std::declval<Args>()...)); -template <typename T, typename U> +template<typename T, typename U> using get_template_function = decltype(std::declval<T>().template get<U>()); // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists -template <typename BasicJsonType, typename T, typename = void> +template<typename BasicJsonType, typename T, typename = void> struct has_from_json : std::false_type {}; +// trait checking if j.get<T> is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) template <typename BasicJsonType, typename T> -struct has_from_json<BasicJsonType, T, - enable_if_t<not is_basic_json<T>::value>> +struct is_getable +{ + static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value; +}; + +template<typename BasicJsonType, typename T> +struct has_from_json < BasicJsonType, T, + enable_if_t < !is_basic_json<T>::value >> { using serializer = typename BasicJsonType::template json_serializer<T, void>; @@ -2874,11 +3056,11 @@ struct has_from_json<BasicJsonType, T, // This trait checks if JSONSerializer<T>::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types -template <typename BasicJsonType, typename T, typename = void> +template<typename BasicJsonType, typename T, typename = void> struct has_non_default_from_json : std::false_type {}; template<typename BasicJsonType, typename T> -struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> { using serializer = typename BasicJsonType::template json_serializer<T, void>; @@ -2889,11 +3071,11 @@ struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json // This trait checks if BasicJsonType::json_serializer<T>::to_json exists // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template <typename BasicJsonType, typename T, typename = void> +template<typename BasicJsonType, typename T, typename = void> struct has_to_json : std::false_type {}; -template <typename BasicJsonType, typename T> -struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +template<typename BasicJsonType, typename T> +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> { using serializer = typename BasicJsonType::template json_serializer<T, void>; @@ -2907,10 +3089,10 @@ struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> // is_ functions // /////////////////// -template <typename T, typename = void> +template<typename T, typename = void> struct is_iterator_traits : std::false_type {}; -template <typename T> +template<typename T> struct is_iterator_traits<iterator_traits<T>> { private: @@ -2927,20 +3109,20 @@ struct is_iterator_traits<iterator_traits<T>> // source: https://stackoverflow.com/a/37193089/4116453 -template <typename T, typename = void> +template<typename T, typename = void> struct is_complete_type : std::false_type {}; -template <typename T> +template<typename T> struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {}; -template <typename BasicJsonType, typename CompatibleObjectType, - typename = void> +template<typename BasicJsonType, typename CompatibleObjectType, + typename = void> struct is_compatible_object_type_impl : std::false_type {}; -template <typename BasicJsonType, typename CompatibleObjectType> +template<typename BasicJsonType, typename CompatibleObjectType> struct is_compatible_object_type_impl < BasicJsonType, CompatibleObjectType, - enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and + enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&& is_detected<key_type_t, CompatibleObjectType>::value >> { @@ -2949,53 +3131,53 @@ struct is_compatible_object_type_impl < // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = std::is_constructible<typename object_t::key_type, - typename CompatibleObjectType::key_type>::value and + typename CompatibleObjectType::key_type>::value && std::is_constructible<typename object_t::mapped_type, typename CompatibleObjectType::mapped_type>::value; }; -template <typename BasicJsonType, typename CompatibleObjectType> +template<typename BasicJsonType, typename CompatibleObjectType> struct is_compatible_object_type : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {}; -template <typename BasicJsonType, typename ConstructibleObjectType, - typename = void> +template<typename BasicJsonType, typename ConstructibleObjectType, + typename = void> struct is_constructible_object_type_impl : std::false_type {}; -template <typename BasicJsonType, typename ConstructibleObjectType> +template<typename BasicJsonType, typename ConstructibleObjectType> struct is_constructible_object_type_impl < BasicJsonType, ConstructibleObjectType, - enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and + enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&& is_detected<key_type_t, ConstructibleObjectType>::value >> { using object_t = typename BasicJsonType::object_t; static constexpr bool value = - (std::is_default_constructible<ConstructibleObjectType>::value and - (std::is_move_assignable<ConstructibleObjectType>::value or - std::is_copy_assignable<ConstructibleObjectType>::value) and + (std::is_default_constructible<ConstructibleObjectType>::value && + (std::is_move_assignable<ConstructibleObjectType>::value || + std::is_copy_assignable<ConstructibleObjectType>::value) && (std::is_constructible<typename ConstructibleObjectType::key_type, - typename object_t::key_type>::value and + typename object_t::key_type>::value && std::is_same < typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) or + typename ConstructibleObjectType::mapped_type >::value)) || (has_from_json<BasicJsonType, - typename ConstructibleObjectType::mapped_type>::value or + typename ConstructibleObjectType::mapped_type>::value || has_non_default_from_json < BasicJsonType, typename ConstructibleObjectType::mapped_type >::value); }; -template <typename BasicJsonType, typename ConstructibleObjectType> +template<typename BasicJsonType, typename ConstructibleObjectType> struct is_constructible_object_type : is_constructible_object_type_impl<BasicJsonType, ConstructibleObjectType> {}; -template <typename BasicJsonType, typename CompatibleStringType, - typename = void> +template<typename BasicJsonType, typename CompatibleStringType, + typename = void> struct is_compatible_string_type_impl : std::false_type {}; -template <typename BasicJsonType, typename CompatibleStringType> +template<typename BasicJsonType, typename CompatibleStringType> struct is_compatible_string_type_impl < BasicJsonType, CompatibleStringType, enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type, @@ -3005,15 +3187,15 @@ struct is_compatible_string_type_impl < std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; }; -template <typename BasicJsonType, typename ConstructibleStringType> +template<typename BasicJsonType, typename ConstructibleStringType> struct is_compatible_string_type : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {}; -template <typename BasicJsonType, typename ConstructibleStringType, - typename = void> +template<typename BasicJsonType, typename ConstructibleStringType, + typename = void> struct is_constructible_string_type_impl : std::false_type {}; -template <typename BasicJsonType, typename ConstructibleStringType> +template<typename BasicJsonType, typename ConstructibleStringType> struct is_constructible_string_type_impl < BasicJsonType, ConstructibleStringType, enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type, @@ -3024,55 +3206,55 @@ struct is_constructible_string_type_impl < typename BasicJsonType::string_t>::value; }; -template <typename BasicJsonType, typename ConstructibleStringType> +template<typename BasicJsonType, typename ConstructibleStringType> struct is_constructible_string_type : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {}; -template <typename BasicJsonType, typename CompatibleArrayType, typename = void> +template<typename BasicJsonType, typename CompatibleArrayType, typename = void> struct is_compatible_array_type_impl : std::false_type {}; -template <typename BasicJsonType, typename CompatibleArrayType> +template<typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, - enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and - is_detected<iterator_t, CompatibleArrayType>::value and + enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&& + is_detected<iterator_t, CompatibleArrayType>::value&& // This is needed because json_reverse_iterator has a ::iterator type... // Therefore it is detected as a CompatibleArrayType. // The real fix would be to have an Iterable concept. - not is_iterator_traits< - iterator_traits<CompatibleArrayType>>::value >> + !is_iterator_traits < + iterator_traits<CompatibleArrayType >>::value >> { static constexpr bool value = std::is_constructible<BasicJsonType, typename CompatibleArrayType::value_type>::value; }; -template <typename BasicJsonType, typename CompatibleArrayType> +template<typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; -template <typename BasicJsonType, typename ConstructibleArrayType, typename = void> +template<typename BasicJsonType, typename ConstructibleArrayType, typename = void> struct is_constructible_array_type_impl : std::false_type {}; -template <typename BasicJsonType, typename ConstructibleArrayType> +template<typename BasicJsonType, typename ConstructibleArrayType> struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, enable_if_t<std::is_same<ConstructibleArrayType, typename BasicJsonType::value_type>::value >> : std::true_type {}; -template <typename BasicJsonType, typename ConstructibleArrayType> +template<typename BasicJsonType, typename ConstructibleArrayType> struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, - enable_if_t<not std::is_same<ConstructibleArrayType, - typename BasicJsonType::value_type>::value and - std::is_default_constructible<ConstructibleArrayType>::value and -(std::is_move_assignable<ConstructibleArrayType>::value or - std::is_copy_assignable<ConstructibleArrayType>::value) and -is_detected<value_type_t, ConstructibleArrayType>::value and -is_detected<iterator_t, ConstructibleArrayType>::value and -is_complete_type< -detected_t<value_type_t, ConstructibleArrayType>>::value >> + enable_if_t < !std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value&& + std::is_default_constructible<ConstructibleArrayType>::value&& +(std::is_move_assignable<ConstructibleArrayType>::value || + std::is_copy_assignable<ConstructibleArrayType>::value)&& +is_detected<value_type_t, ConstructibleArrayType>::value&& +is_detected<iterator_t, ConstructibleArrayType>::value&& +is_complete_type < +detected_t<value_type_t, ConstructibleArrayType >>::value >> { static constexpr bool value = // This is needed because json_reverse_iterator has a ::iterator type, @@ -3080,30 +3262,30 @@ detected_t<value_type_t, ConstructibleArrayType>>::value >> // base class `iterator`... Therefore it is detected as a // ConstructibleArrayType. The real fix would be to have an Iterable // concept. - not is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value and + !is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value && (std::is_same<typename ConstructibleArrayType::value_type, - typename BasicJsonType::array_t::value_type>::value or + typename BasicJsonType::array_t::value_type>::value || has_from_json<BasicJsonType, - typename ConstructibleArrayType::value_type>::value or + typename ConstructibleArrayType::value_type>::value || has_non_default_from_json < BasicJsonType, typename ConstructibleArrayType::value_type >::value); }; -template <typename BasicJsonType, typename ConstructibleArrayType> +template<typename BasicJsonType, typename ConstructibleArrayType> struct is_constructible_array_type : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {}; -template <typename RealIntegerType, typename CompatibleNumberIntegerType, - typename = void> +template<typename RealIntegerType, typename CompatibleNumberIntegerType, + typename = void> struct is_compatible_integer_type_impl : std::false_type {}; -template <typename RealIntegerType, typename CompatibleNumberIntegerType> +template<typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type_impl < RealIntegerType, CompatibleNumberIntegerType, - enable_if_t<std::is_integral<RealIntegerType>::value and - std::is_integral<CompatibleNumberIntegerType>::value and - not std::is_same<bool, CompatibleNumberIntegerType>::value >> + enable_if_t < std::is_integral<RealIntegerType>::value&& + std::is_integral<CompatibleNumberIntegerType>::value&& + !std::is_same<bool, CompatibleNumberIntegerType>::value >> { // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits<RealIntegerType>; @@ -3111,20 +3293,20 @@ struct is_compatible_integer_type_impl < static constexpr auto value = std::is_constructible<RealIntegerType, - CompatibleNumberIntegerType>::value and - CompatibleLimits::is_integer and + CompatibleNumberIntegerType>::value && + CompatibleLimits::is_integer && RealLimits::is_signed == CompatibleLimits::is_signed; }; -template <typename RealIntegerType, typename CompatibleNumberIntegerType> +template<typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type : is_compatible_integer_type_impl<RealIntegerType, CompatibleNumberIntegerType> {}; -template <typename BasicJsonType, typename CompatibleType, typename = void> +template<typename BasicJsonType, typename CompatibleType, typename = void> struct is_compatible_type_impl: std::false_type {}; -template <typename BasicJsonType, typename CompatibleType> +template<typename BasicJsonType, typename CompatibleType> struct is_compatible_type_impl < BasicJsonType, CompatibleType, enable_if_t<is_complete_type<CompatibleType>::value >> @@ -3133,7 +3315,7 @@ struct is_compatible_type_impl < has_to_json<BasicJsonType, CompatibleType>::value; }; -template <typename BasicJsonType, typename CompatibleType> +template<typename BasicJsonType, typename CompatibleType> struct is_compatible_type : is_compatible_type_impl<BasicJsonType, CompatibleType> {}; @@ -3144,10 +3326,10 @@ template<class B1, class... Bn> struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {}; -template <typename T1, typename T2> +template<typename T1, typename T2> struct is_constructible_tuple : std::false_type {}; -template <typename T1, typename... Args> +template<typename T1, typename... Args> struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {}; } // namespace detail } // namespace nlohmann @@ -3160,9 +3342,6 @@ struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_con #include <cstdint> // uint8_t #include <string> // string -// #include <nlohmann/detail/boolean_operators.hpp> - - namespace nlohmann { namespace detail @@ -3233,7 +3412,7 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept const auto l_index = static_cast<std::size_t>(lhs); const auto r_index = static_cast<std::size_t>(rhs); - return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; } } // namespace detail } // namespace nlohmann @@ -3246,7 +3425,7 @@ namespace detail template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { - if (JSON_HEDLEY_UNLIKELY(not j.is_null())) + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); } @@ -3254,10 +3433,10 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) } // overloads for basic_json template parameters -template<typename BasicJsonType, typename ArithmeticType, - enable_if_t<std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, - int> = 0> +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic<ArithmeticType>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, + int > = 0 > void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) @@ -3286,7 +3465,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { - if (JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); } @@ -3296,7 +3475,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { - if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } @@ -3306,13 +3485,13 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) template < typename BasicJsonType, typename ConstructibleStringType, enable_if_t < - is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and - not std::is_same<typename BasicJsonType::string_t, - ConstructibleStringType>::value, + is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&& + !std::is_same<typename BasicJsonType::string_t, + ConstructibleStringType>::value, int > = 0 > void from_json(const BasicJsonType& j, ConstructibleStringType& s) { - if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } @@ -3349,10 +3528,10 @@ void from_json(const BasicJsonType& j, EnumType& e) // forward_list doesn't have an insert method template<typename BasicJsonType, typename T, typename Allocator, - enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> + enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) { - if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } @@ -3366,18 +3545,22 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) // valarray doesn't have an insert method template<typename BasicJsonType, typename T, - enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> + enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> void from_json(const BasicJsonType& j, std::valarray<T>& l) { - if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } l.resize(j.size()); - std::copy(j.begin(), j.end(), std::begin(l)); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get<T>(); + }); } -template <typename BasicJsonType, typename T, std::size_t N> +template<typename BasicJsonType, typename T, std::size_t N> auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get<T>(), void()) { @@ -3393,7 +3576,7 @@ void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>(); } -template <typename BasicJsonType, typename T, std::size_t N> +template<typename BasicJsonType, typename T, std::size_t N> auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/) -> decltype(j.template get<T>(), void()) @@ -3425,7 +3608,7 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p arr = std::move(ret); } -template <typename BasicJsonType, typename ConstructibleArrayType> +template<typename BasicJsonType, typename ConstructibleArrayType> void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { @@ -3443,20 +3626,20 @@ void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, arr = std::move(ret); } -template <typename BasicJsonType, typename ConstructibleArrayType, - enable_if_t < - is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and - not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and - not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and - not std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value and - not is_basic_json<ConstructibleArrayType>::value, - int > = 0 > +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&& + !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&& + !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&& + !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&& + !is_basic_json<ConstructibleArrayType>::value, + int > = 0 > auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get<typename ConstructibleArrayType::value_type>(), void()) { - if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -3465,10 +3648,10 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } -template <typename BasicJsonType> +template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { - if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); } @@ -3480,7 +3663,7 @@ template<typename BasicJsonType, typename ConstructibleObjectType, enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { - if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); } @@ -3502,14 +3685,14 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? -template<typename BasicJsonType, typename ArithmeticType, - enable_if_t < - std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, - int> = 0> +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic<ArithmeticType>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, + int > = 0 > void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) @@ -3558,19 +3741,19 @@ void from_json(const BasicJsonType& j, std::tuple<Args...>& t) from_json_tuple_impl(j, t, index_sequence_for<Args...> {}); } -template <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, - typename = enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, Key>::value>> +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m) { - if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } m.clear(); for (const auto& p : j) { - if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); } @@ -3578,19 +3761,19 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& } } -template <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, - typename = enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, Key>::value>> +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m) { - if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } m.clear(); for (const auto& p : j) { - if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); } @@ -3631,8 +3814,6 @@ constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::va #include <valarray> // valarray #include <vector> // vector -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/iterators/iteration_proxy.hpp> @@ -3653,9 +3834,11 @@ namespace detail template<typename string_type> void int_to_string( string_type& target, std::size_t value ) { - target = std::to_string(value); + // For ADL + using std::to_string; + target = to_string(value); } -template <typename IteratorType> class iteration_proxy_value +template<typename IteratorType> class iteration_proxy_value { public: using difference_type = std::ptrdiff_t; @@ -3710,7 +3893,7 @@ template <typename IteratorType> class iteration_proxy_value /// return key of the iterator const string_type& key() const { - assert(anchor.m_object != nullptr); + JSON_ASSERT(anchor.m_object != nullptr); switch (anchor.m_object->type()) { @@ -3769,7 +3952,7 @@ template<typename IteratorType> class iteration_proxy // Structured Bindings Support // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 -template <std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0> +template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0> auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key()) { return i.key(); @@ -3777,7 +3960,7 @@ auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decl // Structured Bindings Support // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 -template <std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0> +template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0> auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value()) { return i.value(); @@ -3796,11 +3979,11 @@ namespace std #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmismatched-tags" #endif -template <typename IteratorType> +template<typename IteratorType> class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> : public std::integral_constant<std::size_t, 2> {}; -template <std::size_t N, typename IteratorType> +template<std::size_t N, typename IteratorType> class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> { public: @@ -3861,9 +4044,9 @@ struct external_constructor<value_t::string> j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleStringType, - enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value, - int> = 0> + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value, + int > = 0 > static void construct(BasicJsonType& j, const CompatibleStringType& str) { j.m_type = value_t::string; @@ -3949,9 +4132,9 @@ struct external_constructor<value_t::array> j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value, - int> = 0> + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value, + int > = 0 > static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; @@ -4008,8 +4191,8 @@ struct external_constructor<value_t::object> j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0> + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; @@ -4080,20 +4263,20 @@ void to_json(BasicJsonType& j, const std::vector<bool>& e) external_constructor<value_t::array>::construct(j, e); } -template <typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<is_compatible_array_type<BasicJsonType, - CompatibleArrayType>::value and - not is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and - not std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value and - not is_basic_json<CompatibleArrayType>::value, - int> = 0> +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type<BasicJsonType, + CompatibleArrayType>::value&& + !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&& + !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&& + !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&& + !is_basic_json<CompatibleArrayType>::value, + int > = 0 > void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor<value_t::array>::construct(j, arr); } -template <typename BasicJsonType> +template<typename BasicJsonType> void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) { external_constructor<value_t::binary>::construct(j, bin); @@ -4112,8 +4295,8 @@ void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) external_constructor<value_t::array>::construct(j, std::move(arr)); } -template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0> +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 > void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor<value_t::object>::construct(j, obj); @@ -4127,9 +4310,9 @@ void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) template < typename BasicJsonType, typename T, std::size_t N, - enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, - const T(&)[N]>::value, - int> = 0 > + enable_if_t < !std::is_constructible<typename BasicJsonType::string_t, + const T(&)[N]>::value, + int > = 0 > void to_json(BasicJsonType& j, const T(&arr)[N]) { external_constructor<value_t::array>::construct(j, arr); @@ -4142,8 +4325,8 @@ void to_json(BasicJsonType& j, const std::pair<T1, T2>& p) } // for https://github.com/nlohmann/json/pull/1134 -template < typename BasicJsonType, typename T, - enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0> +template<typename BasicJsonType, typename T, + enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0> void to_json(BasicJsonType& j, const T& b) { j = { {b.key(), b.value()} }; @@ -4212,7 +4395,7 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template <typename BasicJsonType, typename ValueType> + template<typename BasicJsonType, typename ValueType> static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val)))) -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void()) @@ -4391,20 +4574,136 @@ class byte_container_with_subtype : public BinaryType } // namespace nlohmann -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/conversions/from_json.hpp> // #include <nlohmann/detail/conversions/to_json.hpp> // #include <nlohmann/detail/exceptions.hpp> +// #include <nlohmann/detail/hash.hpp> + + +#include <cstddef> // size_t, uint8_t +#include <functional> // hash + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template<typename BasicJsonType> +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast<std::size_t>(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash<string_t> {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash<bool> {}(j.template get<bool>()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>()); + return combine(type, h); + } + + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>()); + return combine(type, h); + } + + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>()); + return combine(type, h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash<bool> {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, j.get_binary().subtype()); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash<std::uint8_t> {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + // #include <nlohmann/detail/input/binary_reader.hpp> #include <algorithm> // generate_n #include <array> // array -#include <cassert> // assert #include <cmath> // ldexp #include <cstddef> // size_t #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t @@ -4421,7 +4720,6 @@ class byte_container_with_subtype : public BinaryType #include <array> // array -#include <cassert> // assert #include <cstddef> // size_t #include <cstdio> //FILE * #include <cstring> // strlen @@ -4459,7 +4757,7 @@ class file_input_adapter using char_type = char; JSON_HEDLEY_NON_NULL(2) - explicit file_input_adapter(std::FILE* f) noexcept + explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) {} @@ -4498,7 +4796,7 @@ class input_stream_adapter { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - if (is) + if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } @@ -4513,7 +4811,7 @@ class input_stream_adapter input_stream_adapter& operator=(input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; - input_stream_adapter(input_stream_adapter&& rhs) : is(rhs.is), sb(rhs.sb) + input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb) { rhs.is = nullptr; rhs.sb = nullptr; @@ -4673,7 +4971,7 @@ struct wide_string_input_helper<BaseInputAdapter, 2> utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); utf8_bytes_filled = 2; } - else if (0xD800 > wc or wc >= 0xE000) + else if (0xD800 > wc || wc >= 0xE000) { utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u))); utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu)); @@ -4682,7 +4980,7 @@ struct wide_string_input_helper<BaseInputAdapter, 2> } else { - if (JSON_HEDLEY_UNLIKELY(not input.empty())) + if (JSON_HEDLEY_UNLIKELY(!input.empty())) { const auto wc2 = static_cast<unsigned int>(input.get_character()); const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); @@ -4719,13 +5017,13 @@ class wide_string_input_adapter { fill_buffer<sizeof(WideCharType)>(); - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index == 0); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); } // use buffer - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index < utf8_bytes_filled); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); return utf8_bytes[utf8_bytes_index++]; } @@ -4825,15 +5123,15 @@ using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const // Null-delimited strings, and the like. template < typename CharT, typename std::enable_if < - std::is_pointer<CharT>::value and - not std::is_array<CharT>::value and - std::is_integral<typename std::remove_pointer<CharT>::type>::value and + std::is_pointer<CharT>::value&& + !std::is_array<CharT>::value&& + std::is_integral<typename std::remove_pointer<CharT>::type>::value&& sizeof(typename std::remove_pointer<CharT>::type) == 1, int >::type = 0 > contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast<const char*>(b)); - auto ptr = reinterpret_cast<const char*>(b); + const auto* ptr = reinterpret_cast<const char*>(b); return input_adapter(ptr, ptr + length); } @@ -4849,12 +5147,12 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) class span_input_adapter { public: - template<typename CharT, - typename std::enable_if< - std::is_pointer<CharT>::value and - std::is_integral<typename std::remove_pointer<CharT>::type>::value and - sizeof(typename std::remove_pointer<CharT>::type) == 1, - int>::type = 0> + template < typename CharT, + typename std::enable_if < + std::is_pointer<CharT>::value&& + std::is_integral<typename std::remove_pointer<CharT>::type>::value&& + sizeof(typename std::remove_pointer<CharT>::type) == 1, + int >::type = 0 > span_input_adapter(CharT b, std::size_t l) : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {} @@ -4879,7 +5177,6 @@ class span_input_adapter // #include <nlohmann/detail/input/json_sax.hpp> -#include <cassert> // assert #include <cstddef> #include <string> // string #include <utility> // move @@ -5099,7 +5396,7 @@ class json_sax_dom_parser { 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())) + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); @@ -5125,7 +5422,7 @@ class json_sax_dom_parser { 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())) + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); @@ -5140,30 +5437,15 @@ class json_sax_dom_parser return true; } + template<class Exception> bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const detail::exception& ex) + const Exception& ex) { errored = true; + static_cast<void>(ex); if (allow_exceptions) { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) - { - case 1: - JSON_THROW(*static_cast<const detail::parse_error*>(&ex)); - case 4: - JSON_THROW(*static_cast<const detail::out_of_range*>(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex)); - case 3: - JSON_THROW(*static_cast<const detail::type_error*>(&ex)); - case 5: - JSON_THROW(*static_cast<const detail::other_error*>(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP - } + JSON_THROW(ex); } return false; } @@ -5190,7 +5472,7 @@ class json_sax_dom_parser return &root; } - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); if (ref_stack.back()->is_array()) { @@ -5198,8 +5480,8 @@ class json_sax_dom_parser return &(ref_stack.back()->m_value.array->back()); } - assert(ref_stack.back()->is_object()); - assert(object_element); + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward<Value>(v)); return object_element; } @@ -5295,7 +5577,7 @@ class json_sax_dom_callback_parser 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())) + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); } @@ -5312,7 +5594,7 @@ class json_sax_dom_callback_parser key_keep_stack.push_back(keep); // add discarded value at given key and store the reference for later - if (keep and ref_stack.back()) + if (keep && ref_stack.back()) { object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); } @@ -5322,18 +5604,18 @@ class json_sax_dom_callback_parser bool end_object() { - if (ref_stack.back() and not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + if (ref_stack.back() && !callback(static_cast<int>(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()); + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!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_structured()) + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) { // remove discarded value for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) @@ -5358,7 +5640,7 @@ class json_sax_dom_callback_parser 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())) + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); } @@ -5373,20 +5655,20 @@ class json_sax_dom_callback_parser if (ref_stack.back()) { keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (not keep) + if (!keep) { // discard array *ref_stack.back() = discarded; } } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!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()) + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->pop_back(); } @@ -5394,30 +5676,15 @@ class json_sax_dom_callback_parser return true; } + template<class Exception> bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const detail::exception& ex) + const Exception& ex) { errored = true; + static_cast<void>(ex); if (allow_exceptions) { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) - { - case 1: - JSON_THROW(*static_cast<const detail::parse_error*>(&ex)); - case 4: - JSON_THROW(*static_cast<const detail::out_of_range*>(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex)); - case 3: - JSON_THROW(*static_cast<const detail::type_error*>(&ex)); - case 5: - JSON_THROW(*static_cast<const detail::other_error*>(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP - } + JSON_THROW(ex); } return false; } @@ -5446,11 +5713,11 @@ class json_sax_dom_callback_parser template<typename Value> std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false) { - assert(not keep_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); // do not handle this value if we know it would be added to a discarded // container - if (not keep_stack.back()) + if (!keep_stack.back()) { return {false, nullptr}; } @@ -5459,10 +5726,10 @@ class json_sax_dom_callback_parser auto value = BasicJsonType(std::forward<Value>(v)); // check callback - const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value); + const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value); // do not handle this value if we just learnt it shall be discarded - if (not keep) + if (!keep) { return {false, nullptr}; } @@ -5475,13 +5742,13 @@ class json_sax_dom_callback_parser // 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()) + if (!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()); + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); // array if (ref_stack.back()->is_array()) @@ -5491,18 +5758,18 @@ class json_sax_dom_callback_parser } // object - assert(ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_object()); // check if we should store an element for the current key - assert(not key_keep_stack.empty()); + JSON_ASSERT(!key_keep_stack.empty()); const bool store_element = key_keep_stack.back(); key_keep_stack.pop_back(); - if (not store_element) + if (!store_element) { return {false, nullptr}; } - assert(object_element); + JSON_ASSERT(object_element); *object_element = std::move(value); return {true, object_element}; } @@ -5606,3960 +5873,4209 @@ class json_sax_acceptor } // namespace nlohmann -// #include <nlohmann/detail/macro_scope.hpp> +// #include <nlohmann/detail/input/lexer.hpp> -// #include <nlohmann/detail/meta/is_sax.hpp> +#include <array> // array +#include <clocale> // localeconv +#include <cstddef> // size_t +#include <cstdio> // snprintf +#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull +#include <initializer_list> // initializer_list +#include <string> // char_traits, string +#include <utility> // move +#include <vector> // vector -#include <cstdint> // size_t -#include <utility> // declval -#include <string> // string +// #include <nlohmann/detail/input/input_adapters.hpp> -// #include <nlohmann/detail/meta/detected.hpp> +// #include <nlohmann/detail/input/position_t.hpp> -// #include <nlohmann/detail/meta/type_traits.hpp> +// #include <nlohmann/detail/macro_scope.hpp> namespace nlohmann { namespace detail { -template <typename T> -using null_function_t = decltype(std::declval<T&>().null()); - -template <typename T> -using boolean_function_t = - decltype(std::declval<T&>().boolean(std::declval<bool>())); - -template <typename T, typename Integer> -using number_integer_function_t = - decltype(std::declval<T&>().number_integer(std::declval<Integer>())); - -template <typename T, typename Unsigned> -using number_unsigned_function_t = - decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>())); - -template <typename T, typename Float, typename String> -using number_float_function_t = decltype(std::declval<T&>().number_float( - std::declval<Float>(), std::declval<const String&>())); - -template <typename T, typename String> -using string_function_t = - decltype(std::declval<T&>().string(std::declval<String&>())); - -template <typename T> -using start_object_function_t = - decltype(std::declval<T&>().start_object(std::declval<std::size_t>())); - -template <typename T, typename String> -using key_function_t = - decltype(std::declval<T&>().key(std::declval<String&>())); - -template <typename T> -using end_object_function_t = decltype(std::declval<T&>().end_object()); - -template <typename T> -using start_array_function_t = - decltype(std::declval<T&>().start_array(std::declval<std::size_t>())); - -template <typename T> -using end_array_function_t = decltype(std::declval<T&>().end_array()); - -template <typename T, typename Exception> -using parse_error_function_t = decltype(std::declval<T&>().parse_error( - std::declval<std::size_t>(), std::declval<const std::string&>(), - std::declval<const Exception&>())); +/////////// +// lexer // +/////////// -template <typename SAX, typename BasicJsonType> -struct is_sax +template<typename BasicJsonType> +class lexer_base { - private: - static_assert(is_basic_json<BasicJsonType>::value, - "BasicJsonType must be of type basic_json<...>"); - - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using exception_t = typename BasicJsonType::exception; - public: - static constexpr bool value = - is_detected_exact<bool, null_function_t, SAX>::value && - is_detected_exact<bool, boolean_function_t, SAX>::value && - is_detected_exact<bool, number_integer_function_t, SAX, - number_integer_t>::value && - is_detected_exact<bool, number_unsigned_function_t, SAX, - number_unsigned_t>::value && - is_detected_exact<bool, number_float_function_t, SAX, number_float_t, - string_t>::value && - is_detected_exact<bool, string_function_t, SAX, string_t>::value && - is_detected_exact<bool, start_object_function_t, SAX>::value && - is_detected_exact<bool, key_function_t, SAX, string_t>::value && - is_detected_exact<bool, end_object_function_t, SAX>::value && - is_detected_exact<bool, start_array_function_t, SAX>::value && - is_detected_exact<bool, end_array_function_t, SAX>::value && - is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value; -}; - -template <typename SAX, typename BasicJsonType> -struct is_sax_static_asserts -{ - private: - static_assert(is_basic_json<BasicJsonType>::value, - "BasicJsonType must be of type basic_json<...>"); - - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using exception_t = typename BasicJsonType::exception; + /// 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) + }; - public: - static_assert(is_detected_exact<bool, null_function_t, SAX>::value, - "Missing/invalid function: bool null()"); - static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value, - "Missing/invalid function: bool boolean(bool)"); - static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value, - "Missing/invalid function: bool boolean(bool)"); - static_assert( - is_detected_exact<bool, number_integer_function_t, SAX, - number_integer_t>::value, - "Missing/invalid function: bool number_integer(number_integer_t)"); - static_assert( - is_detected_exact<bool, number_unsigned_function_t, SAX, - number_unsigned_t>::value, - "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); - static_assert(is_detected_exact<bool, number_float_function_t, SAX, - number_float_t, string_t>::value, - "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); - static_assert( - is_detected_exact<bool, string_function_t, SAX, string_t>::value, - "Missing/invalid function: bool string(string_t&)"); - static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value, - "Missing/invalid function: bool start_object(std::size_t)"); - static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value, - "Missing/invalid function: bool key(string_t&)"); - static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value, - "Missing/invalid function: bool end_object()"); - static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value, - "Missing/invalid function: bool start_array(std::size_t)"); - static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value, - "Missing/invalid function: bool end_array()"); - static_assert( - is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value, - "Missing/invalid function: bool parse_error(std::size_t, const " - "std::string&, const exception&)"); + /// 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 "<uninitialized>"; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return "<parse error>"; + 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 + } + } }; -} // namespace detail -} // namespace nlohmann - -// #include <nlohmann/detail/value_t.hpp> - - -namespace nlohmann -{ -namespace detail -{ - /*! -@brief determine system byte order - -@return true if and only if system's byte order is little endian - -@note from https://stackoverflow.com/a/1001328/266378 -*/ -static inline bool little_endianess(int num = 1) noexcept -{ - return *reinterpret_cast<char*>(&num) == 1; -} - - -/////////////////// -// binary reader // -/////////////////// +@brief lexical analysis -/*! -@brief deserialization of CBOR, MessagePack, and UBJSON values +This class organizes the lexical analysis during JSON deserialization. */ -template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>> -class binary_reader +template<typename BasicJsonType, typename InputAdapterType> +class lexer : public lexer_base<BasicJsonType> { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using json_sax_t = SAX; using char_type = typename InputAdapterType::char_type; using char_int_type = typename std::char_traits<char_type>::int_type; public: - /*! - @brief create a binary reader + using token_type = typename lexer_base<BasicJsonType>::token_type; - @param[in] adapter input adapter to read from - */ - explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) - { - (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; - } + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast<char_int_type>(get_decimal_point())) + {} - // 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; + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; + ~lexer() = 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 + private: + ///////////////////// + // locales + ///////////////////// - @return - */ - JSON_HEDLEY_NON_NULL(3) - bool sax_parse(const input_format_t format, - json_sax_t* sax_, - const bool strict = true) + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept { - sax = sax_; - bool result = false; + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } - switch (format) - { - case input_format_t::bson: - result = parse_bson_internal(); - break; + ///////////////////// + // scan functions + ///////////////////// - case input_format_t::cbor: - result = parse_cbor_internal(); - break; + /*! + @brief get codepoint from 4 hex characters following `\u` - case input_format_t::msgpack: - result = parse_msgpack_internal(); - break; + 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) - case input_format_t::ubjson: - result = parse_ubjson_internal(); - break; + 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. - default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE - } + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; - // strict mode: next byte must be EOF - if (result and strict) + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) { - if (format == input_format_t::ubjson) + get(); + + if (current >= '0' && current <= '9') { - get_ignore_noop(); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor); } - else + else if (current >= 'A' && current <= 'F') { - get(); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor); } - - if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof())) + else if (current >= 'a' && current <= 'f') { - 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"))); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor); + } + else + { + return -1; } } - return result; + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; } - 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<std::int32_t, true>(input_format_t::bson, document_size); + @brief check if the next byte(s) are inside a given range - if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) - { - return false; - } + 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. - if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false))) - { - return false; - } + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively - return sax->end_object(); - } + @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. - /*! - @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. + @return true if and only if no range violation was detected */ - bool get_bson_cstr(string_t& result) + bool next_byte_in_range(std::initializer_list<char_int_type> ranges) { - auto out = std::back_inserter(result); - while (true) + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) { - return false; + add(current); } - if (current == 0x00) + else { - return true; + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; } - *out++ = static_cast<typename string_t::value_type>(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<typename NumberType> - 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"))); - } + @brief scan a string literal - return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) and get() != std::char_traits<char_type>::eof(); - } + 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. - /*! - @brief Parses a byte array input of length @a len from the BSON input. - @param[in] len The length of the byte array to be read. - @param[in, out] result A reference to the binary variable where the read - array is to be stored. - @tparam NumberType The type of the length @a len - @pre len >= 0 - @return `true` if the byte array was successfully parsed + @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. */ - template<typename NumberType> - bool get_bson_binary(const NumberType len, binary_t& result) + token_type scan_string() { - if (JSON_HEDLEY_UNLIKELY(len < 0)) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); - } - - // All BSON binary values have a subtype - std::uint8_t subtype; - get_number<std::uint8_t>(input_format_t::bson, subtype); - result.set_subtype(subtype); + // reset token_buffer (ignore opening quote) + reset(); - return get_binary(input_format_t::bson, len, result); - } + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); - /*! - @brief Read a BSON document element of the given @a element_type. - @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html - @param[in] element_type_parse_position The position in the input stream, - where the `element_type` was read. - @warning Not all BSON element types are supported yet. An unsupported - @a element_type will give rise to a parse_error.114: - Unsupported BSON record type 0x... - @return whether a valid BSON-object/array was passed to the SAX parser - */ - bool parse_bson_element_internal(const char_int_type element_type, - const std::size_t element_type_parse_position) - { - switch (element_type) + while (true) { - case 0x01: // double - { - double number; - return get_number<double, true>(input_format_t::bson, number) and sax->number_float(static_cast<number_float_t>(number), ""); - } - - case 0x02: // string + // get next character + switch (get()) { - std::int32_t len; - string_t value; - return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); - } + // end of file while parsing string + case std::char_traits<char_type>::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } - case 0x03: // object - { - return parse_bson_internal(); - } + // closing quote + case '\"': + { + return token_type::value_string; + } - case 0x04: // array - { - return parse_bson_array(); - } + // 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; - case 0x05: // binary - { - std::int32_t len; - binary_t value; - return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); - } + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 - case 0x08: // boolean - { - return sax->boolean(get() != 0); - } + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - case 0x0A: // null - { - return sax->null(); - } + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); - case 0x10: // int32 - { - std::int32_t value; - return get_number<std::int32_t, true>(input_format_t::bson, value) and sax->number_integer(value); - } + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - case 0x12: // int64 - { - std::int64_t value; - return get_number<std::int64_t, true>(input_format_t::bson, value) and sax->number_integer(value); - } + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast<int>( + // high surrogate occupies the most significant 22 bits + (static_cast<unsigned int>(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast<unsigned int>(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } - default: // anything else not supported (yet) - { - std::array<char, 3> cr{{}}; - (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(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()))); - } - } - } + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); - /*! - @brief Read a BSON element list (as specified in the BSON-spec) + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast<char_int_type>(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + } - 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). + break; + } - @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; + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } - while (auto element_type = get()) - { - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) - { - return false; - } + break; + } - const std::size_t element_type_parse_position = chars_read; - if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) - { - return false; - } + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } - if (not is_array and not sax->key(key)) - { - return false; - } + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } - if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position))) - { - return false; - } + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } - // get_bson_cstr only appends - key.clear(); - } + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } - return true; - } + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } - /*! - @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<std::int32_t, true>(input_format_t::bson, document_size); + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } - if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) - { - return false; - } + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } - if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true))) - { - return false; - } + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } - return sax->end_array(); - } + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } - ////////// - // CBOR // - ////////// + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } - /*! - @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 + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } - @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<char_type>::eof(): - return unexpect_eof(input_format_t::cbor, "value"); + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } - // 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<number_unsigned_t>(current)); + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } - 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 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } - 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 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } - 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 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } - 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); - } + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } - // 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<std::int8_t>(0x20 - 1 - current)); + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } - 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<number_integer_t>(-1) - number); - } + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } - 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<number_integer_t>(-1) - number); - } + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } - 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<number_integer_t>(-1) - number); - } + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } - 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<number_integer_t>(-1) - - static_cast<number_integer_t>(number)); - } + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } - // Binary data (0x00..0x17 bytes follow) - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: // Binary data (one-byte uint8_t for n follows) - case 0x59: // Binary data (two-byte uint16_t for n follow) - case 0x5A: // Binary data (four-byte uint32_t for n follow) - case 0x5B: // Binary data (eight-byte uint64_t for n follow) - case 0x5F: // Binary data (indefinite length) - { - binary_t b; - return get_cbor_binary(b) and sax->binary(b); - } - - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7F: // UTF-8 string (indefinite length) - { - string_t s; - return get_cbor_string(s) 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<std::size_t>(static_cast<unsigned int>(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<std::size_t>(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<std::size_t>(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<std::size_t>(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<std::size_t>(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<std::size_t>(static_cast<unsigned int>(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<std::size_t>(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<std::size_t>(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<std::size_t>(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<std::size_t>(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 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } - case 0xF6: // null - return sax->null(); + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } - case 0xF9: // Half-Precision Float (two-byte IEEE 754) - { - const auto byte1_raw = get(); - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + case 0x18: { - return false; + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; } - const auto byte2_raw = get(); - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + + case 0x19: { - return false; + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; } - const auto byte1 = static_cast<unsigned char>(byte1_raw); - const auto byte2 = static_cast<unsigned char>(byte2_raw); + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } - // 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<unsigned int>((byte1 << 8u) + byte2); - const double val = [&half] + case 0x1B: { - 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<double>::infinity() - : std::numeric_limits<double>::quiet_NaN(); - default: - return std::ldexp(mant + 1024, exp - 25); - } - }(); - return sax->number_float((half & 0x8000u) != 0 - ? static_cast<number_float_t>(-val) - : static_cast<number_float_t>(val), ""); - } + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } - 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_float_t>(number), ""); - } + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } - 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_float_t>(number), ""); - } + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } - 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"))); - } - } - } + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } - /*! - @brief reads a CBOR string + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } - 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; - } + // 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; + } - 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<unsigned int>(current) & 0x1Fu, result); - } + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } - 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); - } + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - 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); - } + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - 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); - } + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - 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); - } + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - case 0x7F: // UTF-8 string (indefinite length) - { - while (get() != 0xFF) + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: { - string_t chunk; - if (not get_cbor_string(chunk)) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { - return false; + return token_type::parse_error; } - result.append(chunk); + break; } - 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"))); - } + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } } } /*! - @brief reads a CBOR byte array - - This function first reads starting bytes to determine the expected - byte array length and then copies this number of bytes into the byte array. - Additionally, CBOR's byte arrays with indefinite lengths are supported. - - @param[out] result created byte array - - @return whether byte array creation completed - */ - bool get_cbor_binary(binary_t& result) + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() { - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) - { - return false; - } - - switch (current) + switch (get()) { - // Binary data (0x00..0x17 bytes follow) - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: + // single-line comments skip input until a newline or EOF is read + case '/': { - return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result); - } + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits<char_type>::eof(): + case '\0': + return true; - case 0x58: // Binary data (one-byte uint8_t for n follows) - { - std::uint8_t len; - return get_number(input_format_t::cbor, len) and - get_binary(input_format_t::cbor, len, result); + default: + break; + } + } } - case 0x59: // Binary data (two-byte uint16_t for n follow) + // multi-line comments skip input until */ is read + case '*': { - std::uint16_t len; - return get_number(input_format_t::cbor, len) and - get_binary(input_format_t::cbor, len, result); - } + while (true) + { + switch (get()) + { + case std::char_traits<char_type>::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } - case 0x5A: // Binary data (four-byte uint32_t for n follow) - { - std::uint32_t len; - return get_number(input_format_t::cbor, len) and - get_binary(input_format_t::cbor, len, result); - } + case '*': + { + switch (get()) + { + case '/': + return true; - case 0x5B: // Binary data (eight-byte uint64_t for n follow) - { - std::uint64_t len; - return get_number(input_format_t::cbor, len) and - get_binary(input_format_t::cbor, len, result); - } + default: + { + unget(); + break; + } + } + } - case 0x5F: // Binary data (indefinite length) - { - while (get() != 0xFF) - { - binary_t chunk; - if (not get_cbor_binary(chunk)) - { - return false; + default: + break; } - result.insert(result.end(), chunk.begin(), chunk.end()); } - return true; } + // unexpected character after reading '/' default: { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; } } } - /*! - @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) + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept { - if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) - { - return false; - } + f = std::strtof(str, endptr); + } - 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; - } - } - } + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } - return sax->end_array(); + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); } /*! - @param[in] len the length of the object or std::size_t(-1) for an - object of indefinite size - @return whether object creation completed + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. */ - bool get_cbor_object(const std::size_t len) + token_type scan_number() // lgtm [cpp/use-of-goto] { - if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) - { - return false; - } + // reset token_buffer to store the number's bytes + reset(); - string_t key; - if (len != std::size_t(-1)) + // 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) { - for (std::size_t i = 0; i < len; ++i) + case '-': { - get(); - if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) - { - return false; - } + add(current); + goto scan_number_minus; + } - if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) - { - return false; - } - key.clear(); + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } - else + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) { - while (get() != 0xFF) + case '0': { - 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(); + add(current); + goto scan_number_zero; } - } - return sax->end_object(); - } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - ///////////// - // MsgPack // - ///////////// + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } - /*! - @return whether a valid MessagePack value was passed to the SAX parser - */ - bool parse_msgpack_internal() - { +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) switch (get()) { - // EOF - case std::char_traits<char_type>::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<number_unsigned_t>(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<std::size_t>(static_cast<unsigned int>(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<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu)); + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } - // 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 + case 'e': + case 'E': { - string_t s; - return get_msgpack_string(s) and sax->string(s); + add(current); + goto scan_number_exponent; } - case 0xC0: // nil - return sax->null(); + default: + goto scan_number_done; + } - case 0xC2: // false - return sax->boolean(false); - - case 0xC3: // true - return sax->boolean(true); - - case 0xC4: // bin 8 - case 0xC5: // bin 16 - case 0xC6: // bin 32 - case 0xC7: // ext 8 - case 0xC8: // ext 16 - case 0xC9: // ext 32 - case 0xD4: // fixext 1 - case 0xD5: // fixext 2 - case 0xD6: // fixext 4 - case 0xD7: // fixext 8 - case 0xD8: // fixext 16 +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': { - binary_t b; - return get_msgpack_binary(b) and sax->binary(b); + add(current); + goto scan_number_any1; } - case 0xCA: // float 32 + case '.': { - float number; - return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), ""); + add(decimal_point_char); + goto scan_number_decimal1; } - case 0xCB: // float 64 + case 'e': + case 'E': { - double number; - return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), ""); + add(current); + goto scan_number_exponent; } - case 0xCC: // uint 8 - { - std::uint8_t number; - return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); - } + default: + goto scan_number_done; + } - case 0xCD: // uint 16 +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': { - std::uint16_t number; - return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + add(current); + goto scan_number_decimal2; } - case 0xCE: // uint 32 + default: { - std::uint32_t number; - return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; } + } - case 0xCF: // uint 64 +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': { - std::uint64_t number; - return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + add(current); + goto scan_number_decimal2; } - case 0xD0: // int 8 + case 'e': + case 'E': { - std::int8_t number; - return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + add(current); + goto scan_number_exponent; } - case 0xD1: // int 16 + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': { - std::int16_t number; - return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + add(current); + goto scan_number_sign; } - case 0xD2: // int 32 + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { - std::int32_t number; - return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + add(current); + goto scan_number_any2; } - case 0xD3: // int 64 + default: { - std::int64_t number; - return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; } + } - case 0xDC: // array 16 +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': { - std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len)); + add(current); + goto scan_number_any2; } - case 0xDD: // array 32 + default: { - std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len)); + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; } + } - case 0xDE: // map 16 +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': { - std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len)); + add(current); + goto scan_number_any2; } - case 0xDF: // map 32 + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) { - std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len)); + value_unsigned = static_cast<number_unsigned_t>(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); - // 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<std::int8_t>(current)); + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - default: // anything else + if (errno == 0) { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + value_integer = static_cast<number_integer_t>(x); + if (value_integer == x) + { + return token_type::value_integer; + } } } - } - /*! - @brief reads a MessagePack string + // 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); - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - @param[out] result created string + return token_type::value_float; + } - @return whether string creation completed + /*! + @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 */ - bool get_msgpack_string(string_t& result) + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) { - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) - { - return false; - } - - switch (current) + JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) { - // 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<unsigned int>(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: + if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i])) { - 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"))); + error_message = "invalid literal"; + return token_type::parse_error; } } + return return_type; } - /*! - @brief reads a MessagePack byte array + ///////////////////// + // input management + ///////////////////// - This function first reads starting bytes to determine the expected - byte array length and then copies this number of bytes into a byte array. + /// 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<char_type>::to_char_type(current)); + } - @param[out] result created byte array + /* + @brief get next character from the input - @return whether byte array creation completed + 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<char>::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input */ - bool get_msgpack_binary(binary_t& result) + char_int_type get() { - // helper function to set the subtype - auto assign_and_return_true = [&result](std::int8_t subtype) - { - result.set_subtype(static_cast<std::uint8_t>(subtype)); - return true; - }; + ++position.chars_read_total; + ++position.chars_read_current_line; - switch (current) + if (next_unget) { - case 0xC4: // bin 8 - { - std::uint8_t len; - return get_number(input_format_t::msgpack, len) and - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC5: // bin 16 - { - std::uint16_t len; - return get_number(input_format_t::msgpack, len) and - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC6: // bin 32 - { - std::uint32_t len; - return get_number(input_format_t::msgpack, len) and - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC7: // ext 8 - { - std::uint8_t len; - std::int8_t subtype; - return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, len, result) and - assign_and_return_true(subtype); - } - - case 0xC8: // ext 16 - { - std::uint16_t len; - std::int8_t subtype; - return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, len, result) and - assign_and_return_true(subtype); - } - - case 0xC9: // ext 32 - { - std::uint32_t len; - std::int8_t subtype; - return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, len, result) and - assign_and_return_true(subtype); - } - - case 0xD4: // fixext 1 - { - std::int8_t subtype; - return get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, 1, result) and - assign_and_return_true(subtype); - } - - case 0xD5: // fixext 2 - { - std::int8_t subtype; - return get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, 2, result) and - assign_and_return_true(subtype); - } - - case 0xD6: // fixext 4 - { - std::int8_t subtype; - return get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, 4, result) and - assign_and_return_true(subtype); - } - - case 0xD7: // fixext 8 - { - std::int8_t subtype; - return get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, 8, result) and - assign_and_return_true(subtype); - } - - case 0xD8: // fixext 16 - { - std::int8_t subtype; - return get_number(input_format_t::msgpack, subtype) and - get_binary(input_format_t::msgpack, 16, result) and - assign_and_return_true(subtype); - } - - default: // LCOV_EXCL_LINE - return false; // LCOV_EXCL_LINE + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); } - } - /*! - @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))) + if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) { - return false; + token_string.push_back(std::char_traits<char_type>::to_char_type(current)); } - for (std::size_t i = 0; i < len; ++i) + if (current == '\n') { - if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) - { - return false; - } + ++position.lines_read; + position.chars_read_current_line = 0; } - return sax->end_array(); + return current; } /*! - @param[in] len the length of the object - @return whether object creation completed + @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. */ - bool get_msgpack_object(const std::size_t len) + void unget() { - if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) - { - return false; - } + next_unget = true; - string_t key; - for (std::size_t i = 0; i < len; ++i) + --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) { - get(); - if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + if (position.lines_read > 0) { - return false; + --position.lines_read; } + } + else + { + --position.chars_read_current_line; + } - if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) - { - return false; - } - key.clear(); + if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); } + } - return sax->end_object(); + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast<typename string_t::value_type>(c)); } - //////////// - // UBJSON // - //////////// + public: + ///////////////////// + // value getters + ///////////////////// - /*! - @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 integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } - @return whether a valid UBJSON value was passed to the SAX parser - */ - bool parse_ubjson_internal(const bool get_char = true) + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept { - return get_ubjson_value(get_char ? get_ignore_noop() : current); + return value_unsigned; } - /*! - @brief reads a UBJSON string + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } - 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) + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() { - 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); - } + return token_buffer; + } - case 'L': - { - std::int64_t len; - return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); - } + ///////////////////// + // diagnostics + ///////////////////// - 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"))); - } + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; } - /*! - @param[out] result determined size - @return whether size determination completed - */ - bool get_ubjson_size_value(std::size_t& result) + /// 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 { - switch (get_ignore_noop()) + // escape control characters + std::string result; + for (const auto c : token_string) { - case 'U': - { - std::uint8_t number; - if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) - { - return false; - } - result = static_cast<std::size_t>(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<std::size_t>(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<std::size_t>(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<std::size_t>(number); - return true; - } - - case 'L': + if (static_cast<unsigned char>(c) <= '\x1F') { - std::int64_t number; - if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) - { - return false; - } - result = static_cast<std::size_t>(number); - return true; + // escape control characters + std::array<char, 9> cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c)); + result += cs.data(); } - - default: + else { - 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"))); + // add character as is + result.push_back(static_cast<std::string::value_type>(c)); } } - } - /*! - @brief determine the type and size for a container + return result; + } - In the optimized UBJSON format, a type and a size can be provided to allow - for a more compact representation. + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } - @param[out] result pair of the size and the type + ///////////////////// + // actual scanner + ///////////////////// - @return whether pair creation completed + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped */ - bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result) + bool skip_bom() { - 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 == '#') + if (get() == 0xEF) { - return get_ubjson_size_value(result.first); + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; } + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); return true; } - /*! - @param prefix the previously read or set type prefix - @return whether value creation completed - */ - bool get_ubjson_value(const char_int_type prefix) + void skip_whitespace() { - switch (prefix) + do { - case std::char_traits<char_type>::eof(): // EOF - return unexpect_eof(input_format_t::ubjson, "value"); + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } - case 'T': // true - return sax->boolean(true); - case 'F': // false - return sax->boolean(false); + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } - case 'Z': // null - return sax->null(); + // read next character and ignore whitespace + skip_whitespace(); - case 'U': + // ignore comments + if (ignore_comments && current == '/') + { + if (!scan_comment()) { - std::uint8_t number; - return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); + return token_type::parse_error; } - case 'i': - { - std::int8_t number; - return get_number(input_format_t::ubjson, number) and sax->number_integer(number); - } + // skip following whitespace + skip_whitespace(); + } - case 'I': - { - std::int16_t number; - return get_number(input_format_t::ubjson, number) and sax->number_integer(number); - } + 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; - case 'l': + // literals + case 't': { - std::int32_t number; - return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + std::array<char_type, 4> true_literal = {{'t', 'r', 'u', 'e'}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); } - - case 'L': + case 'f': { - std::int64_t number; - return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + std::array<char_type, 5> false_literal = {{'f', 'a', 'l', 's', 'e'}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); } - - case 'd': + case 'n': { - float number; - return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), ""); + std::array<char_type, 4> null_literal = {{'n', 'u', 'l', 'l'}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); } - case 'D': - { - double number; - return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), ""); - } + // string + case '\"': + return scan_string(); - 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<typename string_t::value_type>(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(); + // 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(); - case '{': // object - return get_ubjson_object(); + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits<char_type>::eof(): + return token_type::end_of_input; - 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"))); - } + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; } } - /*! - @return whether array creation completed - */ - bool get_ubjson_array() - { - std::pair<std::size_t, char_int_type> size_and_type; - if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) - { - return false; - } + private: + /// input adapter + InputAdapterType ia; - if (size_and_type.first != string_t::npos) - { - if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) - { - return false; - } + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = 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; - } + /// the current character + char_int_type current = std::char_traits<char_type>::eof(); - while (current != ']') - { - if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) - { - return false; - } - get_ignore_noop(); - } - } + /// whether the next get() call should just return current + bool next_unget = false; - return sax->end_array(); - } + /// the start position of the current token + position_t position {}; - /*! - @return whether object creation completed - */ - bool get_ubjson_object() - { - std::pair<std::size_t, char_int_type> size_and_type; - if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) - { - return false; - } + /// raw input token string (for error messages) + std::vector<char_type> token_string {}; - 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; - } + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; - 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; - } + /// a description of occurred lexer errors + const char* error_message = ""; - 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(); - } - } + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; - return sax->end_object(); - } + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann - // Note, no reader for UBJSON binary types is implemented because they do - // not exist +// #include <nlohmann/detail/macro_scope.hpp> - /////////////////////// - // Utility functions // - /////////////////////// +// #include <nlohmann/detail/meta/is_sax.hpp> - /*! - @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<char_type>::eof()` in that case. +#include <cstdint> // size_t +#include <utility> // declval +#include <string> // string - @return character read from the input - */ - char_int_type get() - { - ++chars_read; - return current = ia.get_character(); - } +// #include <nlohmann/detail/meta/detected.hpp> - /*! - @return character read from the input after ignoring all 'N' entries - */ - char_int_type get_ignore_noop() - { - do - { - get(); - } - while (current == 'N'); +// #include <nlohmann/detail/meta/type_traits.hpp> - return current; - } - /* - @brief read a number from the input +namespace nlohmann +{ +namespace detail +{ +template<typename T> +using null_function_t = decltype(std::declval<T&>().null()); - @tparam NumberType the type of the number - @param[in] format the current format (for diagnostics) - @param[out] result number of type @a NumberType +template<typename T> +using boolean_function_t = + decltype(std::declval<T&>().boolean(std::declval<bool>())); - @return whether conversion completed +template<typename T, typename Integer> +using number_integer_function_t = + decltype(std::declval<T&>().number_integer(std::declval<Integer>())); - @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<typename NumberType, bool InputIsLittleEndian = false> - bool get_number(const input_format_t format, NumberType& result) - { - // step 1: read input into array with system's byte order - std::array<std::uint8_t, sizeof(NumberType)> vec; - for (std::size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) - { - return false; - } +template<typename T, typename Unsigned> +using number_unsigned_function_t = + decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>())); - // reverse byte order prior to conversion if necessary - if (is_little_endian != InputIsLittleEndian) - { - vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current); - } - else - { - vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE - } - } +template<typename T, typename Float, typename String> +using number_float_function_t = decltype(std::declval<T&>().number_float( + std::declval<Float>(), std::declval<const String&>())); - // step 2: convert array into number of type T and return - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return true; - } +template<typename T, typename String> +using string_function_t = + decltype(std::declval<T&>().string(std::declval<String&>())); - /*! - @brief create a string by reading characters from the input +template<typename T, typename Binary> +using binary_function_t = + decltype(std::declval<T&>().binary(std::declval<Binary&>())); - @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 +template<typename T> +using start_object_function_t = + decltype(std::declval<T&>().start_object(std::declval<std::size_t>())); - @return whether string creation completed +template<typename T, typename String> +using key_function_t = + decltype(std::declval<T&>().key(std::declval<String&>())); - @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<typename NumberType> - 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 std::char_traits<char_type>::to_char_type(current); - }); - return success; - } +template<typename T> +using end_object_function_t = decltype(std::declval<T&>().end_object()); - /*! - @brief create a byte array by reading bytes from the input +template<typename T> +using start_array_function_t = + decltype(std::declval<T&>().start_array(std::declval<std::size_t>())); - @tparam NumberType the type of the number - @param[in] format the current format (for diagnostics) - @param[in] len number of bytes to read - @param[out] result byte array created by reading @a len bytes +template<typename T> +using end_array_function_t = decltype(std::declval<T&>().end_array()); - @return whether byte array creation completed +template<typename T, typename Exception> +using parse_error_function_t = decltype(std::declval<T&>().parse_error( + std::declval<std::size_t>(), std::declval<const std::string&>(), + std::declval<const Exception&>())); - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref unexpect_eof() detects the end of - the input before we run out of memory. - */ - template<typename NumberType> - bool get_binary(const input_format_t format, - const NumberType len, - binary_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, "binary"))) - { - success = false; - } - return static_cast<std::uint8_t>(current); - }); - return success; - } +template<typename SAX, typename BasicJsonType> +struct is_sax +{ + private: + static_assert(is_basic_json<BasicJsonType>::value, + "BasicJsonType must be of type basic_json<...>"); - /*! - @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<char_type>::eof())) - { - return sax->parse_error(chars_read, "<end of file>", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); - } - return true; - } + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact<bool, null_function_t, SAX>::value && + is_detected_exact<bool, boolean_function_t, SAX>::value && + is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value && + is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value && + is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value && + is_detected_exact<bool, string_function_t, SAX, string_t>::value && + is_detected_exact<bool, binary_function_t, SAX, binary_t>::value && + is_detected_exact<bool, start_object_function_t, SAX>::value && + is_detected_exact<bool, key_function_t, SAX, string_t>::value && + is_detected_exact<bool, end_object_function_t, SAX>::value && + is_detected_exact<bool, start_array_function_t, SAX>::value && + is_detected_exact<bool, end_array_function_t, SAX>::value && + is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value; +}; + +template<typename SAX, typename BasicJsonType> +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json<BasicJsonType>::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact<bool, null_function_t, SAX>::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact<bool, number_integer_function_t, SAX, + number_integer_t>::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact<bool, number_unsigned_function_t, SAX, + number_unsigned_t>::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact<bool, number_float_function_t, SAX, + number_float_t, string_t>::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact<bool, string_function_t, SAX, string_t>::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact<bool, binary_function_t, SAX, binary_t>::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/value_t.hpp> + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore ///< ignore tags +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast<char*>(&num) == 1; +} + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits<char_type>::int_type; + + public: /*! - @return a string representation of the last read byte + @brief create a binary reader + + @param[in] adapter input adapter to read from */ - std::string get_token_string() const + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) { - std::array<char, 3> cr{{}}; - (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current)); - return std::string{cr.data()}; + (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; } + // 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 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 + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return */ - std::string exception_message(const input_format_t format, - const std::string& detail, - const std::string& context) const + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - std::string error_msg = "syntax error while parsing "; + sax = sax_; + bool result = false; switch (format) { + case input_format_t::bson: + result = parse_bson_internal(); + break; + case input_format_t::cbor: - error_msg += "CBOR"; + result = parse_cbor_internal(true, tag_handler); break; case input_format_t::msgpack: - error_msg += "MessagePack"; + result = parse_msgpack_internal(); break; case input_format_t::ubjson: - error_msg += "UBJSON"; - break; - - case input_format_t::bson: - error_msg += "BSON"; + result = parse_ubjson_internal(); break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } - return error_msg + " " + context + ": " + detail; - } - - private: - /// input adapter - InputAdapterType ia; - - /// the current character - char_int_type current = std::char_traits<char_type>::eof(); - - /// the number of characters read - std::size_t chars_read = 0; - - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); - - /// the SAX parser - json_sax_t* sax = nullptr; -}; -} // namespace detail -} // namespace nlohmann - -// #include <nlohmann/detail/input/input_adapters.hpp> - -// #include <nlohmann/detail/input/lexer.hpp> - - -#include <array> // array -#include <clocale> // localeconv -#include <cstddef> // size_t -#include <cstdio> // snprintf -#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull -#include <initializer_list> // initializer_list -#include <string> // char_traits, string -#include <utility> // move -#include <vector> // vector - -// #include <nlohmann/detail/input/input_adapters.hpp> - -// #include <nlohmann/detail/input/position_t.hpp> + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } -// #include <nlohmann/detail/macro_scope.hpp> + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::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; + } -namespace nlohmann -{ -namespace detail -{ -/////////// -// lexer // -/////////// + private: + ////////// + // BSON // + ////////// -template<typename BasicJsonType> -class lexer_base -{ - public: - /// token types for the parser - enum class token_type + /*! + @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() { - 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) - }; + std::int32_t document_size{}; + get_number<std::int32_t, true>(input_format_t::bson, document_size); - /// 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) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) { - case token_type::uninitialized: - return "<uninitialized>"; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_unsigned: - case token_type::value_integer: - case token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return "<parse error>"; - 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 + return false; } - } -}; -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ -template<typename BasicJsonType, typename InputAdapterType> -class lexer : public lexer_base<BasicJsonType> -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using char_type = typename InputAdapterType::char_type; - using char_int_type = typename std::char_traits<char_type>::int_type; - - public: - using token_type = typename lexer_base<BasicJsonType>::token_type; - - explicit lexer(InputAdapterType&& adapter) - : ia(std::move(adapter)), decimal_point_char(static_cast<char_int_type>(get_decimal_point())) {} - // delete because of pointer members - lexer(const lexer&) = delete; - lexer(lexer&&) = default; - lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = default; - ~lexer() = default; - - private: - ///////////////////// - // locales - ///////////////////// + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } - /// 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); + return sax->end_object(); } - ///////////////////// - // 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) + @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. */ - int get_codepoint() + bool get_bson_cstr(string_t& result) { - // 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) + auto out = std::back_inserter(result); + while (true) { get(); - - if (current >= '0' and current <= '9') - { - codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor); - } - else if (current >= 'A' and current <= 'F') - { - codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor); - } - else if (current >= 'a' and current <= 'f') + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) { - codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor); + return false; } - else + if (current == 0x00) { - return -1; + return true; } + *out++ = static_cast<typename string_t::value_type>(current); } - - assert(0x0000 <= codepoint and codepoint <= 0xFFFF); - return codepoint; } /*! - @brief check if the next byte(s) are inside a given range + @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<typename NumberType> + 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"))); + } - 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. + return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof(); + } - @param[in] ranges list of integers; interpreted as list of pairs of - inclusive lower and upper bound, respectively + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in, out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template<typename NumberType> + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + } - @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. + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number<std::uint8_t>(input_format_t::bson, subtype); + result.set_subtype(subtype); - @return true if and only if no range violation was detected + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser */ - bool next_byte_in_range(std::initializer_list<char_int_type> ranges) + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) { - assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); - add(current); - - for (auto range = ranges.begin(); range != ranges.end(); ++range) + switch (element_type) { - get(); - if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) + case 0x01: // double { - add(current); + double number{}; + return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), ""); } - else + + case 0x02: // string { - error_message = "invalid string: ill-formed UTF-8 byte"; - return false; + std::int32_t len{}; + string_t value; + return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); } - } - return true; + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array<char, 3> cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(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 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. + @brief Read a BSON element list (as specified in the BSON-spec) - @return token_type::value_string if string could be successfully scanned, - token_type::parse_error otherwise + 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). - @note In case of errors, variable error_message contains a textual - description. + @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 */ - token_type scan_string() + bool parse_bson_element_list(const bool is_array) { - // reset token_buffer (ignore opening quote) - reset(); - - // we entered the function by reading an open quote - assert(current == '\"'); + string_t key; - while (true) + while (auto element_type = get()) { - // get next character - switch (get()) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) { - // end of file while parsing string - case std::char_traits<char_type>::eof(): - { - error_message = "invalid string: missing closing quote"; - return token_type::parse_error; - } - - // closing quote - case '\"': - { - return token_type::value_string; - } + return false; + } - // 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; + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } - // unicode escapes - case 'u': - { - const int codepoint1 = get_codepoint(); - int codepoint = codepoint1; // start with codepoint1 + if (!is_array && !sax->key(key)) + { + return false; + } - if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } - // 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(); + // get_bson_cstr only appends + key.clear(); + } - if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + return true; + } - // check if codepoint2 is a low surrogate - if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) - { - // overwrite codepoint - codepoint = static_cast<int>( - // high surrogate occupies the most significant 22 bits - (static_cast<unsigned int>(codepoint1) << 10u) - // low surrogate occupies the least significant 15 bits - + static_cast<unsigned int>(codepoint2) - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00u); - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; - return token_type::parse_error; - } - } + /*! + @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<std::int32_t, true>(input_format_t::bson, document_size); - // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } - // translate codepoint into bytes - if (codepoint < 0x80) + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits<char_type>::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<number_unsigned_t>(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) + - static_cast<number_integer_t>(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + + case cbor_tag_handler_t::ignore: + { + switch (current) + { + case 0xD8: { - // 1-byte characters: 0xxxxxxx (ASCII) - add(static_cast<char_int_type>(codepoint)); + std::uint8_t len{}; + get_number(input_format_t::cbor, len); + break; } - else if (codepoint <= 0x7FF) + case 0xD9: { - // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u))); - add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + std::uint16_t len{}; + get_number(input_format_t::cbor, len); + break; } - else if (codepoint <= 0xFFFF) + case 0xDA: { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u))); - add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); - add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + std::uint32_t len{}; + get_number(input_format_t::cbor, len); + break; } - else + case 0xDB: { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u))); - add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu))); - add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); - add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); + std::uint64_t len{}; + get_number(input_format_t::cbor, len); + break; } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast<unsigned char>(byte1_raw); + const auto byte2 = static_cast<unsigned char>(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<unsigned int>((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits<double>::infinity() + : std::numeric_limits<double>::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast<number_float_t>(-val) + : static_cast<number_float_t>(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } - break; - } + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits<char_type>::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<number_unsigned_t>(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<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu)); - // other characters after escape - default: - error_message = "invalid string: forbidden character after backslash"; - return token_type::parse_error; - } + // 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<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu)); - break; - } + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } - // 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 0xC0: // nil + return sax->null(); - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } + case 0xC2: // false + return sax->boolean(false); - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } + case 0xC3: // true + return sax->boolean(true); - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len)); + } - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len)); + } - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len)); + } - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len)); + } - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } + // 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<std::int8_t>(current)); - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } + 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"))); + } + } + } - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } + /*! + @brief reads a MessagePack string - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } + @param[out] result created string - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } + 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<unsigned int>(current) & 0x1Fu, result); + } - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } + 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"))); + } + } + } - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } + /*! + @brief reads a MessagePack byte array - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. - case 0x1F: - { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; - return token_type::parse_error; - } + @param[out] result created byte array - // 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; - } + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast<std::uint8_t>(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - // 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; - } + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - // 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; - } + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - // 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; - } + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - // 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; - } + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - // 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; - } + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - // 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; - } + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } - // 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; - } + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } - // 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; - } + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE } } - JSON_HEDLEY_NON_NULL(2) - static void strtof(float& f, const char* str, char** endptr) noexcept + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) { - f = std::strtof(str, endptr); - } + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } - JSON_HEDLEY_NON_NULL(2) - static void strtof(double& f, const char* str, char** endptr) noexcept - { - f = std::strtod(str, endptr); - } + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } - JSON_HEDLEY_NON_NULL(2) - static void strtof(long double& f, const char* str, char** endptr) noexcept - { - f = std::strtold(str, endptr); + return sax->end_array(); } /*! - @brief scan a number literal + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } - This function scans a string according to Sect. 6 of RFC 7159. + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } - 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. + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } - state | 0 | 1-9 | e E | + | - | . | anything - ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | [error] - zero | done | done | exponent | done | done | decimal1 | done - any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | done - exponent | any2 | any2 | [error] | sign | sign | [error] | [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] - any2 | any2 | any2 | done | done | done | done | done + return sax->end_object(); + } - 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. + //////////// + // UBJSON // + //////////// - 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. + /*! + @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 token_type::value_unsigned, token_type::value_integer, or - token_type::value_float if number could be successfully scanned, - token_type::parse_error otherwise + @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 - @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. + @return whether string creation completed */ - token_type scan_number() // lgtm [cpp/use-of-goto] + bool get_ubjson_string(string_t& result, const bool get_char = true) { - // reset token_buffer to store the number's bytes - reset(); + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } - // 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; + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } - // state (init): we just found out we need to scan a number switch (current) { - case '-': + case 'U': { - add(current); - goto scan_number_minus; + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } - case '0': + case 'i': { - add(current); - goto scan_number_zero; + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case 'I': { - add(current); - goto scan_number_any1; + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } - // 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': + case 'l': { - add(current); - goto scan_number_zero; + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case 'L': { - add(current); - goto scan_number_any1; + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } default: - { - error_message = "invalid number; expected digit after '-'"; - return token_type::parse_error; - } + 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"))); } + } -scan_number_zero: - // state: we just parse a zero (maybe with a leading minus sign) - switch (get()) + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) { - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } - - case 'e': - case 'E': + case 'U': { - add(current); - goto scan_number_exponent; + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast<std::size_t>(number); + return true; } - 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': + case 'i': { - add(current); - goto scan_number_any1; + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast<std::size_t>(number); + return true; } - case '.': + case 'I': { - add(decimal_point_char); - goto scan_number_decimal1; + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast<std::size_t>(number); + return true; } - case 'e': - case 'E': + case 'l': { - add(current); - goto scan_number_exponent; + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast<std::size_t>(number); + return true; } - 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': + case 'L': { - add(current); - goto scan_number_decimal2; + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast<std::size_t>(number); + return true; } default: { - error_message = "invalid number; expected digit after '.'"; - return token_type::parse_error; + 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"))); } } + } -scan_number_decimal2: - // we just parsed at least one number after a decimal point - switch (get()) + /*! + @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<std::size_t, char_int_type>& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) { - add(current); - goto scan_number_decimal2; + return false; } - case 'e': - case 'E': + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) { - add(current); - goto scan_number_exponent; + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); } - default: - goto scan_number_done; + return get_ubjson_size_value(result.first); } -scan_number_exponent: - // we just parsed an exponent - number_type = token_type::value_float; - switch (get()) + if (current == '#') { - case '+': - case '-': + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits<char_type>::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': { - add(current); - goto scan_number_sign; + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case 'i': { - add(current); - goto scan_number_any2; + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } - default: + case 'I': { - error_message = - "invalid number; expected '+', '-', or digit after exponent"; - return token_type::parse_error; + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } - } -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': + case 'l': { - add(current); - goto scan_number_any2; + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } - default: + case 'L': { - error_message = "invalid number; expected digit after exponent sign"; - return token_type::parse_error; + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } - } -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': + case 'd': { - add(current); - goto scan_number_any2; + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), ""); } - 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); + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } - // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + case 'H': + { + return get_ubjson_high_precision_number(); + } - if (errno == 0) + case 'C': // char { - value_unsigned = static_cast<number_unsigned_t>(x); - if (value_unsigned == x) + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) { - return token_type::value_unsigned; + 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<typename string_t::value_type>(current)); + return sax->string(s); } - } - 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) + case 'S': // string { - value_integer = static_cast<number_integer_t>(x); - if (value_integer == x) - { - return token_type::value_integer; - } + string_t s; + return get_ubjson_string(s) && sax->string(s); } - } - // 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); + case '[': // array + return get_ubjson_array(); - // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + case '{': // object + return get_ubjson_object(); - return token_type::value_float; + 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"))); + } + } } /*! - @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 + @return whether array creation completed */ - JSON_HEDLEY_NON_NULL(2) - token_type scan_literal(const char_type* literal_text, const std::size_t length, - token_type return_type) + bool get_ubjson_array() { - assert(std::char_traits<char_type>::to_char_type(current) == literal_text[0]); - for (std::size_t i = 1; i < length; ++i) + std::pair<std::size_t, char_int_type> size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { - if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i])) + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) { - error_message = "invalid literal"; - return token_type::parse_error; + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } } } - return return_type; - } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } - ///////////////////// - // input management - ///////////////////// + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } - /// 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<char_type>::to_char_type(current)); + return sax->end_array(); } - /* - @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<char>::eof()` in that case. Stores the scanned characters - for use in error messages. - - @return character read from the input + /*! + @return whether object creation completed */ - char_int_type get() + bool get_ubjson_object() { - ++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 + std::pair<std::size_t, char_int_type> size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { - current = ia.get_character(); + return false; } - if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) + string_t key; + if (size_and_type.first != string_t::npos) { - token_string.push_back(std::char_traits<char_type>::to_char_type(current)); - } + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } - if (current == '\n') + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else { - ++position.lines_read; - position.chars_read_current_line = 0; + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } } - return current; + return sax->end_object(); } - /*! - @brief unget current character (read it again on next get) + // Note, no reader for UBJSON binary types is implemented because they do + // not exist - 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() + bool get_ubjson_high_precision_number() { - next_unget = true; - - --position.chars_read_total; + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } - // in case we "unget" a newline, we have to also decrement the lines_read - if (position.chars_read_current_line == 0) + // get number string + std::vector<char> number_vector; + for (std::size_t i = 0; i < size; ++i) { - if (position.lines_read > 0) + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) { - --position.lines_read; + return false; } + number_vector.push_back(static_cast<char>(current)); } - else + + // parse number string + auto number_ia = detail::input_adapter(std::forward<decltype(number_vector)>(number_vector)); + auto number_lexer = detail::lexer<BasicJsonType, decltype(number_ia)>(std::move(number_ia), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base<BasicJsonType>::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - --position.chars_read_current_line; + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); } - if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) + switch (result_number) { - assert(not token_string.empty()); - token_string.pop_back(); + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); } } - /// add a character to token_buffer - void add(char_int_type c) - { - token_buffer.push_back(static_cast<typename string_t::value_type>(c)); - } + /////////////////////// + // Utility functions // + /////////////////////// - public: - ///////////////////// - // value getters - ///////////////////// + /*! + @brief get next character from the input - /// return integer value - constexpr number_integer_t get_number_integer() const noexcept - { - return value_integer; - } + 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<char_type>::eof()` in that case. - /// return unsigned integer value - constexpr number_unsigned_t get_number_unsigned() const noexcept + @return character read from the input + */ + char_int_type get() { - return value_unsigned; + ++chars_read; + return current = ia.get_character(); } - /// return floating-point value - constexpr number_float_t get_number_float() const noexcept + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() { - return value_float; - } + do + { + get(); + } + while (current == 'N'); - /// return current string value (implicitly resets the token; useful only once) - string_t& get_string() - { - return token_buffer; + return current; } - ///////////////////// - // diagnostics - ///////////////////// + /* + @brief read a number from the input - /// return position of last read token - constexpr position_t get_position() const noexcept - { - return position; - } + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType - /// 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 + @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<typename NumberType, bool InputIsLittleEndian = false> + bool get_number(const input_format_t format, NumberType& result) { - // escape control characters - std::string result; - for (const auto c : token_string) + // step 1: read input into array with system's byte order + std::array<std::uint8_t, sizeof(NumberType)> vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) { - if (static_cast<unsigned char>(c) <= '\x1F') + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) { - // escape control characters - std::array<char, 9> cs{{}}; - (std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c)); - result += cs.data(); + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current); } else { - // add character as is - result.push_back(static_cast<std::string::value_type>(c)); + vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE } } - return result; + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; } - /// return syntax error message - JSON_HEDLEY_RETURNS_NON_NULL - constexpr const char* get_error_message() const noexcept + /*! + @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<typename NumberType> + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) { - return error_message; + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast<typename string_t::value_type>(current)); + }; + return success; } - ///////////////////// - // actual scanner - ///////////////////// - /*! - @brief skip the UTF-8 byte order mark - @return true iff there is no BOM or the correct BOM has been skipped + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. */ - bool skip_bom() + template<typename NumberType> + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) { - if (get() == 0xEF) + bool success = true; + for (NumberType i = 0; i < len; i++) { - // check if we completely parse the BOM - return get() == 0xBB and get() == 0xBF; + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast<std::uint8_t>(current)); } - - // the first character is not the beginning of the BOM; unget it to - // process is later - unget(); - return true; + return success; } - token_type scan() + /*! + @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 { - // initially, skip the BOM - if (position.chars_read_total == 0 and not skip_bom()) + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof())) { - error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; - return token_type::parse_error; + return sax->parse_error(chars_read, "<end of file>", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); } + return true; + } - // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array<char, 3> cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(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 (current) + switch (format) { - // structural characters - case '[': - return token_type::begin_array; - case ']': - return token_type::end_array; - case '{': - return token_type::begin_object; - case '}': - return token_type::end_object; - case ':': - return token_type::name_separator; - case ',': - return token_type::value_separator; - - // literals - case 't': - { - std::array<char_type, 4> true_literal = {{'t', 'r', 'u', 'e'}}; - return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); - } - case 'f': - { - std::array<char_type, 5> false_literal = {{'f', 'a', 'l', 's', 'e'}}; - return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); - } - case 'n': - { - std::array<char_type, 4> null_literal = {{'n', 'u', 'l', 'l'}}; - return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); - } + case input_format_t::cbor: + error_msg += "CBOR"; + break; - // string - case '\"': - return scan_string(); + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; - // 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(); + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; - // end of input (the null byte is needed when parsing from - // string literals) - case '\0': - case std::char_traits<char_type>::eof(): - return token_type::end_of_input; + case input_format_t::bson: + error_msg += "BSON"; + break; - // error - default: - error_message = "invalid literal"; - return token_type::parse_error; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } + + return error_msg + " " + context + ": " + detail; } private: @@ -9569,36 +10085,25 @@ scan_number_done: /// the current character char_int_type current = std::char_traits<char_type>::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<char_type> token_string {}; - - /// buffer for variable-length tokens (numbers, strings) - string_t token_buffer {}; - - /// a description of occurred lexer errors - const char* error_message = ""; + /// the number of characters read + std::size_t chars_read = 0; - // number values - number_integer_t value_integer = 0; - number_unsigned_t value_unsigned = 0; - number_float_t value_float = 0; + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); - /// the decimal point - const char_int_type decimal_point_char = '.'; + /// the SAX parser + json_sax_t* sax = nullptr; }; } // namespace detail } // namespace nlohmann +// #include <nlohmann/detail/input/input_adapters.hpp> + +// #include <nlohmann/detail/input/lexer.hpp> + // #include <nlohmann/detail/input/parser.hpp> -#include <cassert> // assert #include <cmath> // isfinite #include <cstdint> // uint8_t #include <functional> // function @@ -9668,8 +10173,11 @@ class parser /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t<BasicJsonType> cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { // read first token get_token(); @@ -9694,7 +10202,7 @@ class parser result.assert_invariant(); // in strict mode, input must be completely read - if (strict and (get_token() != token_type::end_of_input)) + if (strict && (get_token() != token_type::end_of_input)) { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), @@ -9723,7 +10231,7 @@ class parser result.assert_invariant(); // in strict mode, input must be completely read - if (strict and (get_token() != token_type::end_of_input)) + if (strict && (get_token() != token_type::end_of_input)) { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), @@ -9752,7 +10260,7 @@ class parser return sax_parse(&sax_acceptor, strict); } - template <typename SAX> + template<typename SAX> JSON_HEDLEY_NON_NULL(2) bool sax_parse(SAX* sax, const bool strict = true) { @@ -9760,7 +10268,7 @@ class parser 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)) + if (result && strict && (get_token() != token_type::end_of_input)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), @@ -9772,7 +10280,7 @@ class parser } private: - template <typename SAX> + template<typename SAX> JSON_HEDLEY_NON_NULL(2) bool sax_parse_internal(SAX* sax) { @@ -9784,14 +10292,14 @@ class parser while (true) { - if (not skip_to_state_evaluation) + if (!skip_to_state_evaluation) { // invariant: get_token() was called before each iteration switch (last_token) { case token_type::begin_object: { - if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) { return false; } @@ -9799,7 +10307,7 @@ class parser // closing } -> we are done if (get_token() == token_type::end_object) { - if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) { return false; } @@ -9814,7 +10322,7 @@ class parser 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()))) + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { return false; } @@ -9838,7 +10346,7 @@ class parser case token_type::begin_array: { - if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) { return false; } @@ -9846,7 +10354,7 @@ class parser // closing ] -> we are done if (get_token() == token_type::end_array) { - if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) { return false; } @@ -9864,14 +10372,14 @@ class parser { const auto res = m_lexer.get_number_float(); - if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); } - if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) { return false; } @@ -9881,7 +10389,7 @@ class parser case token_type::literal_false: { - if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) { return false; } @@ -9890,7 +10398,7 @@ class parser case token_type::literal_null: { - if (JSON_HEDLEY_UNLIKELY(not sax->null())) + if (JSON_HEDLEY_UNLIKELY(!sax->null())) { return false; } @@ -9899,7 +10407,7 @@ class parser case token_type::literal_true: { - if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) { return false; } @@ -9908,7 +10416,7 @@ class parser case token_type::value_integer: { - if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) { return false; } @@ -9917,7 +10425,7 @@ class parser case token_type::value_string: { - if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) { return false; } @@ -9926,7 +10434,7 @@ class parser case token_type::value_unsigned: { - if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) { return false; } @@ -9976,7 +10484,7 @@ class parser // closing ] if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) { - if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) { return false; } @@ -9985,7 +10493,7 @@ class parser // 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()); + JSON_ASSERT(!states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; @@ -10010,7 +10518,7 @@ class parser exception_message(token_type::value_string, "object key"))); } - if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { return false; } @@ -10032,7 +10540,7 @@ class parser // closing } if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) { - if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) { return false; } @@ -10041,7 +10549,7 @@ class parser // 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()); + JSON_ASSERT(!states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; @@ -10065,7 +10573,7 @@ class parser { std::string error_msg = "syntax error "; - if (not context.empty()) + if (!context.empty()) { error_msg += "while parsing " + context + " "; } @@ -10245,8 +10753,6 @@ template<typename BasicJsonType> struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; - /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -10259,8 +10765,6 @@ template<typename BasicJsonType> struct internal_iterator #include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include <type_traits> // conditional, is_const, remove_const -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/exceptions.hpp> // #include <nlohmann/detail/iterators/internal_iterator.hpp> @@ -10349,7 +10853,7 @@ class iter_impl */ explicit iter_impl(pointer object) noexcept : m_object(object) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10435,7 +10939,7 @@ class iter_impl */ void set_begin() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10472,7 +10976,7 @@ class iter_impl */ void set_end() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10503,19 +11007,19 @@ class iter_impl */ reference operator*() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_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()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -10540,19 +11044,19 @@ class iter_impl */ pointer operator->() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_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()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -10585,7 +11089,7 @@ class iter_impl */ iter_impl& operator++() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10628,7 +11132,7 @@ class iter_impl */ iter_impl& operator--() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10666,7 +11170,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10687,7 +11191,7 @@ class iter_impl */ bool operator!=(const iter_impl& other) const { - return not operator==(other); + return !operator==(other); } /*! @@ -10702,7 +11206,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10723,7 +11227,7 @@ class iter_impl */ bool operator<=(const iter_impl& other) const { - return not other.operator < (*this); + return !other.operator < (*this); } /*! @@ -10732,7 +11236,7 @@ class iter_impl */ bool operator>(const iter_impl& other) const { - return not operator<=(other); + return !operator<=(other); } /*! @@ -10741,7 +11245,7 @@ class iter_impl */ bool operator>=(const iter_impl& other) const { - return not operator<(other); + return !operator<(other); } /*! @@ -10750,7 +11254,7 @@ class iter_impl */ iter_impl& operator+=(difference_type i) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10821,7 +11325,7 @@ class iter_impl */ difference_type operator-(const iter_impl& other) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10842,7 +11346,7 @@ class iter_impl */ reference operator[](difference_type n) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10873,7 +11377,7 @@ class iter_impl */ const typename object_t::key_type& key() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); if (JSON_HEDLEY_LIKELY(m_object->is_object())) { @@ -11030,8 +11534,8 @@ class json_reverse_iterator : public std::reverse_iterator<Base> #include <algorithm> // all_of -#include <cassert> // assert #include <cctype> // isdigit +#include <limits> // max #include <numeric> // accumulate #include <string> // string #include <utility> // move @@ -11357,12 +11861,17 @@ class json_pointer @return integer representation of @a s + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type */ - static int array_index(const std::string& s) + static typename BasicJsonType::size_type array_index(const std::string& s) { + using size_type = typename BasicJsonType::size_type; + // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + @@ -11370,16 +11879,16 @@ class json_pointer } // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9'))) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); } std::size_t processed_chars = 0; - int res = 0; + unsigned long long res = 0; JSON_TRY { - res = std::stoi(s, &processed_chars); + res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { @@ -11392,7 +11901,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - return res; + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast<size_type>(res); } json_pointer top() const @@ -11417,7 +11933,6 @@ class json_pointer */ 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 @@ -11451,7 +11966,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](static_cast<size_type>(array_index(reference_token))); + result = &result->operator[](array_index(reference_token)); break; } @@ -11490,7 +12005,6 @@ class json_pointer */ 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 @@ -11505,7 +12019,7 @@ class json_pointer }); // change value to array for numbers or "-" or to object otherwise - *ptr = (nums or reference_token == "-") + *ptr = (nums || reference_token == "-") ? detail::value_t::array : detail::value_t::object; } @@ -11529,8 +12043,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[]( - static_cast<size_type>(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -11551,7 +12064,6 @@ class json_pointer */ BasicJsonType& get_checked(BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -11574,7 +12086,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11601,7 +12113,6 @@ class json_pointer */ 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()) @@ -11624,8 +12135,7 @@ class json_pointer } // use unchecked array access - ptr = &ptr->operator[]( - static_cast<size_type>(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -11645,7 +12155,6 @@ class json_pointer */ 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()) @@ -11668,7 +12177,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11686,14 +12195,13 @@ class json_pointer */ 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)) + if (!ptr->contains(reference_token)) { // we did not find the key in the object return false; @@ -11710,21 +12218,21 @@ class json_pointer // "-" always fails the range check return false; } - if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 and not ("0" <= reference_token and reference_token <= "9"))) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) { // invalid char return false; } if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) { - if (JSON_HEDLEY_UNLIKELY(not ('1' <= reference_token[0] and reference_token[0] <= '9'))) + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) { // first char should be between '1' and '9' return false; } for (std::size_t i = 1; i < reference_token.size(); i++) { - if (JSON_HEDLEY_UNLIKELY(not ('0' <= reference_token[i] and reference_token[i] <= '9'))) + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) { // other char should be between '0' and '9' return false; @@ -11732,7 +12240,7 @@ class json_pointer } } - const auto idx = static_cast<size_type>(array_index(reference_token)); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range @@ -11808,11 +12316,11 @@ class json_pointer pos != std::string::npos; pos = reference_token.find_first_of('~', pos + 1)) { - assert(reference_token[pos] == '~'); + JSON_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 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); @@ -11843,7 +12351,7 @@ class json_pointer static void replace_substring(std::string& s, const std::string& f, const std::string& t) { - assert(not f.empty()); + JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and @@ -11938,7 +12446,7 @@ class json_pointer static BasicJsonType unflatten(const BasicJsonType& value) { - if (JSON_HEDLEY_UNLIKELY(not value.is_object())) + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); } @@ -11948,7 +12456,7 @@ class json_pointer // iterate the JSON object values for (const auto& element : *value.m_value.object) { - if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); } @@ -11994,7 +12502,7 @@ class json_pointer friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept { - return not (lhs == rhs); + return !(lhs == rhs); } /// the reference tokens @@ -12022,23 +12530,30 @@ class json_ref using value_type = BasicJsonType; json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast<value_type*>(&value)), is_rvalue(false) + : value_ref(const_cast<value_type*>(&value)) + , is_rvalue(false) {} json_ref(std::initializer_list<json_ref> init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) {} template < class... Args, enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 > json_ref(Args && ... args) - : owned_value(std::forward<Args>(args)...), value_ref(&owned_value), - is_rvalue(true) {} + : owned_value(std::forward<Args>(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} // class should be movable only json_ref(json_ref&&) = default; @@ -12069,7 +12584,7 @@ class json_ref private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; - const bool is_rvalue; + const bool is_rvalue = true; }; } // namespace detail } // namespace nlohmann @@ -12238,6 +12753,7 @@ class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; public: /*! @@ -12247,7 +12763,7 @@ class binary_writer */ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter) { - assert(oa); + JSON_ASSERT(oa); } /*! @@ -12404,18 +12920,7 @@ class binary_writer } else { - if (static_cast<double>(j.m_value.number_float) >= static_cast<double>(std::numeric_limits<float>::lowest()) and - static_cast<double>(j.m_value.number_float) <= static_cast<double>((std::numeric_limits<float>::max)()) and - static_cast<double>(static_cast<float>(j.m_value.number_float)) == static_cast<double>(j.m_value.number_float)) - { - oa->write_character(get_cbor_float_prefix(static_cast<float>(j.m_value.number_float))); - write_number(static_cast<float>(j.m_value.number_float)); - } - else - { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -12499,6 +13004,12 @@ class binary_writer case value_t::binary: { + if (j.m_value.binary->has_subtype()) + { + write_number(static_cast<std::uint8_t>(0xd8)); + write_number(j.m_value.binary->subtype()); + } + // step 1: write control byte and the binary array size const auto N = j.m_value.binary->size(); if (N <= 0x17) @@ -12646,28 +13157,28 @@ class binary_writer // negative fixnum write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() and + else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { // int 8 oa->write_character(to_char_type(0xD0)); write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() and + else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { // int 16 oa->write_character(to_char_type(0xD1)); write_number(static_cast<std::int16_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() and + else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { // int 32 oa->write_character(to_char_type(0xD2)); write_number(static_cast<std::int32_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() and + else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) { // int 64 @@ -12714,8 +13225,7 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -12794,7 +13304,7 @@ class binary_writer const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits<std::uint8_t>::max)()) { - std::uint8_t output_type; + std::uint8_t output_type{}; bool fixed = true; if (use_ext) { @@ -12829,37 +13339,25 @@ class binary_writer } oa->write_character(to_char_type(output_type)); - if (not fixed) + if (!fixed) { write_number(static_cast<std::uint8_t>(N)); } } else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC8; // ext 16 - } - else - { - output_type = 0xC5; // bin 16 - } + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast<std::uint16_t>(N)); } else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC9; // ext 32 - } - else - { - output_type = 0xC6; // bin 32 - } + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast<std::uint32_t>(N)); @@ -12985,9 +13483,9 @@ class binary_writer } bool prefix_required = true; - if (use_type and not j.m_value.array->empty()) + if (use_type && !j.m_value.array->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) @@ -13014,7 +13512,7 @@ class binary_writer write_ubjson(el, use_count, use_type, prefix_required); } - if (not use_count) + if (!use_count) { oa->write_character(to_char_type(']')); } @@ -13029,9 +13527,9 @@ class binary_writer oa->write_character(to_char_type('[')); } - if (use_type and not j.m_value.binary->empty()) + if (use_type && !j.m_value.binary->empty()) { - assert(use_count); + JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); oa->write_character('U'); } @@ -13057,7 +13555,7 @@ class binary_writer } } - if (not use_count) + if (!use_count) { oa->write_character(to_char_type(']')); } @@ -13073,9 +13571,9 @@ class binary_writer } bool prefix_required = true; - if (use_type and not j.m_value.object->empty()) + if (use_type && !j.m_value.object->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) @@ -13106,7 +13604,7 @@ class binary_writer write_ubjson(el.second, use_count, use_type, prefix_required); } - if (not use_count) + if (!use_count) { oa->write_character(to_char_type('}')); } @@ -13207,7 +13705,7 @@ class binary_writer */ static std::size_t calc_bson_integer_size(const std::int64_t value) { - return (std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)() + return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)() ? sizeof(std::int32_t) : sizeof(std::int64_t); } @@ -13218,7 +13716,7 @@ class binary_writer void write_bson_integer(const string_t& name, const std::int64_t value) { - if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)()) + if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()) { write_bson_entry_header(name, 0x10); // int32 write_number<std::int32_t, true>(static_cast<std::int32_t>(value)); @@ -13367,7 +13865,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return 0ul; // LCOV_EXCL_STOP } @@ -13414,7 +13912,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return; // LCOV_EXCL_STOP } @@ -13546,18 +14044,28 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64")); + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i]))); + } } } // UBJSON: write number (signed integer) - template<typename NumberType, typename std::enable_if< - std::is_signed<NumberType>::value and - not std::is_floating_point<NumberType>::value, int>::type = 0> + template < typename NumberType, typename std::enable_if < + std::is_signed<NumberType>::value&& + !std::is_floating_point<NumberType>::value, int >::type = 0 > void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { - if ((std::numeric_limits<std::int8_t>::min)() <= n and n <= (std::numeric_limits<std::int8_t>::max)()) + if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)()) { if (add_prefix) { @@ -13565,7 +14073,7 @@ class binary_writer } write_number(static_cast<std::int8_t>(n)); } - else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n and n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)())) + else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)())) { if (add_prefix) { @@ -13573,7 +14081,7 @@ class binary_writer } write_number(static_cast<std::uint8_t>(n)); } - else if ((std::numeric_limits<std::int16_t>::min)() <= n and n <= (std::numeric_limits<std::int16_t>::max)()) + else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)()) { if (add_prefix) { @@ -13581,7 +14089,7 @@ class binary_writer } write_number(static_cast<std::int16_t>(n)); } - else if ((std::numeric_limits<std::int32_t>::min)() <= n and n <= (std::numeric_limits<std::int32_t>::max)()) + else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)()) { if (add_prefix) { @@ -13589,7 +14097,7 @@ class binary_writer } write_number(static_cast<std::int32_t>(n)); } - else if ((std::numeric_limits<std::int64_t>::min)() <= n and n <= (std::numeric_limits<std::int64_t>::max)()) + else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)()) { if (add_prefix) { @@ -13600,19 +14108,23 @@ class binary_writer // LCOV_EXCL_START else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64")); + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i]))); + } } // LCOV_EXCL_STOP } /*! @brief determine the type prefix of container values - - @note This function does not need to be 100% accurate when it comes to - integer limits. In case a number exceeds the limits of int64_t, - this will be detected by a later call to function - write_number_with_ubjson_prefix. Therefore, we return 'L' for any - value that does not fit the previous limits. */ CharType ubjson_prefix(const BasicJsonType& j) const noexcept { @@ -13626,24 +14138,28 @@ class binary_writer case value_t::number_integer: { - if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) + if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { return 'i'; } - if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) + if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) { return 'U'; } - if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) + if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { return 'I'; } - if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) + if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { return 'l'; } - // no check and assume int64_t (see note above) - return 'L'; + if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE } case value_t::number_unsigned: @@ -13664,8 +14180,12 @@ class binary_writer { return 'l'; } - // no check and assume int64_t (see note above) - return 'L'; + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE } case value_t::number_float: @@ -13728,20 +14248,40 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) && + static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) && + static_cast<double>(static_cast<float>(n)) == static_cast<double>(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast<float>(n)) + : get_msgpack_float_prefix(static_cast<float>(n))); + write_number(static_cast<float>(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, // such a conversion is required to allow values greater than 128. // See <https://github.com/nlohmann/json/issues/1286> for a discussion. template < typename C = CharType, - enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr > + enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr > static constexpr CharType to_char_type(std::uint8_t x) noexcept { return *reinterpret_cast<char*>(&x); } template < typename C = CharType, - enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr > + enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr > static CharType to_char_type(std::uint8_t x) noexcept { static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); @@ -13760,8 +14300,8 @@ class binary_writer template < typename InputCharType, typename C = CharType, enable_if_t < - std::is_signed<C>::value and - std::is_signed<char>::value and + std::is_signed<C>::value && + std::is_signed<char>::value && std::is_same<char, typename std::remove_cv<InputCharType>::type>::value > * = nullptr > static constexpr CharType to_char_type(InputCharType x) noexcept @@ -13786,32 +14326,26 @@ class binary_writer #include <algorithm> // reverse, remove, fill, find, none_of #include <array> // array -#include <cassert> // assert #include <clocale> // localeconv, lconv #include <cmath> // labs, isfinite, isnan, signbit #include <cstddef> // size_t, ptrdiff_t #include <cstdint> // uint8_t #include <cstdio> // snprintf #include <limits> // numeric_limits -#include <string> // string +#include <string> // string, char_traits #include <type_traits> // is_same #include <utility> // move -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/conversions/to_chars.hpp> #include <array> // array -#include <cassert> // assert #include <cmath> // signbit, isfinite #include <cstdint> // intN_t, uintN_t #include <cstring> // memcpy, memmove #include <limits> // numeric_limits #include <type_traits> // conditional -// #include <nlohmann/detail/boolean_operators.hpp> - // #include <nlohmann/detail/macro_scope.hpp> @@ -13842,7 +14376,7 @@ For a detailed description of the algorithm see: namespace dtoa_impl { -template <typename Target, typename Source> +template<typename Target, typename Source> Target reinterpret_bits(const Source source) { static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); @@ -13867,8 +14401,8 @@ struct diyfp // f * 2^e */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { - assert(x.e == y.e); - assert(x.f >= y.f); + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); return {x.f - y.f, x.e}; } @@ -13944,7 +14478,7 @@ struct diyfp // f * 2^e */ static diyfp normalize(diyfp x) noexcept { - assert(x.f != 0); + JSON_ASSERT(x.f != 0); while ((x.f >> 63u) == 0) { @@ -13963,8 +14497,8 @@ struct diyfp // f * 2^e { const int delta = x.e - target_exponent; - assert(delta >= 0); - assert(((x.f << delta) >> delta) == x.f); + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); return {x.f << delta, target_exponent}; } @@ -13983,11 +14517,11 @@ boundaries. @pre value must be finite and positive */ -template <typename FloatType> +template<typename FloatType> boundaries compute_boundaries(FloatType value) { - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // Convert the IEEE representation into a diyfp. // @@ -14036,7 +14570,7 @@ boundaries compute_boundaries(FloatType value) // -----------------+------+------+-------------+-------------+--- (B) // v- m- v m+ v+ - const bool lower_boundary_is_closer = F == 0 and E > 1; + const bool lower_boundary_is_closer = F == 0 && E > 1; const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer ? diyfp(4 * v.f - 1, v.e - 2) // (B) @@ -14267,18 +14801,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // 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); + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - assert(index >= 0); - assert(static_cast<std::size_t>(index) < kCachedPowers.size()); + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size()); const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)]; - assert(kAlpha <= cached.e + e + 64); - assert(kGamma >= cached.e + e + 64); + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); return cached; } @@ -14346,10 +14880,10 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) 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); + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> @@ -14371,10 +14905,10 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d // integer arithmetic. while (rest < dist - and delta - rest >= ten_k - and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { - assert(buf[len - 1] != '0'); + JSON_ASSERT(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } @@ -14402,8 +14936,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // 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); + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) @@ -14424,7 +14958,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - assert(p1 > 0); + JSON_ASSERT(p1 > 0); std::uint32_t pow10; const int k = find_largest_pow10(p1, pow10); @@ -14460,7 +14994,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // 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); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) @@ -14547,7 +15081,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - assert(p2 > delta); + JSON_ASSERT(p2 > delta); int m = 0; for (;;) @@ -14558,7 +15092,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = 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<std::uint64_t>::max)() / 10); + JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::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 @@ -14567,7 +15101,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = 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); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e @@ -14628,8 +15162,8 @@ 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); + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ @@ -14683,15 +15217,15 @@ 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 <typename FloatType> +template<typename FloatType> JSON_HEDLEY_NON_NULL(1) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3, "internal error: not enough precision"); - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting @@ -14727,8 +15261,8 @@ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { - assert(e > -1000); - assert(e < 1000); + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); if (e < 0) { @@ -14780,8 +15314,8 @@ 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); + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); const int k = len; const int n = len + decimal_exponent; @@ -14790,7 +15324,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // 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) + if (k <= n && n <= max_exp) { // digits[000] // len <= max_exp + 2 @@ -14802,19 +15336,19 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, return buf + (static_cast<size_t>(n) + 2); } - if (0 < n and n <= max_exp) + if (0 < n && n <= max_exp) { // dig.its // len <= max_digits10 + 1 - assert(k > n); + JSON_ASSERT(k > n); std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n)); buf[n] = '.'; return buf + (static_cast<size_t>(k) + 1U); } - if (min_exp < n and n <= 0) + if (min_exp < n && n <= 0) { // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 @@ -14859,13 +15393,13 @@ format. Returns an iterator pointing past-the-end of the decimal representation. @note The buffer must be large enough. @note The result is NOT null-terminated. */ -template <typename FloatType> +template<typename FloatType> JSON_HEDLEY_NON_NULL(1, 2) JSON_HEDLEY_RETURNS_NON_NULL char* to_chars(char* first, const char* last, FloatType value) { static_cast<void>(last); // maybe unused - fix warning - assert(std::isfinite(value)); + JSON_ASSERT(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) @@ -14883,7 +15417,7 @@ char* to_chars(char* first, const char* last, FloatType value) return first; } - assert(last - first >= std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted @@ -14893,16 +15427,16 @@ char* to_chars(char* first, const char* last, FloatType value) int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); - assert(len <= std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(len <= std::numeric_limits<FloatType>::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<FloatType>::digits10; - assert(last - first >= kMaxExp + 2); - assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10); - assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6); + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } @@ -14960,8 +15494,8 @@ class serializer error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) - , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)) - , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) @@ -15036,8 +15570,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -15064,8 +15598,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); @@ -15106,7 +15640,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(!val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); @@ -15127,7 +15661,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(!val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); @@ -15161,7 +15695,7 @@ class serializer o->write_characters("\"bytes\": [", 10); - if (not val.m_value.binary->empty()) + if (!val.m_value.binary->empty()) { for (auto i = val.m_value.binary->cbegin(); i != val.m_value.binary->cend() - 1; ++i) @@ -15192,7 +15726,7 @@ class serializer { o->write_characters("{\"bytes\":[", 10); - if (not val.m_value.binary->empty()) + if (!val.m_value.binary->empty()) { for (auto i = val.m_value.binary->cbegin(); i != val.m_value.binary->cend() - 1; ++i) @@ -15261,7 +15795,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } @@ -15353,7 +15887,7 @@ class serializer { // escape control characters (0x00..0x1F) or, if // ensure_ascii parameter is used, non-ASCII characters - if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F))) + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) { if (codepoint <= 0xFFFF) { @@ -15460,14 +15994,14 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } break; } default: // decode found yet incomplete multi-byte code point { - if (not ensure_ascii) + if (!ensure_ascii) { // code point will not be escaped - copy byte to buffer string_buffer[bytes++] = s[i]; @@ -15523,7 +16057,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } } @@ -15571,11 +16105,11 @@ class serializer @param[in] x integer number (signed or unsigned) to dump @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ - template<typename NumberType, detail::enable_if_t< - std::is_same<NumberType, number_unsigned_t>::value or - std::is_same<NumberType, number_integer_t>::value or - std::is_same<NumberType, binary_char_t>::value, - int> = 0> + template < typename NumberType, detail::enable_if_t < + std::is_same<NumberType, number_unsigned_t>::value || + std::is_same<NumberType, number_integer_t>::value || + std::is_same<NumberType, binary_char_t>::value, + int > = 0 > void dump_integer(NumberType x) { static constexpr std::array<std::array<char, 2>, 100> digits_to_99 @@ -15604,7 +16138,7 @@ class serializer // use a pointer to fill the buffer auto buffer_ptr = number_buffer.begin(); - const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755 + const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755 number_unsigned_t abs_value; unsigned int n_chars; @@ -15624,7 +16158,7 @@ class serializer } // spare 1 byte for '\0' - assert(n_chars < number_buffer.size() - 1); + JSON_ASSERT(n_chars < number_buffer.size() - 1); // jump to the end to generate the string from backward // so we later avoid reversing the result @@ -15665,7 +16199,7 @@ class serializer void dump_float(number_float_t x) { // NaN / inf - if (not std::isfinite(x)) + if (!std::isfinite(x)) { o->write_characters("null", 4); return; @@ -15677,8 +16211,8 @@ class serializer // // NB: The test below works if <long double> == <double>. static constexpr bool is_ieee_single_or_double - = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or - (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024); + = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) || + (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024); dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>()); } @@ -15700,9 +16234,9 @@ class serializer std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error - assert(len > 0); + JSON_ASSERT(len > 0); // check if buffer was large enough - assert(static_cast<std::size_t>(len) < number_buffer.size()); + JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') @@ -15710,12 +16244,12 @@ class serializer const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); + JSON_ASSERT((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') + if (decimal_point != '\0' && decimal_point != '.') { const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); if (dec_pos != number_buffer.end()) @@ -15731,7 +16265,7 @@ class serializer std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, [](char c) { - return c == '.' or c == 'e'; + return c == '.' || c == 'e'; }); if (value_is_int_like) @@ -15790,7 +16324,7 @@ class serializer : (0xFFu >> type) & (byte); std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type); - assert(index < 400); + JSON_ASSERT(index < 400); state = utf8d[index]; return state; } @@ -15802,7 +16336,7 @@ class serializer */ number_unsigned_t remove_sign(number_unsigned_t x) { - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE return x; // LCOV_EXCL_LINE } @@ -15817,7 +16351,7 @@ class serializer */ inline number_unsigned_t remove_sign(number_integer_t x) noexcept { - assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)()); + JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); return static_cast<number_unsigned_t>(-(x + 1)) + 1; } @@ -15853,6 +16387,79 @@ class serializer // #include <nlohmann/json_fwd.hpp> +// #include <nlohmann/ordered_map.hpp> + + +#include <functional> // less +#include <memory> // allocator +#include <utility> // pair +#include <vector> // vector + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json<ordered_map> +template <class Key, class T, class IgnoredLess = std::less<Key>, + class Allocator = std::allocator<std::pair<const Key, T>>> + struct ordered_map : std::vector<std::pair<const Key, T>, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector<std::pair<const Key, T>, Allocator>; + using typename Container::iterator; + using typename Container::size_type; + using typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template <class It> + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair<iterator, bool> emplace(key_type&& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](Key&& key) + { + return emplace(std::move(key), T{}).first->second; + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } +}; + +} // namespace nlohmann + /*! @brief namespace for Niels Lohmann @@ -15977,10 +16584,12 @@ class basic_json static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser( InputAdapterType adapter, detail::parser_callback_t<basic_json>cb = nullptr, - bool allow_exceptions = true - ) + const bool allow_exceptions = true, + const bool ignore_comments = false + ) { - return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter), std::move(cb), allow_exceptions); + return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; @@ -16009,6 +16618,8 @@ class basic_json using json_serializer = JSONSerializer<T, SFINAE>; /// how to treat decoding errors using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>; @@ -16121,7 +16732,7 @@ class basic_json { basic_json result; - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["copyright"] = "(C) 2013-2020 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = @@ -16703,7 +17314,7 @@ class basic_json }; std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter); AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...); - assert(object != nullptr); + JSON_ASSERT(object != nullptr); return object.release(); } @@ -16829,7 +17440,7 @@ class basic_json object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.8.0")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.0")); // LCOV_EXCL_LINE } break; } @@ -16916,7 +17527,7 @@ class basic_json } } - while (not stack.empty()) + while (!stack.empty()) { // move the last item to local variable to be processed basic_json current_item(std::move(stack.back())); @@ -16998,10 +17609,10 @@ class basic_json */ void assert_invariant() const noexcept { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - assert(m_type != value_t::binary or m_value.binary != nullptr); + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); } public: @@ -17209,10 +17820,10 @@ class basic_json @since version 2.1.0 */ - template <typename CompatibleType, - typename U = detail::uncvref_t<CompatibleType>, - detail::enable_if_t< - not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0> + template < typename CompatibleType, + typename U = detail::uncvref_t<CompatibleType>, + detail::enable_if_t < + !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 > basic_json(CompatibleType && val) noexcept(noexcept( JSONSerializer<U>::to_json(std::declval<basic_json_t&>(), std::forward<CompatibleType>(val)))) @@ -17247,9 +17858,9 @@ class basic_json @since version 3.2.0 */ - template <typename BasicJsonType, - detail::enable_if_t< - detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0> + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 > basic_json(const BasicJsonType& val) { using other_boolean_t = typename BasicJsonType::boolean_t; @@ -17294,7 +17905,7 @@ class basic_json m_type = value_t::discarded; break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } assert_invariant(); } @@ -17382,11 +17993,11 @@ class basic_json bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref<basic_json>& element_ref) { - return element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string(); + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); }); // adjust type if type deduction is not wanted - if (not type_deduction) + if (!type_deduction) { // if array is wanted, do not create an object though possible if (manual_type == value_t::array) @@ -17395,7 +18006,7 @@ class basic_json } // if object is wanted but impossible, throw an exception - if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object and not is_an_object)) + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list")); } @@ -17689,13 +18300,13 @@ class basic_json @since version 1.0.0 */ - template<class InputIT, typename std::enable_if< - std::is_same<InputIT, typename basic_json_t::iterator>::value or - std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0> + template < class InputIT, typename std::enable_if < + std::is_same<InputIT, typename basic_json_t::iterator>::value || + std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 > basic_json(InputIT first, InputIT last) { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) @@ -17715,8 +18326,8 @@ class basic_json case value_t::number_unsigned: case value_t::string: { - if (JSON_HEDLEY_UNLIKELY(not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } @@ -17792,9 +18403,9 @@ class basic_json // other constructors and destructor // /////////////////////////////////////// - template <typename JsonRef, - detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>, - std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 > + template<typename JsonRef, + detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>, + std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 > basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} /*! @@ -17949,9 +18560,9 @@ class basic_json @since version 1.0.0 */ basic_json& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible<value_t>::value and - std::is_nothrow_move_assignable<value_t>::value and - std::is_nothrow_move_constructible<json_value>::value and + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { @@ -18017,7 +18628,8 @@ class basic_json @param[in] error_handler how to react on decoding errors; there are three possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), - and `ignore` (ignore invalid UTF-8 sequences during serialization). + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). @return string containing the serialization of the JSON value @@ -18130,7 +18742,7 @@ class basic_json */ constexpr bool is_primitive() const noexcept { - return is_null() or is_string() or is_boolean() or is_number() or is_binary(); + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); } /*! @@ -18157,7 +18769,7 @@ class basic_json */ constexpr bool is_structured() const noexcept { - return is_array() or is_object(); + return is_array() || is_object(); } /*! @@ -18231,7 +18843,7 @@ class basic_json */ constexpr bool is_number() const noexcept { - return is_number_integer() or is_number_float(); + return is_number_integer() || is_number_float(); } /*! @@ -18260,7 +18872,7 @@ class basic_json */ constexpr bool is_number_integer() const noexcept { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; } /*! @@ -18641,9 +19253,9 @@ class basic_json @since version 3.2.0 */ - template<typename BasicJsonType, detail::enable_if_t< - not std::is_same<BasicJsonType, basic_json>::value and - detail::is_basic_json<BasicJsonType>::value, int> = 0> + template < typename BasicJsonType, detail::enable_if_t < + !std::is_same<BasicJsonType, basic_json>::value&& + detail::is_basic_json<BasicJsonType>::value, int > = 0 > BasicJsonType get() const { return *this; @@ -18688,19 +19300,19 @@ class basic_json @since version 2.1.0 */ - template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, - detail::enable_if_t < - not detail::is_basic_json<ValueType>::value and - detail::has_from_json<basic_json_t, ValueType>::value and - not detail::has_non_default_from_json<basic_json_t, ValueType>::value, - int> = 0> + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, + detail::enable_if_t < + !detail::is_basic_json<ValueType>::value && + detail::has_from_json<basic_json_t, ValueType>::value && + !detail::has_non_default_from_json<basic_json_t, ValueType>::value, + int > = 0 > ValueType get() const noexcept(noexcept( JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>()))) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get<const basic_json_t>(), which is why we // still need the uncvref - static_assert(not std::is_reference<ValueTypeCV>::value, + static_assert(!std::is_reference<ValueTypeCV>::value, "get() cannot be used with reference types, you might want to use get_ref()"); static_assert(std::is_default_constructible<ValueType>::value, "types must be DefaultConstructible when used with get()"); @@ -18741,14 +19353,14 @@ class basic_json @since version 2.1.0 */ - template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, - detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and - detail::has_non_default_from_json<basic_json_t, ValueType>::value, - int> = 0> + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, + detail::enable_if_t < !std::is_same<basic_json_t, ValueType>::value && + detail::has_non_default_from_json<basic_json_t, ValueType>::value, + int > = 0 > ValueType get() const noexcept(noexcept( JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>()))) { - static_assert(not std::is_reference<ValueTypeCV>::value, + static_assert(!std::is_reference<ValueTypeCV>::value, "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer<ValueType>::from_json(*this); } @@ -18786,11 +19398,11 @@ class basic_json @since version 3.3.0 */ - template<typename ValueType, - detail::enable_if_t < - not detail::is_basic_json<ValueType>::value and - detail::has_from_json<basic_json_t, ValueType>::value, - int> = 0> + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json<ValueType>::value&& + detail::has_from_json<basic_json_t, ValueType>::value, + int > = 0 > ValueType & get_to(ValueType& v) const noexcept(noexcept( JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v))) { @@ -18798,6 +19410,18 @@ class basic_json return v; } + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template<typename ValueType, + detail::enable_if_t < + detail::is_basic_json<ValueType>::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + template < typename T, std::size_t N, typename Array = T (&)[N], @@ -18850,9 +19474,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template<typename PointerType, typename std::enable_if< - std::is_pointer<PointerType>::value and - std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> + template < typename PointerType, typename std::enable_if < + std::is_pointer<PointerType>::value&& + std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 > constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>())) { // delegate the call to get_impl_ptr<>() const @@ -18944,9 +19568,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template<typename ReferenceType, typename std::enable_if< - std::is_reference<ReferenceType>::value and - std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0> + template < typename ReferenceType, typename std::enable_if < + std::is_reference<ReferenceType>::value&& + std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 > ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -18983,17 +19607,17 @@ class basic_json @since version 1.0.0 */ template < typename ValueType, typename std::enable_if < - not std::is_pointer<ValueType>::value and - not std::is_same<ValueType, detail::json_ref<basic_json>>::value and - not std::is_same<ValueType, typename string_t::value_type>::value and - not detail::is_basic_json<ValueType>::value - and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value -#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER >= 1910 and _MSC_VER <= 1914)) - and not std::is_same<ValueType, typename std::string_view>::value -#endif - and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value + !std::is_pointer<ValueType>::value&& + !std::is_same<ValueType, detail::json_ref<basic_json>>::value&& + !std::is_same<ValueType, typename string_t::value_type>::value&& + !detail::is_basic_json<ValueType>::value + && !std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + && !std::is_same<ValueType, typename std::string_view>::value +#endif + && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value , int >::type = 0 > - operator ValueType() const + JSON_EXPLICIT operator ValueType() const { // delegate the call to get<>() const return get<ValueType>(); @@ -19010,7 +19634,7 @@ class basic_json */ binary_t& get_binary() { - if (not is_binary()) + if (!is_binary()) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); } @@ -19021,7 +19645,7 @@ class basic_json /// @copydoc get_binary() const binary_t& get_binary() const { - if (not is_binary()) + if (!is_binary()) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); } @@ -19399,7 +20023,7 @@ class basic_json // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -19491,7 +20115,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -19548,9 +20172,10 @@ class basic_json @since version 1.0.0 */ - template<class ValueType, typename std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value - and not std::is_same<value_t, ValueType>::value, int>::type = 0> + // using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable<basic_json_t, ValueType>::value + && !std::is_same<value_t, ValueType>::value, int >::type = 0 > ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects @@ -19560,7 +20185,7 @@ class basic_json const auto it = find(key); if (it != end()) { - return *it; + return it->template get<ValueType>(); } return default_value; @@ -19622,7 +20247,7 @@ class basic_json @since version 2.0.2 */ template<class ValueType, typename std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> + detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects @@ -19631,7 +20256,7 @@ class basic_json // if pointer resolves a value, return it or use default value JSON_TRY { - return ptr.get_checked(this); + return ptr.get_checked(this).template get<ValueType>(); } JSON_INTERNAL_CATCH (out_of_range&) { @@ -19784,10 +20409,10 @@ class basic_json @since version 1.0.0 */ - template<class IteratorType, typename std::enable_if< - std::is_same<IteratorType, typename basic_json_t::iterator>::value or - std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type - = 0> + template < class IteratorType, typename std::enable_if < + std::is_same<IteratorType, typename basic_json_t::iterator>::value || + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type + = 0 > IteratorType erase(IteratorType pos) { // make sure iterator fits the current value @@ -19807,7 +20432,7 @@ class basic_json case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { JSON_THROW(invalid_iterator::create(205, "iterator out of range")); } @@ -19897,14 +20522,14 @@ class basic_json @since version 1.0.0 */ - template<class IteratorType, typename std::enable_if< - std::is_same<IteratorType, typename basic_json_t::iterator>::value or - std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type - = 0> + template < class IteratorType, typename std::enable_if < + std::is_same<IteratorType, typename basic_json_t::iterator>::value || + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type + = 0 > IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value - if (JSON_HEDLEY_UNLIKELY(this != first.m_object or this != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } @@ -19920,8 +20545,8 @@ class basic_json case value_t::string: case value_t::binary: { - if (JSON_HEDLEY_LIKELY(not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } @@ -20166,11 +20791,11 @@ class basic_json @since version 3.6.0 */ - template<typename KeyT, typename std::enable_if< - not std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int>::type = 0> + template < typename KeyT, typename std::enable_if < + !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 > bool contains(KeyT && key) const { - return is_object() and m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end(); + return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end(); } /*! @@ -20988,7 +21613,7 @@ class basic_json void push_back(basic_json&& val) { // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } @@ -21023,7 +21648,7 @@ class basic_json void push_back(const basic_json& val) { // push_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } @@ -21073,7 +21698,7 @@ class basic_json void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects - if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } @@ -21127,7 +21752,7 @@ class basic_json */ void push_back(initializer_list_t init) { - if (is_object() and init.size() == 2 and (*init.begin())->is_string()) + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) { basic_json&& key = init.begin()->moved_or_copied(); push_back(typename object_t::value_type( @@ -21176,7 +21801,7 @@ class basic_json reference emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); } @@ -21229,7 +21854,7 @@ class basic_json std::pair<iterator, bool> emplace(Args&& ... args) { // emplace only works for null objects or arrays - if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); } @@ -21259,7 +21884,7 @@ class basic_json iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); - assert(m_value.array != nullptr); + JSON_ASSERT(m_value.array != nullptr); auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...); @@ -21396,7 +22021,7 @@ class basic_json iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays - if (JSON_HEDLEY_UNLIKELY(not is_array())) + if (JSON_HEDLEY_UNLIKELY(!is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } @@ -21449,7 +22074,7 @@ class basic_json iterator insert(const_iterator pos, initializer_list_t ilist) { // insert only works for arrays - if (JSON_HEDLEY_UNLIKELY(not is_array())) + if (JSON_HEDLEY_UNLIKELY(!is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } @@ -21490,7 +22115,7 @@ class basic_json void insert(const_iterator first, const_iterator last) { // insert only works for objects - if (JSON_HEDLEY_UNLIKELY(not is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } @@ -21502,7 +22127,7 @@ class basic_json } // passed iterators must belong to objects - if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object())) + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); } @@ -21539,11 +22164,11 @@ class basic_json assert_invariant(); } - if (JSON_HEDLEY_UNLIKELY(not is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); } - if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); } @@ -21590,7 +22215,7 @@ class basic_json assert_invariant(); } - if (JSON_HEDLEY_UNLIKELY(not is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); } @@ -21602,8 +22227,8 @@ class basic_json } // passed iterators must belong to objects - if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object() - or not last.m_object->is_object())) + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() + || !last.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); } @@ -21632,9 +22257,9 @@ class basic_json @since version 1.0.0 */ void swap(reference other) noexcept ( - std::is_nothrow_move_constructible<value_t>::value and - std::is_nothrow_move_assignable<value_t>::value and - std::is_nothrow_move_constructible<json_value>::value and + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { @@ -21646,6 +22271,34 @@ class basic_json /*! @brief exchanges the values + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& + std::is_nothrow_move_assignable<json_value>::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is @@ -21894,27 +22547,27 @@ class basic_json return false; } } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned); } @@ -21964,7 +22617,7 @@ class basic_json */ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { - return not (lhs == rhs); + return !(lhs == rhs); } /*! @@ -22057,27 +22710,27 @@ class basic_json return false; } } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } @@ -22131,7 +22784,7 @@ class basic_json */ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { - return not (rhs < lhs); + return !(rhs < lhs); } /*! @@ -22177,7 +22830,7 @@ class basic_json */ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { - return not (lhs <= rhs); + return !(lhs <= rhs); } /*! @@ -22223,7 +22876,7 @@ class basic_json */ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { - return not (lhs < rhs); + return !(lhs < rhs); } /*! @@ -22344,6 +22997,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22372,16 +23028,18 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 2.0.3 (contiguous containers) + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. */ template<typename InputType> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22398,6 +23056,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22413,10 +23074,11 @@ class basic_json static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22424,10 +23086,11 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i.get(), cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22447,6 +23110,9 @@ class basic_json iterators. @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return Whether the input read from @a i is valid JSON. @@ -22459,22 +23125,25 @@ class basic_json from a string.,accept__string} */ template<typename InputType> - static bool accept(InputType&& i) + static bool accept(InputType&& i, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true); + return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true); } template<typename IteratorType> - static bool accept(IteratorType first, IteratorType last) + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) - static bool accept(detail::span_input_adapter&& i) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) { - return parser(i.get()).accept(true); + return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @@ -22494,6 +23163,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. @return return value of the last processed SAX event @@ -22518,11 +23190,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward<InputType>(i)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } @@ -22530,11 +23203,12 @@ class basic_json JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } @@ -22543,11 +23217,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } @@ -22749,7 +23424,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @@ -22765,7 +23439,7 @@ class basic_json vector in CBOR format.,to_cbor} @sa http://cbor.io - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the analogous deserialization @sa @ref to_msgpack(const basic_json&) for the related MessagePack format @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the @@ -22821,7 +23495,8 @@ class basic_json number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -22845,9 +23520,6 @@ class basic_json - arrays with more than 4294967295 elements - objects with more than 4294967295 elements - @note The following MessagePack types are not used in the conversion: - - float 32 (0xCA) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @@ -22916,6 +23588,7 @@ class basic_json number_unsigned | 256..32767 | int16 | `I` number_unsigned | 32768..2147483647 | int32 | `l` number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` number_float | *any value* | float64 | `D` string | *with shortest length indicator* | string | `S` array | *see notes on optimized format* | array | `[` @@ -22926,7 +23599,6 @@ class basic_json @note The following values can **not** be converted to a UBJSON value: - strings with more than 9223372036854775807 bytes (theoretical) - - unsigned integer numbers above 9223372036854775807 @note The following markers are not used in the conversion: - `Z`: no-op values are not created. @@ -23138,7 +23810,6 @@ class basic_json - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @@ -23155,6 +23826,7 @@ class basic_json (true by default) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] tag_handler how to treat CBOR tags (optional, error by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -23181,34 +23853,36 @@ class basic_json @since version 2.0.9; parameter @a start_index since 2.1.1; changed to consume input adapters, removed start_index parameter, and added @a strict parameter since 3.0.0; added @a allow_exceptions parameter - since 3.2.0 + since 3.2.0; added @a tag_handler parameter since 3.9.0. */ template<typename InputType> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(InputType&& i, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward<InputType>(i)); - const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } /*! - @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) + @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) */ template<typename IteratorType> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(IteratorType first, IteratorType last, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -23217,9 +23891,10 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(const T* ptr, std::size_t len, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { - return from_cbor(ptr, ptr + len, strict, allow_exceptions); + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); } @@ -23227,12 +23902,13 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); auto ia = i.get(); - const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -23310,7 +23986,7 @@ class basic_json @sa http://msgpack.org @sa @ref to_msgpack(const basic_json&) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the related UBJSON format @@ -23397,6 +24073,7 @@ class basic_json int16 | number_integer | `I` int32 | number_integer | `l` int64 | number_integer | `L` + high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' string | string | `S` char | string | `C` array | array (optimized values are supported) | `[` @@ -23428,7 +24105,7 @@ class basic_json @sa http://ubjson.org @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the related MessagePack format @@ -23543,7 +24220,7 @@ class basic_json @sa http://bsonspec.org/spec.html @sa @ref to_bson(const basic_json&) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the related MessagePack format @@ -23962,7 +24639,7 @@ class basic_json else { const auto idx = json_pointer::array_index(last_path); - if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size())) + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -23976,7 +24653,7 @@ class basic_json // if there exists a parent it cannot be primitive default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } }; @@ -24005,12 +24682,12 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast<size_type>(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; // type check: top level value must be an array - if (JSON_HEDLEY_UNLIKELY(not json_patch.is_array())) + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } @@ -24036,7 +24713,7 @@ class basic_json } // check if result is of type string - if (JSON_HEDLEY_UNLIKELY(string_type and not it->second.is_string())) + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } @@ -24046,14 +24723,14 @@ class basic_json }; // type check: every element of the array must be an object - if (JSON_HEDLEY_UNLIKELY(not val.is_object())) + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members - const std::string op = get_value("op", "op", true); - const std::string path = get_value(op, "path", true); + const auto op = get_value("op", "op", true).template get<std::string>(); + const auto path = get_value(op, "path", true).template get<std::string>(); json_pointer ptr(path); switch (get_op(op)) @@ -24079,7 +24756,7 @@ class basic_json case patch_operations::move: { - const std::string from_path = get_value("move", "from", true); + const auto from_path = get_value("move", "from", true).template get<std::string>(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -24096,7 +24773,7 @@ class basic_json case patch_operations::copy: { - const std::string from_path = get_value("copy", "from", true); + const auto from_path = get_value("copy", "from", true).template get<std::string>(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -24124,7 +24801,7 @@ class basic_json } // throw an exception if test fails - if (JSON_HEDLEY_UNLIKELY(not success)) + if (JSON_HEDLEY_UNLIKELY(!success)) { JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } @@ -24206,7 +24883,7 @@ class basic_json { // first pass: traverse common elements std::size_t i = 0; - while (i < source.size() and i < target.size()) + while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); @@ -24357,7 +25034,7 @@ class basic_json { if (apply_patch.is_object()) { - if (not is_object()) + if (!is_object()) { *this = object(); } @@ -24417,9 +25094,7 @@ struct hash<nlohmann::json> */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash<nlohmann::json::string_t>(); - return h(j.dump()); + return nlohmann::detail::hash(j); } }; @@ -24440,6 +25115,9 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + /*! @brief exchanges the values of two JSON objects @@ -24447,13 +25125,15 @@ struct less<::nlohmann::detail::value_t> */ template<> inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( - is_nothrow_move_constructible<nlohmann::json>::value and + is_nothrow_move_constructible<nlohmann::json>::value&& is_nothrow_move_assignable<nlohmann::json>::value -) + ) { j1.swap(j2); } +#endif + } // namespace std /*! @@ -24506,6 +25186,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #endif // clean up +#undef JSON_ASSERT #undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW @@ -24514,6 +25195,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_HAS_CPP_17 #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT // #include <nlohmann/thirdparty/hedley/hedley_undef.hpp> #undef JSON_HEDLEY_ALWAYS_INLINE diff --git a/include/nlohmann/json_fwd.hpp b/include/nlohmann/json_fwd.hpp index 824e86a1f6fcda1ea8632d679ffecc0d0165d5f5..332227c1ba280e8a3a60dd489d3a154b0ea7b96c 100644 --- a/include/nlohmann/json_fwd.hpp +++ b/include/nlohmann/json_fwd.hpp @@ -60,6 +60,19 @@ uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; + +template<class Key, class T, class IgnoredLess, class Allocator> +struct ordered_map; + +/*! +@brief ordered JSON class + +This type preserves the insertion order of object keys. + +@since version 3.9.0 +*/ +using ordered_json = basic_json<nlohmann::ordered_map>; + } // namespace nlohmann #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_