From 7ed980e8b9f25eb57a9af3744424a19d81876fa7 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 9 Feb 2024 13:15:42 +0100 Subject: [PATCH 001/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1fbfe27..32d5a3b 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1fbfe272a8c596235f3510295c759314d0b38474 +Subproject commit 32d5a3baae930eb44aee84fbd778feace6f3eb40 From 9fc0f1c816cff159f859a44deabc29ce94d370c0 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 9 Feb 2024 13:49:18 +0100 Subject: [PATCH 002/137] Update CMakeLists.txt and json.hpp --- CMakeLists.txt | 61 ++++++--------------------------------------- include/json.hpp | 5 ++-- src/json.cpp | 4 +-- tests/test_json.cpp | 10 ++++---- 4 files changed, 16 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc7a02e..f04279e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,67 +1,20 @@ -cmake_minimum_required(VERSION 3.0.0) -project(json VERSION 0.1.0) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") - -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - -include(GenerateExportHeader) -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) +cmake_minimum_required(VERSION 3.5.0) +project(json VERSION 0.2.0 LANGUAGES CXX) include(CTest) enable_testing() -option(JSON_INCLUDE_UTILS "Include utils library" ON) -if(JSON_INCLUDE_UTILS) - add_subdirectory(extern/utils) -endif() - -file(GLOB JSON_SOURCES src/*.cpp) -file(GLOB JSON_HEADERS include/*.hpp) +add_subdirectory(extern/utils) -add_library(${PROJECT_NAME} SHARED ${JSON_SOURCES}) -add_dependencies(${PROJECT_NAME} utils) -GENERATE_EXPORT_HEADER(${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} PUBLIC $/include $) -target_link_libraries(${PROJECT_NAME} PUBLIC utils) +add_library(json src/json.cpp) +target_compile_features(json PUBLIC cxx_std_17) +target_include_directories(json PUBLIC $) +target_link_libraries(json PUBLIC utils) if(BUILD_TESTING) add_subdirectory(tests) endif() -if(MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) - if (ADD_COVERAGE) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(${PROJECT_NAME} PRIVATE --coverage) - target_link_libraries(${PROJECT_NAME} PUBLIC gcov) - endif() - endif() -endif() - -install( - TARGETS ${PROJECT_NAME} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) -install( - FILES ${JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/json_export.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} -) -configure_package_config_file(src/${PROJECT_NAME}Config.cmake.in ${PROJECT_NAME}Config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} PATH_VARS CMAKE_INSTALL_INCLUDEDIR) -write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION 1.0.0 COMPATIBILITY SameMajorVersion) -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) - set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/include/json.hpp b/include/json.hpp index 34283b1..d145d14 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -1,6 +1,5 @@ #pragma once -#include "json_export.h" #include #include #include @@ -457,7 +456,7 @@ namespace json std::vector arr_val; }; - JSON_EXPORT json load(std::istream &is); + json load(std::istream &is); inline json load(const char *str) { std::stringstream ss; @@ -470,5 +469,5 @@ namespace json ss << str; return load(ss); } - JSON_EXPORT std::string parse_string(std::istream &is); + std::string parse_string(std::istream &is); } // namespace json diff --git a/src/json.cpp b/src/json.cpp index 0b12f19..fa8547c 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -5,7 +5,7 @@ inline char get_char(std::istream &is) { return static_cast(is.get()); } namespace json { - JSON_EXPORT json load(std::istream &is) + json load(std::istream &is) { is >> std::ws; // we remove all the leading whitespace.. switch (is.peek()) @@ -185,7 +185,7 @@ namespace json } } - JSON_EXPORT std::string parse_string(std::istream &is) + std::string parse_string(std::istream &is) { get_char(is); std::string val; diff --git a/tests/test_json.cpp b/tests/test_json.cpp index d4b8fd8..88cf953 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -1,5 +1,5 @@ #include "json.hpp" -#include "logging.h" +#include "logging.hpp" #include #include @@ -67,7 +67,7 @@ void test_json_special_chars() } )"; json::json j = json::load(ss); - LOG(j); + LOG_INFO(j); assert(j["a"] == "\b\f\n\r\t\"\\"); } @@ -81,7 +81,7 @@ void test_initializer_lists() {"e", nullptr}, {"f", {1, 2}}, {"g", {{"h", 1}, {"i", 2}}}}; - LOG(j); + LOG_INFO(j); assert(j["a"] == 1); assert(j["b"] == 2.0); assert(j["c"] == "3"); @@ -181,7 +181,7 @@ void test_iterate() std::map &m = j0.get_object(); for ([[maybe_unused]] auto &[key, value] : m) - LOG("key " << key << " value " << value); + LOG_INFO("key " << key << " value " << value); json::json j1 = nullptr; j1.push_back(1); @@ -189,7 +189,7 @@ void test_iterate() j1.push_back(3); for ([[maybe_unused]] auto &value : j1.get_array()) - LOG("value " << value); + LOG_INFO("value " << value); } void test_null() From 1beca5fe080a39d1e55a06649aea273f181e944c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 9 Feb 2024 14:16:37 +0100 Subject: [PATCH 003/137] Add build status badge to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d0c4b83..b169b71 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # JSON +![Build Status](https://github.com/ratioSolver/json/actions/workflows/cmake.yml/badge.svg) + This repository contains a [JSON](http://www.json.org/) parser and generator written in C++. ## Read a JSON from a string From 5668a7d7cc391ea2311648886253e3462707e5fa Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 10 Feb 2024 16:32:48 +0100 Subject: [PATCH 004/137] Add compiler flags for debugging and coverage Update subproject commit hash --- CMakeLists.txt | 21 +++++++++++++++++---- extern/utils | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f04279e..e9fea34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,33 @@ cmake_minimum_required(VERSION 3.5.0) project(json VERSION 0.2.0 LANGUAGES CXX) +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") include(CTest) enable_testing() add_subdirectory(extern/utils) -add_library(json src/json.cpp) -target_compile_features(json PUBLIC cxx_std_17) -target_include_directories(json PUBLIC $) -target_link_libraries(json PUBLIC utils) +add_library(${PROJECT_NAME} src/json.cpp) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) +target_include_directories(${PROJECT_NAME} PUBLIC $) +target_link_libraries(${PROJECT_NAME} PUBLIC utils) if(BUILD_TESTING) add_subdirectory(tests) endif() +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /W4) +else() + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) + if (ADD_COVERAGE) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${PROJECT_NAME} PRIVATE --coverage) + target_link_libraries(${PROJECT_NAME} PUBLIC gcov) + endif() + endif() +endif() + set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/extern/utils b/extern/utils index 32d5a3b..af41ee0 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 32d5a3baae930eb44aee84fbd778feace6f3eb40 +Subproject commit af41ee0754ddf78be0e38b7737c1f88121e5c5c4 From 6a1d3790ad321ce4e82019a6b7e741d395e3cfbb Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 10 Feb 2024 17:05:25 +0100 Subject: [PATCH 005/137] Add coverage support for Debug builds on Ubuntu --- .github/workflows/cmake.yml | 18 +++++++++++++++++- CMakeLists.txt | 2 +- extern/utils | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ce675e3..ff9f761 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -20,8 +20,17 @@ jobs: with: submodules: recursive + - name: Install dependencies + if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' + run: sudo apt-get install -y lcov + + - name: Configure CMake (Debug) + if: matrix.build_type == 'Debug' + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON + - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + if: matrix.build_type == 'Release' + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} @@ -29,3 +38,10 @@ jobs: - name: Test working-directory: ${{github.workspace}}/build run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage + + - name: Make coverage report + if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' + run: | + lcov --capture --directory ${{github.workspace}}/build --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + lcov --list coverage.info \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e9fea34..a7e996b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4) else() target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) - if (ADD_COVERAGE) + if (ENABLE_COVERAGE) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(${PROJECT_NAME} PRIVATE --coverage) target_link_libraries(${PROJECT_NAME} PUBLIC gcov) diff --git a/extern/utils b/extern/utils index af41ee0..7553d67 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit af41ee0754ddf78be0e38b7737c1f88121e5c5c4 +Subproject commit 7553d6759e3f9c7819f4fd19430461314efd38f9 From eb8490112c672a72c752ee95554b3937910bcc6a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 26 Feb 2024 14:35:42 +0100 Subject: [PATCH 006/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 7553d67..103f64f 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 7553d6759e3f9c7819f4fd19430461314efd38f9 +Subproject commit 103f64f25eed66d905213573852ece0d8671deea From f7ec4051d751ed4b7296b737c471aeda4cd02a0c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 26 Feb 2024 15:17:48 +0100 Subject: [PATCH 007/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 103f64f..adc4493 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 103f64f25eed66d905213573852ece0d8671deea +Subproject commit adc4493bcb1b855166c169c756cf0f03e84f5283 From 180c70b410b83d6346b99ce6995f9bb04fbecf18 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 2 Mar 2024 11:32:07 +0100 Subject: [PATCH 008/137] Add option to include utils library in CMakeLists.txt --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7e996b..47d2e7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,10 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") include(CTest) enable_testing() -add_subdirectory(extern/utils) +option(JSON_INCLUDE_UTILS "Include utils library" ON) +if(JSON_INCLUDE_UTILS) + add_subdirectory(extern/utils) +endif() add_library(${PROJECT_NAME} src/json.cpp) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) From 340dc324ef669183f709cd362ccc8494a74e2a3d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 2 Mar 2024 16:58:28 +0100 Subject: [PATCH 009/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index adc4493..8a7b36b 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit adc4493bcb1b855166c169c756cf0f03e84f5283 +Subproject commit 8a7b36b94d26f3fcd3f62463cf66a588cb8b0273 From 6896941608a14e489cfe1ee7b90aba435c5373b7 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 4 Mar 2024 15:47:08 +0100 Subject: [PATCH 010/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 8a7b36b..a8d0814 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 8a7b36b94d26f3fcd3f62463cf66a588cb8b0273 +Subproject commit a8d0814d5d465d32cb137afff80819eec563b850 From 2581af1316df5e2bb7fefd40775aea78b5b71c08 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 9 Mar 2024 11:19:47 +0100 Subject: [PATCH 011/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index a8d0814..f7649a8 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit a8d0814d5d465d32cb137afff80819eec563b850 +Subproject commit f7649a8558235c45cae11692583e0e8d3efe1e22 From 9daea9ffd0e0e40714aeca011fbb5fe98d887f1c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Mar 2024 11:50:37 +0100 Subject: [PATCH 012/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index f7649a8..758016c 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f7649a8558235c45cae11692583e0e8d3efe1e22 +Subproject commit 758016caf588f2d60bf3476a968dd7405113d3be From 2244f314633972a7861fdaa02c4da7747d1ea539 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Mar 2024 21:53:11 +0100 Subject: [PATCH 013/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 758016c..9d43f5a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 758016caf588f2d60bf3476a968dd7405113d3be +Subproject commit 9d43f5a0256aafa919be679c2caf44f96d969968 From 67f7cc531fa30d028d9aec03482b3863007bb523 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 20 Mar 2024 11:26:42 +0100 Subject: [PATCH 014/137] Update actions/checkout to v4 --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ff9f761..a8d9f3a 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -16,7 +16,7 @@ jobs: BUILD_TYPE: ${{ matrix.build_type }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive From e1adae8e5a9faab483f0d27e095619c2627db400 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 14:56:21 +0100 Subject: [PATCH 015/137] Refactor CMakeLists.txt to use separate library name and update target names --- CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47d2e7f..0097ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,27 +10,28 @@ if(JSON_INCLUDE_UTILS) add_subdirectory(extern/utils) endif() -add_library(${PROJECT_NAME} src/json.cpp) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) -target_include_directories(${PROJECT_NAME} PUBLIC $) -target_link_libraries(${PROJECT_NAME} PUBLIC utils) +add_library(json src/json.cpp) +add_dependencies(json utils) +target_compile_features(json PUBLIC cxx_std_17) +target_include_directories(json PUBLIC $) +target_link_libraries(json PUBLIC utils) if(BUILD_TESTING) add_subdirectory(tests) endif() if(MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4) + target_compile_options(json PRIVATE /W4) else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) + target_compile_options(json PRIVATE -Wall -Wextra -Wpedantic) if (ENABLE_COVERAGE) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(${PROJECT_NAME} PRIVATE --coverage) - target_link_libraries(${PROJECT_NAME} PUBLIC gcov) + target_compile_options(json PRIVATE --coverage) + target_link_libraries(json PUBLIC gcov) endif() endif() endif() -set(CPACK_PROJECT_NAME ${PROJECT_NAME}) +set(CPACK_PROJECT_NAME json) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) From 0b194a993fb38822004cce25ef721164fd055eac Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 15:08:26 +0100 Subject: [PATCH 016/137] Update CMake build configuration and subproject commit reference --- .github/workflows/cmake.yml | 2 +- extern/utils | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index a8d9f3a..2e3d7e5 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -33,7 +33,7 @@ jobs: run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build - name: Test working-directory: ${{github.workspace}}/build diff --git a/extern/utils b/extern/utils index 9d43f5a..f5c4d84 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 9d43f5a0256aafa919be679c2caf44f96d969968 +Subproject commit f5c4d8485fd70827ead2ed952b37cfea1a226637 From a2143396270bbcf4edcdc5fb9839ad57a2a3f8c7 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 15:18:37 +0100 Subject: [PATCH 017/137] Update build workflow and test configuration --- .github/workflows/cmake.yml | 8 +++++++- extern/utils | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 2e3d7e5..ca3cdf0 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -35,10 +35,16 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build - - name: Test + - name: Test with coverage + if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' working-directory: ${{github.workspace}}/build run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage + - name: Test without coverage + if: matrix.build_type == 'Release' || matrix.os != 'ubuntu-latest' + working-directory: ${{github.workspace}}/build + run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test + - name: Make coverage report if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' run: | diff --git a/extern/utils b/extern/utils index f5c4d84..b4a9c36 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f5c4d8485fd70827ead2ed952b37cfea1a226637 +Subproject commit b4a9c36e16c714d4778cd25f71b0634d0fc10a0d From fa89801a91c3378ee58c5a8a8a47bee8c8b7a8da Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 15:27:03 +0100 Subject: [PATCH 018/137] Update build command to include build type configuration --- .github/workflows/cmake.yml | 2 +- extern/utils | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ca3cdf0..ebc526f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -33,7 +33,7 @@ jobs: run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release - name: Build - run: cmake --build ${{github.workspace}}/build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Test with coverage if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' diff --git a/extern/utils b/extern/utils index b4a9c36..3e2ca38 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit b4a9c36e16c714d4778cd25f71b0634d0fc10a0d +Subproject commit 3e2ca38f38fb191f3af3d435c7952df180574ceb From d2532c25a372f48fafaaf9a84ccb28cba3d596ce Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 15:31:21 +0100 Subject: [PATCH 019/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 3e2ca38..d4db8c8 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 3e2ca38f38fb191f3af3d435c7952df180574ceb +Subproject commit d4db8c877dd5e31dee2d4595a106077e89edfc6b From 02bd5115597a49b4a4a7c0c30d94ede6a5357de4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 25 Mar 2024 16:43:33 +0100 Subject: [PATCH 020/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index d4db8c8..05a4a2e 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit d4db8c877dd5e31dee2d4595a106077e89edfc6b +Subproject commit 05a4a2e341192ebf2027d203ef373b6d354d5458 From 005f4e574dd6f68b7e79f6c3a03ea1f5b064ca84 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 27 Mar 2024 12:39:13 +0100 Subject: [PATCH 021/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 05a4a2e..db89718 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 05a4a2e341192ebf2027d203ef373b6d354d5458 +Subproject commit db8971867c70a37ec24ce7235392c67e6aac8129 From 262bdcb57bde6539bfbcdcbef45e01b1202f4be3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 27 Mar 2024 18:13:55 +0100 Subject: [PATCH 022/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index db89718..f307b10 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit db8971867c70a37ec24ce7235392c67e6aac8129 +Subproject commit f307b1016df79675b51614ef9a8a79bc9541c721 From 17a4a75d65bb1eb1b94fefe8fd912dccaa995e54 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 28 Mar 2024 03:53:26 +0100 Subject: [PATCH 023/137] Update CMake workflow to include memory sanitizer and valgrind*** ***Update subproject commit hash in extern/utils --- .github/workflows/cmake.yml | 8 ++++---- extern/utils | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ebc526f..d6d0ab7 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' - run: sudo apt-get install -y lcov + run: sudo apt-get install -y lcov valgrind - name: Configure CMake (Debug) if: matrix.build_type == 'Debug' @@ -35,12 +35,12 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - name: Test with coverage + - name: Test with coverage and memory sanitizer if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' working-directory: ${{github.workspace}}/build - run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage + run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage -T MemCheck - - name: Test without coverage + - name: Test without coverage and memory sanitizer if: matrix.build_type == 'Release' || matrix.os != 'ubuntu-latest' working-directory: ${{github.workspace}}/build run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test diff --git a/extern/utils b/extern/utils index f307b10..5882315 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f307b1016df79675b51614ef9a8a79bc9541c721 +Subproject commit 58823156cafd77023db993250091c84bbb50bb00 From 8c660740af15002766c58a2538789aa2202e370d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 28 Mar 2024 03:54:28 +0100 Subject: [PATCH 024/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 5882315..86fd1ab 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 58823156cafd77023db993250091c84bbb50bb00 +Subproject commit 86fd1ab8b7858bec371cba69ae6b8e2239ef6d43 From 67a0b13f50ceecf837a63e896a3e5f4620a753fd Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 28 Mar 2024 11:40:54 +0100 Subject: [PATCH 025/137] Update CMake workflow and submodules --- .github/workflows/cmake.yml | 6 +++--- extern/utils | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d6d0ab7..86e2543 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -20,7 +20,7 @@ jobs: with: submodules: recursive - - name: Install dependencies + - name: Install dependencies (Ubuntu) if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' run: sudo apt-get install -y lcov valgrind @@ -29,8 +29,8 @@ jobs: run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON - name: Configure CMake - if: matrix.build_type == 'Release' - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release + if: matrix.build_type != 'Debug' + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} diff --git a/extern/utils b/extern/utils index 86fd1ab..2c95e86 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 86fd1ab8b7858bec371cba69ae6b8e2239ef6d43 +Subproject commit 2c95e86b9fd0c13db2cabce806cbe352e40748ef From ec4304f0d30d1eecdf0f494a389bfdf6824453af Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 28 Mar 2024 12:01:03 +0100 Subject: [PATCH 026/137] Update extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 2c95e86..0c0b211 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 2c95e86b9fd0c13db2cabce806cbe352e40748ef +Subproject commit 0c0b2113a407f8efabbd5dab63bb2a5e2eb6de46 From 7c7a5e147ed92e73567d18765a4d8707d5b15ad6 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 9 Apr 2024 12:31:12 +0200 Subject: [PATCH 027/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 0c0b211..e616b67 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 0c0b2113a407f8efabbd5dab63bb2a5e2eb6de46 +Subproject commit e616b6790e42cdc02ca14c43de1bb55cecd00beb From 6bfba45f5b4b9910d7d007c8e44facb8709e8ba6 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 9 Apr 2024 12:38:41 +0200 Subject: [PATCH 028/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index e616b67..56972c7 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e616b6790e42cdc02ca14c43de1bb55cecd00beb +Subproject commit 56972c7d24d2b544936398759d20bc4a1289aae6 From 0fc6772fc4c780ff50adc3dcf849168441720e7d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 11 Apr 2024 12:27:10 +0200 Subject: [PATCH 029/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 56972c7..33b7871 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 56972c7d24d2b544936398759d20bc4a1289aae6 +Subproject commit 33b7871868f12168dc1d054bd02b28e5a90e1f42 From dd25d07dd89b38ad781c84a8b1200a7d8bc79bf3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 12 Apr 2024 12:16:07 +0200 Subject: [PATCH 030/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 33b7871..c3d41a1 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 33b7871868f12168dc1d054bd02b28e5a90e1f42 +Subproject commit c3d41a1237d715b43b7b609ab0caa40eee66239e From a0f50eef9fe1311574c7655c3c1ba20238fd6cea Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 15 Apr 2024 17:37:09 +0200 Subject: [PATCH 031/137] Update extern/utils subproject commit hash --- src/json.cpp | 94 +++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index fa8547c..57f0938 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,8 +1,6 @@ #include "json.hpp" #include -inline char get_char(std::istream &is) { return static_cast(is.get()); } - namespace json { json load(std::istream &is) @@ -12,12 +10,12 @@ namespace json { case '{': { // we have a json object.. - get_char(is); + is.get(); std::map vals; is >> std::ws; if (is.peek() == '}') { // we have an empty object.. - get_char(is); + is.get(); return vals; } do @@ -27,31 +25,31 @@ namespace json throw std::invalid_argument("not a valid json"); std::string id = parse_string(is); is >> std::ws; - if (get_char(is) != ':') + if (is.get() != ':') throw std::invalid_argument("not a valid json"); auto val = load(is); vals.emplace(id, std::move(val)); is >> std::ws; - } while (is.peek() == ',' && get_char(is)); - if (get_char(is) != '}') + } while (is.peek() == ',' && is.get()); + if (is.get() != '}') throw std::invalid_argument("not a valid json"); return vals; } case '[': { // we have a json array.. - get_char(is); + is.get(); std::vector vals; if (is.peek() == ']') { // we have an empty array.. - get_char(is); + is.get(); return vals; } do { vals.emplace_back(load(is)); is >> std::ws; - } while (is.peek() == ',' && get_char(is)); - if (get_char(is) != ']') + } while (is.peek() == ',' && is.get()); + if (is.get() != ']') throw std::invalid_argument("not a valid json"); return vals; } @@ -68,36 +66,36 @@ namespace json case '9': { std::string num; - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); if (is.peek() == '.') { - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); if (is.peek() == 'e' || is.peek() == 'E') { - num += get_char(is); + num += is.get(); if (is.peek() == '+') - num += get_char(is); + num += is.get(); if (is.peek() == '-') - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); return json(num, true); } return json(num, true); } else if (is.peek() == 'e' || is.peek() == 'E') { - num += get_char(is); + num += is.get(); if (is.peek() == '+') - num += get_char(is); + num += is.get(); if (is.peek() == '-') - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); return json(num, true); } else @@ -106,47 +104,47 @@ namespace json case '.': { std::string num; - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); if (is.peek() == 'e' || is.peek() == 'E') { - num += get_char(is); + num += is.get(); if (is.peek() == '+') - num += get_char(is); + num += is.get(); if (is.peek() == '-') - num += get_char(is); + num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += get_char(is); + num += is.get(); return json(num, true); } return json(num, true); } case 'f': { // we have a false literal.. - get_char(is); - if (get_char(is) == 'a' && get_char(is) == 'l' && get_char(is) == 's' && get_char(is) == 'e') + is.get(); + if (is.get() == 'a' && is.get() == 'l' && is.get() == 's' && is.get() == 'e') return false; throw std::invalid_argument("not a valid json"); } case 't': { // we have a true literal.. - get_char(is); - if (get_char(is) == 'r' && get_char(is) == 'u' && get_char(is) == 'e') + is.get(); + if (is.get() == 'r' && is.get() == 'u' && is.get() == 'e') return true; throw std::invalid_argument("not a valid json"); } case 'n': { // we have a null literal.. - get_char(is); - switch (get_char(is)) + is.get(); + switch (is.get()) { case 'a': - if (get_char(is) == 'n') + if (is.get() == 'n') return nullptr; throw std::invalid_argument("not a valid json"); case 'u': - if (get_char(is) == 'l' && get_char(is) == 'l') + if (is.get() == 'l' && is.get() == 'l') return nullptr; throw std::invalid_argument("not a valid json"); default: @@ -157,21 +155,21 @@ namespace json return parse_string(is); case '/': { // we have a json comment.. - get_char(is); + is.get(); if (is.peek() == '/') { while (is.peek() != '\n') - get_char(is); + is.get(); return load(is); } else if (is.peek() == '*') { while (is.peek() != '*') - get_char(is); - get_char(is); + is.get(); + is.get(); if (is.peek() == '/') { - get_char(is); + is.get(); return load(is); } else @@ -187,13 +185,13 @@ namespace json std::string parse_string(std::istream &is) { - get_char(is); + is.get(); std::string val; while (is.peek() != '\"') if (is.peek() == '\\') { - get_char(is); - switch (get_char(is)) + is.get(); + switch (is.get()) { case '\"': val += '\"'; @@ -226,7 +224,7 @@ namespace json char c; for (const auto factor : factors) { - c = get_char(is); + c = is.get(); if (c >= '0' && c <= '9') codepoint += static_cast((static_cast(c) - 0x30u) << factor); else if (c >= 'A' && c <= 'F') @@ -267,8 +265,8 @@ namespace json } } else - val += get_char(is); - get_char(is); + val += is.get(); + is.get(); return val; } } // namespace json From 10f6ba969d183c31552a7fcea2c75179b9b2a137 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 19 Apr 2024 03:20:11 +0200 Subject: [PATCH 032/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- include/json.hpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index c3d41a1..1fbfe27 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit c3d41a1237d715b43b7b609ab0caa40eee66239e +Subproject commit 1fbfe272a8c596235f3510295c759314d0b38474 diff --git a/include/json.hpp b/include/json.hpp index d145d14..292870e 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -456,18 +456,42 @@ namespace json std::vector arr_val; }; + /** + * Loads a JSON object from the specified input stream. + * + * @param is The input stream to read the JSON object from. + * @return The loaded JSON object. + */ json load(std::istream &is); + /** + * Loads a JSON object from a C-style string. + * + * @param str The C-style string containing the JSON data. + * @return A JSON object representing the parsed data. + */ inline json load(const char *str) { std::stringstream ss; ss << str; return load(ss); } + /** + * Loads a JSON object from a string. + * + * @param str The string containing the JSON data. + * @return The loaded JSON object. + */ inline json load(const std::string &str) { std::stringstream ss; ss << str; return load(ss); } + /** + * Parses a string from the given input stream. + * + * @param is The input stream to read the string from. + * @return The parsed string. + */ std::string parse_string(std::istream &is); } // namespace json From e111cfff86c925e95d220079378b21b37f87e4aa Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 19 Apr 2024 03:22:13 +0200 Subject: [PATCH 033/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1fbfe27..c3d41a1 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1fbfe272a8c596235f3510295c759314d0b38474 +Subproject commit c3d41a1237d715b43b7b609ab0caa40eee66239e From d23ca9e0a776c44af6f5930fab0a5ff1e44dc0d3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 20 Apr 2024 20:50:04 +0200 Subject: [PATCH 034/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index c3d41a1..24b8cbc 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit c3d41a1237d715b43b7b609ab0caa40eee66239e +Subproject commit 24b8cbcc0b8a51dd8b7a15560bd6e53b0d9d7723 From fd9c18afea474f197b263bfc68950db94a0ec294 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sun, 21 Apr 2024 05:51:19 +0200 Subject: [PATCH 035/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 24b8cbc..76565fe 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 24b8cbcc0b8a51dd8b7a15560bd6e53b0d9d7723 +Subproject commit 76565fee998a300fd596d8fd70f5064e485b2f64 From 47c22217263423dcac65022f5384e7b200583d08 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sun, 21 Apr 2024 08:14:52 +0200 Subject: [PATCH 036/137] Update dependencies installation command in .github/workflows/cmake.yml --- .github/workflows/cmake.yml | 4 +++- extern/utils | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 86e2543..da7beee 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -22,7 +22,9 @@ jobs: - name: Install dependencies (Ubuntu) if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' - run: sudo apt-get install -y lcov valgrind + run: | + sudo apt-get update + sudo apt-get install -y lcov valgrind - name: Configure CMake (Debug) if: matrix.build_type == 'Debug' diff --git a/extern/utils b/extern/utils index 76565fe..887109d 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 76565fee998a300fd596d8fd70f5064e485b2f64 +Subproject commit 887109d7655b90d7b114ee84d0225eaeb66829e1 From c43ba8bc61a999ae2031f7508d66824cbbda7cca Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 22 Apr 2024 12:22:38 +0200 Subject: [PATCH 037/137] Refactor CMakeLists.txt to conditionally add the utils subdirectory --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0097ef9..d588d2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") include(CTest) enable_testing() -option(JSON_INCLUDE_UTILS "Include utils library" ON) -if(JSON_INCLUDE_UTILS) +if(NOT TARGET utils) add_subdirectory(extern/utils) endif() From f0acd66bf4f81ea58f8a26f4a6c566e3d7701cc6 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 22 Apr 2024 12:30:43 +0200 Subject: [PATCH 038/137] Refactor CMakeLists.txt to conditionally add the utils subdirectory and update dependencies installation command in .github/workflows/cmake.yml --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d588d2b..fa8a669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,14 +5,14 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") include(CTest) enable_testing() -if(NOT TARGET utils) - add_subdirectory(extern/utils) -endif() - add_library(json src/json.cpp) add_dependencies(json utils) target_compile_features(json PUBLIC cxx_std_17) target_include_directories(json PUBLIC $) +if(NOT TARGET utils) + add_subdirectory(extern/utils) + add_dependencies(json utils) +endif() target_link_libraries(json PUBLIC utils) if(BUILD_TESTING) From a450eda2e17da8bbbfe22b708440a83f91ed1030 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 22 Apr 2024 15:12:30 +0200 Subject: [PATCH 039/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 887109d..76b687a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 887109d7655b90d7b114ee84d0225eaeb66829e1 +Subproject commit 76b687a811aec076f664bbe216b49b24579b1d96 From 5fbf126898ae974c2a1b136fafc1ca0d6cc6113a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 22 Apr 2024 15:16:10 +0200 Subject: [PATCH 040/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 76b687a..d79cb81 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 76b687a811aec076f664bbe216b49b24579b1d96 +Subproject commit d79cb8146125a7dac89c28e7c01fa62ccf5e852d From 5a558edc4dcafec51e3b9c6ea882421128492b8d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 22 Apr 2024 15:35:43 +0200 Subject: [PATCH 041/137] Update extern/utils subproject commit hash --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index d79cb81..e666c7a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit d79cb8146125a7dac89c28e7c01fa62ccf5e852d +Subproject commit e666c7a6e69ea56d6dff5c6a34cfad66c7e78710 From 77a8b43739a15c141accd18e1bc4789c73fa4778 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 6 May 2024 13:31:39 +0200 Subject: [PATCH 042/137] Refactor json.hpp and json.cpp to improve code organization and readability --- include/json.hpp | 8 +++++--- src/json.cpp | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 292870e..da5cb6a 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -37,18 +37,18 @@ namespace json json(std::initializer_list list) : type(json_type::array) { if (list.size() == 2 && list.begin()->type == json_type::string) - { + { // key-value pair type = json_type::object; obj_val.emplace(list.begin()->str_val, *(list.begin() + 1)); } else if (list.begin()->type == json_type::object) - { + { // list of key-value pairs type = json_type::object; for (auto &p : list) for (auto &q : p.obj_val) obj_val.emplace(q.first, q.second); } - else + else // list of values for (auto &p : list) arr_val.emplace_back(p); } @@ -456,6 +456,8 @@ namespace json std::vector arr_val; }; + json to_list(std::vector &&arr); + /** * Loads a JSON object from the specified input stream. * diff --git a/src/json.cpp b/src/json.cpp index 57f0938..41fa2ab 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -3,6 +3,14 @@ namespace json { + json to_list(const std::vector &vals) + { + json j(json_type::array); + for (const auto &val : vals) + j.push_back(val); + return j; + } + json load(std::istream &is) { is >> std::ws; // we remove all the leading whitespace.. From 00968122d412052e7ae69a0b9814b82046d5f536 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 6 May 2024 13:32:19 +0200 Subject: [PATCH 043/137] Refactor json.hpp and json.cpp for improved code organization and readability --- include/json.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/json.hpp b/include/json.hpp index da5cb6a..5c4f829 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -456,6 +456,12 @@ namespace json std::vector arr_val; }; + /** + * Converts a list of JSON objects to a JSON array. + * + * @param arr The list of JSON objects to convert. + * @return A JSON array containing the specified objects. + */ json to_list(std::vector &&arr); /** From 5b6a5c7a9f823c5e0d96201d1ab9b6872864cee1 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 6 May 2024 14:59:58 +0200 Subject: [PATCH 044/137] Refactor to_array function in json.hpp and json.cpp for improved code organization and readability --- include/json.hpp | 2 +- src/json.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 5c4f829..d9e0067 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -462,7 +462,7 @@ namespace json * @param arr The list of JSON objects to convert. * @return A JSON array containing the specified objects. */ - json to_list(std::vector &&arr); + json to_array(std::vector &&arr); /** * Loads a JSON object from the specified input stream. diff --git a/src/json.cpp b/src/json.cpp index 41fa2ab..114bfb9 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -3,11 +3,11 @@ namespace json { - json to_list(const std::vector &vals) + json to_array(std::vector &&vals) { json j(json_type::array); - for (const auto &val : vals) - j.push_back(val); + for (auto &val : vals) + j.push_back(std::move(val)); return j; } From 1589eb4a14c21cc92f2abd2b36a57c4ab6ef747c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 12:35:49 +0200 Subject: [PATCH 045/137] Refactor test_json.cpp for using variant --- include/json.hpp | 641 ++++++++++++++++------------------------ src/json.cpp | 200 ++++++------- src/jsonConfig.cmake.in | 7 - tests/test_json.cpp | 36 ++- 4 files changed, 380 insertions(+), 504 deletions(-) delete mode 100644 src/jsonConfig.cmake.in diff --git a/include/json.hpp b/include/json.hpp index d9e0067..8046ea8 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -1,10 +1,9 @@ #pragma once #include +#include #include #include -#include -#include namespace json { @@ -20,486 +19,366 @@ namespace json class json { - private: - json(std::map &obj) : type(json_type::object), obj_val(std::move(obj)) {} - json(std::vector &arr) : type(json_type::array), arr_val(std::move(arr)) {} - public: - json(json_type type = json_type::object) : type(type) {} - json(std::nullptr_t) : type(json_type::null) {} - json(const std::string &str, bool is_number = false) : type(is_number ? json_type::number : json_type::string), str_val(str) {} - json(const char *str, bool is_number = false) : type(is_number ? json_type::number : json_type::string), str_val(str) {} - json(bool b) : type(json_type::boolean), bool_val(b) {} - template ::value, T>::type> - json(T t) : type(json_type::number), str_val(std::to_string(t)) {} - json(std::map &&obj) : type(json_type::object), obj_val(std::move(obj)) {} - json(std::vector &&arr) : type(json_type::array), arr_val(std::move(arr)) {} - json(std::initializer_list list) : type(json_type::array) + json(json_type type = json_type::object) noexcept { - if (list.size() == 2 && list.begin()->type == json_type::string) - { // key-value pair - type = json_type::object; - obj_val.emplace(list.begin()->str_val, *(list.begin() + 1)); + switch (type) + { + case json_type::null: + value = nullptr; + break; + case json_type::boolean: + value = false; + break; + case json_type::number: + value = 0; + break; + case json_type::string: + value = std::string(); + break; + case json_type::array: + value = std::vector(); + break; + case json_type::object: + value = std::map(); + break; } - else if (list.begin()->type == json_type::object) - { // list of key-value pairs - type = json_type::object; - for (auto &p : list) - for (auto &q : p.obj_val) - obj_val.emplace(q.first, q.second); + } + json(const json &other) noexcept : value(other.value) {} + json(json &&other) noexcept : value(std::move(other.value)) { other.value = nullptr; } + json(std::nullptr_t) noexcept : value(nullptr) {} + json(bool b) noexcept : value(b) {} + json(int l) noexcept : value(l) {} + json(int64_t l) noexcept : value(l) {} + json(uint64_t l) noexcept : value(l) {} + json(double d) noexcept : value(d) {} + json(const std::string &str) noexcept : value(str) {} + json(const char *str) noexcept : value(std::string(str)) {} + json(std::initializer_list init) + { + if (init.size() == 2 && init.begin()->get_type() == json_type::string) + { + value = std::map(); + operator[](static_cast(*init.begin())) = *(init.begin() + 1); + } + else if (init.begin()->get_type() == json_type::object) + { + value = std::map(); + for (auto &obj : init) + for (auto &[key, value] : obj.as_object()) + operator[](key) = value; } - else // list of values - for (auto &p : list) - arr_val.emplace_back(p); + else + value = std::vector(init); } - json(const json &other) : type(other.type), bool_val(other.bool_val), str_val(other.str_val), obj_val(other.obj_val), arr_val(other.arr_val) {} - json(json &&other) : type(other.type), bool_val(other.bool_val), str_val(std::move(other.str_val)), obj_val(std::move(other.obj_val)), arr_val(std::move(other.arr_val)) { other.type = json_type::null; } - json &operator=(const json &other) + json &operator=(const json &other) noexcept { - type = other.type; - bool_val = other.bool_val; - str_val = other.str_val; - obj_val = other.obj_val; - arr_val = other.arr_val; + value = other.value; return *this; } - json &operator=(json &&other) + + json &operator=(json &&other) noexcept { - type = other.type; - bool_val = other.bool_val; - str_val = std::move(other.str_val); - obj_val = std::move(other.obj_val); - arr_val = std::move(other.arr_val); + value = std::move(other.value); return *this; } - json &operator=(const std::string &str) + + json &operator=(std::nullptr_t) noexcept { - set_type(json_type::string); - str_val = str; + value = nullptr; return *this; } - json &operator=(const char *str) + + json &operator=(const std::string &str) noexcept { - set_type(json_type::string); - str_val = str; + value = str; return *this; } - json &operator=(bool b) + + json &operator=(const char *str) noexcept { - set_type(json_type::boolean); - bool_val = b; + value = std::string(str); return *this; } - template ::value, T>::type> - json &operator=(T t) + + json &operator=(bool b) noexcept { - set_type(json_type::number); - str_val = std::to_string(t); + value = b; return *this; } - json &operator=(std::nullptr_t) + + json &operator=(int l) noexcept { - set_type(json_type::null); + value = l; return *this; } - bool has(const std::string &key) const { return type == json_type::object && obj_val.find(key) != obj_val.end(); } - bool has(const char *key) const { return type == json_type::object && obj_val.find(key) != obj_val.end(); } - bool has(size_t index) const { return type == json_type::array && index < arr_val.size(); } - - json &operator[](const std::string &key) + json &operator=(int64_t l) noexcept { - set_type(json_type::object); - return obj_val[key]; - } - const json &operator[](const std::string &key) const { return obj_val.at(key); } - json &operator[](const char *key) - { - set_type(json_type::object); - return obj_val[key]; + value = l; + return *this; } - const json &operator[](const char *key) const { return obj_val.at(key); } - json &operator[](int index) + + json &operator=(uint64_t l) noexcept { - set_type(json_type::array); - return arr_val[index]; + value = l; + return *this; } - const json &operator[](int index) const { return arr_val[index]; } - json &operator[](size_t index) + + json &operator=(double d) noexcept { - set_type(json_type::array); - return arr_val[index]; + value = d; + return *this; } - const json &operator[](size_t index) const { return arr_val.at(index); } - bool operator==(const json &other) const + json &operator[](const char *str) { return operator[](std::string(str)); } + const json &operator[](const char *str) const { return operator[](std::string(str)); } + + json &operator[](const std::string &key) { return std::get>(value)[key]; } + const json &operator[](const std::string &key) const { return std::get>(value).at(key); } + + json &operator[](int index) { return operator[](static_cast(index)); } + const json &operator[](int index) const { return operator[](static_cast(index)); } + + json &operator[](size_t index) { return std::get>(value)[index]; } + const json &operator[](size_t index) const { return std::get>(value).at(index); } + + json_type get_type() const noexcept { - if (type != other.type) - return false; - switch (type) + switch (value.index()) { - case json_type::null: - return true; - case json_type::string: - return str_val == other.str_val; - case json_type::number: - return str_val == other.str_val; - case json_type::boolean: - return bool_val == other.bool_val; - case json_type::array: - return arr_val == other.arr_val; - case json_type::object: - return obj_val == other.obj_val; - default: - return false; + case 0: + return json_type::null; + case 1: + return json_type::boolean; + case 2: + case 3: + case 4: + return json_type::number; + case 5: + return json_type::string; + case 6: + return json_type::object; + case 7: + return json_type::array; + default: // should never happen + return json_type::null; } } - bool operator!=(const json &other) const { return !(*this == other); } - - bool operator==(const std::string &str) const { return type == json_type::string && str_val == str; } - bool operator!=(const std::string &str) const { return !(*this == str); } - bool operator==(const char *str) const { return type == json_type::string && str_val == str; } - bool operator!=(const char *str) const { return !(*this == str); } - bool operator==(bool b) const { return type == json_type::boolean && bool_val == b; } - bool operator!=(bool b) const { return type != json_type::boolean || bool_val != b; } - bool operator==(int i) const { return type == json_type::number && std::stoi(str_val) == i; } - bool operator!=(int i) const { return !(*this == i); } - bool operator==(double d) const { return type == json_type::number && std::stod(str_val) == d; } - bool operator!=(double d) const { return !(*this == d); } - bool operator==(long l) const { return type == json_type::number && std::stol(str_val) == l; } - bool operator!=(long l) const { return !(*this == l); } - bool operator==(unsigned long l) const { return type == json_type::number && std::stoul(str_val) == l; } - bool operator!=(unsigned long l) const { return !(*this == l); } - bool operator==(std::nullptr_t) const { return type == json_type::null; } - bool operator!=(std::nullptr_t) const { return type != json_type::null; } - - json_type get_type() const { return type; } + size_t size() const { - switch (type) + switch (get_type()) { - case json_type::null: - return 0; - case json_type::string: - return str_val.size(); - case json_type::number: - return str_val.size(); - case json_type::boolean: - return 1; case json_type::array: - return arr_val.size(); + return std::get>(value).size(); case json_type::object: - return obj_val.size(); + return std::get>(value).size(); default: return 0; } } - operator bool() const + bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>(value).count(key) > 0; } + + bool operator==(const json &other) const noexcept { return value == other.value; } + bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } + bool operator==(bool b) const noexcept { return value.index() == 1 && std::get(value) == b; } + bool operator==(int l) const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return false; - case json_type::string: - return !str_val.empty(); - case json_type::number: - return str_val != "0"; - case json_type::boolean: - return bool_val; - case json_type::array: - return !arr_val.empty(); - case json_type::object: - return !obj_val.empty(); + case 1: + return std::get(value) == l; + case 2: + return std::get(value) == l; + case 3: + return std::get(value) == static_cast(l); + case 4: + return std::get(value) == l; default: return false; } } - operator int() const + bool operator==(int64_t l) const noexcept { return value.index() == 2 && std::get(value) == l; } + bool operator==(uint64_t l) const noexcept { return value.index() == 3 && std::get(value) == l; } + bool operator==(double d) const noexcept { return value.index() == 4 && std::get(value) == d; } + bool operator==(const std::string &str) const noexcept { return value.index() == 5 && std::get(value) == str; } + bool operator==(const char *str) const noexcept { return value.index() == 5 && std::get(value) == str; } + + bool operator!=(const json &other) const noexcept { return value != other.value; } + bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } + bool operator!=(bool b) const noexcept { return value.index() != 1 || std::get(value) != b; } + bool operator!=(int l) const noexcept { return !(*this == l); } + bool operator!=(int64_t l) const noexcept { return value.index() != 2 || std::get(value) != l; } + bool operator!=(uint64_t l) const noexcept { return value.index() != 3 || std::get(value) != l; } + bool operator!=(double d) const noexcept { return value.index() != 4 || std::get(value) != d; } + bool operator!=(const std::string &str) const noexcept { return value.index() != 5 || std::get(value) != str; } + bool operator!=(const char *str) const noexcept { return value.index() != 5 || std::get(value) != str; } + + operator bool() const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return 0; - case json_type::string: - return std::stoi(str_val); - case json_type::number: - return std::stoi(str_val); - case json_type::boolean: - return bool_val ? 1 : 0; - case json_type::array: - return static_cast(arr_val.size()); - case json_type::object: - return static_cast(obj_val.size()); + case 1: + return std::get(value); + case 2: + return std::get(value) != 0; + case 3: + return std::get(value) != 0; + case 4: + return !std::get(value); + case 5: + return !std::get(value).empty(); + case 6: + return !std::get>(value).empty(); + case 7: + return !std::get>(value).empty(); default: - return 0; + return false; } } - operator double() const + + operator int64_t() const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return 0; - case json_type::string: - return std::stod(str_val); - case json_type::number: - return std::stod(str_val); - case json_type::boolean: - return bool_val ? 1 : 0; - case json_type::array: - return static_cast(arr_val.size()); - case json_type::object: - return static_cast(obj_val.size()); + case 1: + return std::get(value) ? 1 : 0; + case 2: + return std::get(value); + case 3: + return std::get(value); + case 4: + return static_cast(std::get(value)); default: return 0; } } - operator long() const + + operator uint64_t() const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return 0; - case json_type::string: - return std::stol(str_val); - case json_type::number: - return std::stol(str_val); - case json_type::boolean: - return bool_val ? 1 : 0; - case json_type::array: - return static_cast(arr_val.size()); - case json_type::object: - return static_cast(obj_val.size()); + case 1: + return std::get(value) ? 1 : 0; + case 2: + return std::get(value); + case 3: + return std::get(value); + case 4: + return static_cast(std::get(value)); default: return 0; } } - operator unsigned long() const + + operator double() const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return 0; - case json_type::string: - return std::stoul(str_val); - case json_type::number: - return std::stoul(str_val); - case json_type::boolean: - return bool_val ? 1 : 0; - case json_type::array: - return static_cast(arr_val.size()); - case json_type::object: - return static_cast(obj_val.size()); + case 1: + return std::get(value) ? 1.0 : 0.0; + case 2: + return static_cast(std::get(value)); + case 3: + return static_cast(std::get(value)); + case 4: + return std::get(value); default: - return 0; + return 0.0; } } - operator std::string() const + + operator std::string() const noexcept { - switch (type) + switch (value.index()) { - case json_type::null: - return ""; - case json_type::string: - return str_val; - case json_type::number: - return str_val; - case json_type::boolean: - return bool_val ? "true" : "false"; - case json_type::array: - return std::to_string(arr_val.size()); - case json_type::object: - return std::to_string(obj_val.size()); + case 1: + return std::get(value) ? "true" : "false"; + case 2: + return std::to_string(std::get(value)); + case 3: + return std::to_string(std::get(value)); + case 4: + return std::to_string(std::get(value)); + case 5: + return std::get(value); default: return ""; } } - operator std::map() const - { - switch (type) - { - case json_type::null: - return {}; - case json_type::string: - return {}; - case json_type::number: - return {}; - case json_type::boolean: - return {}; - case json_type::array: - return {}; - case json_type::object: - return obj_val; - default: - return {}; - } - } - operator std::vector() const - { - switch (type) - { - case json_type::null: - return {}; - case json_type::string: - return {}; - case json_type::number: - return {}; - case json_type::boolean: - return {}; - case json_type::array: - return arr_val; - case json_type::object: - return {}; - default: - return {}; - } - } - std::map &get_object() - { - set_type(json_type::object); - return obj_val; - } - const std::map &get_object() const { return obj_val; } - std::vector &get_array() - { - set_type(json_type::array); - return arr_val; - } - const std::vector &get_array() const { return arr_val; } + operator std::map() const noexcept { return value.index() == 6 ? std::get>(value) : std::map(); } + const std::map &as_object() const noexcept { return std::get>(value); } - void set(const std::string &key, json &&j) - { - set_type(json_type::object); - obj_val[key] = std::move(j); - } + operator std::vector() const noexcept { return value.index() == 7 ? std::get>(value) : std::vector(); } + const std::vector &as_array() const noexcept { return std::get>(value); } - void push_back(json &&j) - { - set_type(json_type::array); - arr_val.push_back(std::move(j)); - } + void clear() noexcept { value = nullptr; } - std::string to_string() const - { - std::stringstream ss; - dump(ss); - return ss.str(); - } + void push_back(const json &val) { std::get>(value).push_back(val); } + void push_back(json &&val) { std::get>(value).push_back(std::move(val)); } - friend std::ostream &operator<<(std::ostream &os, const json &j) - { - j.dump(os); - return os; - } + void erase(const std::string &key) { std::get>(value).erase(key); } - private: - void set_type(json_type tp) - { - if (tp != type) - { - bool_val = false; - str_val = tp == json_type::string ? "" : "0"; - obj_val.clear(); - arr_val.clear(); - type = tp; - } - } + void erase(size_t index) { std::get>(value).erase(std::get>(value).begin() + index); } - void dump(std::ostream &os) const + std::string dump() const { - switch (type) + switch (value.index()) { - case json_type::null: - os << "null"; - break; - case json_type::string: - os << "\"" << str_val << "\""; - break; - case json_type::number: - os << str_val; - break; - case json_type::boolean: - os << (bool_val ? "true" : "false"); - break; - case json_type::array: - os << "["; - for (size_t i = 0; i < arr_val.size(); i++) + case 0: + return "null"; + case 1: + return std::get(value) ? "true" : "false"; + case 2: + return std::to_string(std::get(value)); + case 3: + return std::to_string(std::get(value)); + case 4: + return std::to_string(std::get(value)); + case 5: + return "\"" + std::get(value) + "\""; + case 6: + { + std::string str = "{"; + const auto &m = std::get>(value); + for (auto it = m.begin(); it != m.end(); ++it) { - arr_val[i].dump(os); - if (i != arr_val.size() - 1) - os << ","; + if (it != m.begin()) + str += ","; + str += "\"" + it->first + "\":" + it->second.dump(); } - os << "]"; - break; - case json_type::object: - os << "{"; - for (auto it = obj_val.begin(); it != obj_val.end(); it++) + return str + "}"; + } + case 7: + { + std::string str = "["; + const auto &v = std::get>(value); + for (size_t i = 0; i < v.size(); ++i) { - os << "\"" << it->first << "\":"; - it->second.dump(os); - if (it != --obj_val.end()) - os << ","; + if (i != 0) + str += ","; + str += v[i].dump(); } - os << "}"; - break; + return str + "]"; + } + default: + return ""; } } + friend std::ostream &operator<<(std::ostream &os, const json &j) { return os << j.dump(); } + private: - json_type type; - bool bool_val = false; - std::string str_val; - std::map obj_val; - std::vector arr_val; + std::variant, std::vector> value; }; - /** - * Converts a list of JSON objects to a JSON array. - * - * @param arr The list of JSON objects to convert. - * @return A JSON array containing the specified objects. - */ - json to_array(std::vector &&arr); - - /** - * Loads a JSON object from the specified input stream. - * - * @param is The input stream to read the JSON object from. - * @return The loaded JSON object. - */ json load(std::istream &is); - /** - * Loads a JSON object from a C-style string. - * - * @param str The C-style string containing the JSON data. - * @return A JSON object representing the parsed data. - */ - inline json load(const char *str) - { - std::stringstream ss; - ss << str; - return load(ss); - } - /** - * Loads a JSON object from a string. - * - * @param str The string containing the JSON data. - * @return The loaded JSON object. - */ - inline json load(const std::string &str) - { - std::stringstream ss; - ss << str; - return load(ss); - } - /** - * Parses a string from the given input stream. - * - * @param is The input stream to read the string from. - * @return The parsed string. - */ - std::string parse_string(std::istream &is); + + json load(const char *str); + + inline json load(const std::string &str) { return load(str.c_str()); } } // namespace json diff --git a/src/json.cpp b/src/json.cpp index 114bfb9..fe370a2 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,14 +1,94 @@ #include "json.hpp" #include +#include namespace json { - json to_array(std::vector &&vals) + std::string parse_string(std::istream &is) { - json j(json_type::array); - for (auto &val : vals) - j.push_back(std::move(val)); - return j; + is.get(); + std::string val; + while (is.peek() != '\"') + if (is.peek() == '\\') + { + is.get(); + switch (is.get()) + { + case '\"': + val += '\"'; + break; + case '\\': + val += '\\'; + break; + case '/': + val += '/'; + break; + case 'b': + val += '\b'; + break; + case 'f': + val += '\f'; + break; + case 'n': + val += '\n'; + break; + case 'r': + val += '\r'; + break; + case 't': + val += '\t'; + break; + case 'u': + { + int codepoint = 0; + const auto factors = {12u, 8u, 4u, 0u}; + char c; + for (const auto factor : factors) + { + c = is.get(); + if (c >= '0' && c <= '9') + codepoint += static_cast((static_cast(c) - 0x30u) << factor); + else if (c >= 'A' && c <= 'F') + codepoint += static_cast((static_cast(c) - 0x37u) << factor); + else if (c >= 'a' && c <= 'f') + codepoint += static_cast((static_cast(c) - 0x57u) << factor); + else + throw std::invalid_argument("not a valid json"); + } + + // translate codepoint into bytes + if (codepoint < 0x80) + { // 1-byte characters: 0xxxxxxx (ASCII) + val.push_back(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { // 2-byte characters: 110xxxxx 10xxxxxx + val.push_back(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + val.push_back(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + val.push_back(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + break; + } + default: + throw std::invalid_argument("not a valid json"); + } + } + else + val += is.get(); + is.get(); + return val; } json load(std::istream &is) @@ -19,7 +99,7 @@ namespace json case '{': { // we have a json object.. is.get(); - std::map vals; + json vals; is >> std::ws; if (is.peek() == '}') { // we have an empty object.. @@ -36,7 +116,7 @@ namespace json if (is.get() != ':') throw std::invalid_argument("not a valid json"); auto val = load(is); - vals.emplace(id, std::move(val)); + vals[id] = std::move(val); is >> std::ws; } while (is.peek() == ',' && is.get()); if (is.get() != '}') @@ -46,7 +126,7 @@ namespace json case '[': { // we have a json array.. is.get(); - std::vector vals; + json vals(json_type::array); if (is.peek() == ']') { // we have an empty array.. is.get(); @@ -54,7 +134,7 @@ namespace json } do { - vals.emplace_back(load(is)); + vals.push_back(load(is)); is >> std::ws; } while (is.peek() == ',' && is.get()); if (is.get() != ']') @@ -91,9 +171,9 @@ namespace json num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') num += is.get(); - return json(num, true); + return json(std::stod(num)); } - return json(num, true); + return json(std::stod(num)); } else if (is.peek() == 'e' || is.peek() == 'E') { @@ -104,10 +184,10 @@ namespace json num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') num += is.get(); - return json(num, true); + return json(std::stod(num)); } else - return json(num, true); + return json(std::stol(num)); } case '.': { @@ -124,9 +204,9 @@ namespace json num += is.get(); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') num += is.get(); - return json(num, true); + return json(std::stod(num)); } - return json(num, true); + return json(std::stod(num)); } case 'f': { // we have a false literal.. @@ -191,90 +271,10 @@ namespace json } } - std::string parse_string(std::istream &is) + json load(const char *str) { - is.get(); - std::string val; - while (is.peek() != '\"') - if (is.peek() == '\\') - { - is.get(); - switch (is.get()) - { - case '\"': - val += '\"'; - break; - case '\\': - val += '\\'; - break; - case '/': - val += '/'; - break; - case 'b': - val += '\b'; - break; - case 'f': - val += '\f'; - break; - case 'n': - val += '\n'; - break; - case 'r': - val += '\r'; - break; - case 't': - val += '\t'; - break; - case 'u': - { - int codepoint = 0; - const auto factors = {12u, 8u, 4u, 0u}; - char c; - for (const auto factor : factors) - { - c = is.get(); - if (c >= '0' && c <= '9') - codepoint += static_cast((static_cast(c) - 0x30u) << factor); - else if (c >= 'A' && c <= 'F') - codepoint += static_cast((static_cast(c) - 0x37u) << factor); - else if (c >= 'a' && c <= 'f') - codepoint += static_cast((static_cast(c) - 0x57u) << factor); - else - throw std::invalid_argument("not a valid json"); - } - - // translate codepoint into bytes - if (codepoint < 0x80) - { // 1-byte characters: 0xxxxxxx (ASCII) - val.push_back(static_cast(codepoint)); - } - else if (codepoint <= 0x7FF) - { // 2-byte characters: 110xxxxx 10xxxxxx - val.push_back(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else if (codepoint <= 0xFFFF) - { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - val.push_back(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else - { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - val.push_back(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - val.push_back(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - val.push_back(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - break; - } - default: - throw std::invalid_argument("not a valid json"); - } - } - else - val += is.get(); - is.get(); - return val; + std::stringstream ss; + ss << str; + return load(ss); } -} // namespace json +} // namespace json \ No newline at end of file diff --git a/src/jsonConfig.cmake.in b/src/jsonConfig.cmake.in deleted file mode 100644 index 12d6f58..0000000 --- a/src/jsonConfig.cmake.in +++ /dev/null @@ -1,7 +0,0 @@ -set(JSON_VERSION 0.1.0) - -@PACKAGE_INIT@ - -set_and_check(JSON_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@/json") - -check_required_components(json) \ No newline at end of file diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 88cf953..52ebeda 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -95,8 +95,8 @@ void test_initializer_lists() void test_json_comparison() { - json::json j0 = nullptr; - json::json j1 = nullptr; + json::json j0; + json::json j1; assert(j0 == j1); j0["a"] = 1; @@ -113,9 +113,11 @@ void test_json_comparison() assert(j0 == j1); + j0["f"] = json::json_type::array; j0["f"].push_back(1); j0["f"].push_back(2); + j1["f"] = json::json_type::array; j1["f"].push_back(1); j1["f"].push_back(2); @@ -127,12 +129,13 @@ void test_json_comparison() void test_move_semantics() { - json::json j0 = nullptr; + json::json j0; j0["a"] = 1; j0["b"] = 2.0; j0["c"] = "3"; j0["d"] = true; j0["e"] = nullptr; + j0["f"] = json::json_type::array; j0["f"].push_back(1); j0["f"].push_back(2); j0["f"].push_back(3); @@ -152,12 +155,12 @@ void test_move_semantics() void test_move_into_array() { - json::json j0; + json::json j0 = json::json_type::array; j0.push_back(1); j0.push_back(2); j0.push_back(3); - json::json j1; + json::json j1 = json::json_type::array; j1.push_back(std::move(j0)); assert(j0 == nullptr); @@ -169,26 +172,27 @@ void test_move_into_array() void test_iterate() { - json::json j0 = nullptr; + json::json j0; j0["a"] = 1; j0["b"] = 2.0; j0["c"] = "3"; j0["d"] = true; j0["e"] = nullptr; + j0["f"] = json::json_type::array; j0["f"].push_back(1); j0["f"].push_back(2); j0["f"].push_back(3); - std::map &m = j0.get_object(); + auto &m = j0.as_object(); for ([[maybe_unused]] auto &[key, value] : m) LOG_INFO("key " << key << " value " << value); - json::json j1 = nullptr; + json::json j1 = json::json_type::array; j1.push_back(1); j1.push_back(2); j1.push_back(3); - for ([[maybe_unused]] auto &value : j1.get_array()) + for ([[maybe_unused]] auto &value : j1.as_array()) LOG_INFO("value " << value); } @@ -197,36 +201,36 @@ void test_null() json::json j0 = nullptr; assert(j0 == nullptr); assert(j0.get_type() == json::json_type::null); - assert(j0.to_string() == "null"); + assert(j0.dump() == "null"); } void test_empty_array() { json::json j0 = json::json_type::array; assert(j0.get_type() == json::json_type::array); - assert(j0.to_string() == "[]"); + assert(j0.dump() == "[]"); } void test_empty_object() { json::json j0 = json::json_type::object; assert(j0.get_type() == json::json_type::object); - assert(j0.to_string() == "{}"); + assert(j0.dump() == "{}"); } void test_scientific_numbers() { json::json j0 = 1e+10; assert(j0.get_type() == json::json_type::number); - assert(j0.to_string() == std::to_string(1e+10)); + assert(j0.dump() == std::to_string(1e+10)); json::json j1 = 1.23e+10; assert(j1.get_type() == json::json_type::number); - assert(j1.to_string() == std::to_string(1.23e+10)); + assert(j1.dump() == std::to_string(1.23e+10)); json::json j2 = .23e+10; assert(j2.get_type() == json::json_type::number); - assert(j2.to_string() == std::to_string(.23e+10)); + assert(j2.dump() == std::to_string(.23e+10)); } void test_array_of_scientific_numbers() @@ -236,7 +240,7 @@ void test_array_of_scientific_numbers() j0.push_back(1.23e+10); j0.push_back(.23e+10); assert(j0.get_type() == json::json_type::array); - assert(j0.to_string() == "[" + std::to_string(1e+10) + "," + std::to_string(1.23e+10) + "," + std::to_string(.23e+10) + "]"); + assert(j0.dump() == "[" + std::to_string(1e+10) + "," + std::to_string(1.23e+10) + "," + std::to_string(.23e+10) + "]"); } int main(int, char **) From 9819032c14b5d652aa2aabc433673e0753267893 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 12:55:14 +0200 Subject: [PATCH 046/137] Refactor code for improved organization and readability --- include/json.hpp | 561 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) diff --git a/include/json.hpp b/include/json.hpp index 8046ea8..7e3b28a 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -17,9 +17,28 @@ namespace json object }; + /** + * @brief A class representing a JSON value. + * + * The `json` class is a versatile container that can hold different types of JSON values, including null, boolean, number, string, array, and object. + * It provides various member functions and overloaded operators for accessing and manipulating JSON values. + */ class json { public: + /** + * @brief Constructs a JSON object with the specified type. + * + * This constructor initializes a JSON object with the specified type. The type can be one of the following: + * - json_type::null: Initializes the JSON object with a null value. + * - json_type::boolean: Initializes the JSON object with a boolean value (false). + * - json_type::number: Initializes the JSON object with a numeric value (0). + * - json_type::string: Initializes the JSON object with an empty string. + * - json_type::array: Initializes the JSON object with an empty array. + * - json_type::object: Initializes the JSON object with an empty object. + * + * @param type The type of the JSON object to be constructed. + */ json(json_type type = json_type::object) noexcept { switch (type) @@ -44,16 +63,102 @@ namespace json break; } } + /** + * @brief Copy constructor for the json class. + * + * This constructor creates a new json object by copying the contents of another json object. + * + * @param other The json object to be copied. + */ json(const json &other) noexcept : value(other.value) {} + /** + * @brief Move constructor for the json class. + * + * This constructor moves the contents of the `other` object into the newly created `json` object. + * The `other` object is left in a valid but unspecified state. + * + * @param other The `json` object to be moved from. + */ json(json &&other) noexcept : value(std::move(other.value)) { other.value = nullptr; } + /** + * @brief Constructs a `json` object from a `nullptr_t`. + * + * This constructor creates a `json` object with a null value. + * + * @param[in] nullptr_t A null pointer constant. + */ json(std::nullptr_t) noexcept : value(nullptr) {} + /** + * @brief Constructs a `json` object from a boolean value. + * + * This constructor initializes a `json` object with the given boolean value. + * + * @param b The boolean value to initialize the `json` object with. + */ json(bool b) noexcept : value(b) {} + /** + * @brief Constructs a `json` object with an integer value. + * + * This constructor initializes a `json` object with the given integer value. + * + * @param l The integer value to initialize the `json` object with. + */ json(int l) noexcept : value(l) {} + /** + * @brief Constructs a `json` object from an int64_t value. + * + * This constructor initializes a `json` object with the given int64_t value. + * + * @param l The int64_t value to initialize the `json` object with. + */ json(int64_t l) noexcept : value(l) {} + /** + * @brief Constructs a `json` object with an unsigned 64-bit integer value. + * + * This constructor initializes a `json` object with the provided unsigned 64-bit integer value. + * + * @param l The unsigned 64-bit integer value to initialize the `json` object with. + */ json(uint64_t l) noexcept : value(l) {} + /** + * @brief Constructs a `json` object from a double value. + * + * This constructor initializes a `json` object with the given double value. + * + * @param d The double value to initialize the `json` object with. + */ json(double d) noexcept : value(d) {} + /** + * @brief Constructs a `json` object from a string. + * + * This constructor initializes a `json` object with the given string. + * + * @param str The string to initialize the `json` object with. + */ json(const std::string &str) noexcept : value(str) {} + /** + * @brief Constructs a `json` object from a C-style string. + * + * This constructor creates a `json` object by converting the given C-style string into a `std::string`. + * + * @param str The C-style string to be converted into a `json` object. + */ json(const char *str) noexcept : value(std::string(str)) {} + /** + * @brief Constructs a `json` object from an initializer list. + * + * This constructor allows initializing a `json` object using an initializer list. + * It supports two types of initializer lists: + * 1. If the initializer list contains two elements and the first element is a string, + * it constructs a JSON object with a single key-value pair, where the key is the string + * and the value is the second element of the initializer list. + * 2. If the initializer list contains one or more JSON objects, it constructs a JSON object + * by merging all the key-value pairs from the objects in the initializer list. + * If there are duplicate keys, the value from the last object in the list will be used. + * Any other elements in the initializer list are treated as values in a JSON array. + * + * @param init The initializer list to construct the `json` object from. + */ json(std::initializer_list init) { if (init.size() == 2 && init.begin()->get_type() == json_type::string) @@ -72,78 +177,240 @@ namespace json value = std::vector(init); } + /** + * @brief Assignment operator for the json class. + * + * This operator assigns the value of another json object to the current object. + * + * @param other The json object to be assigned. + * @return A reference to the current json object after assignment. + */ json &operator=(const json &other) noexcept { value = other.value; return *this; } + /** + * @brief Move assignment operator for the json class. + * + * This operator allows the json object to be assigned the contents of another json object using move semantics. + * + * @param other The json object to be moved from. + * @return A reference to the modified json object. + */ json &operator=(json &&other) noexcept { value = std::move(other.value); return *this; } + /** + * @brief Assignment operator overload for assigning a nullptr to a json object. + * + * This operator allows assigning a nullptr to a json object. It sets the value of the json object to nullptr. + * + * @param[in] nullptr_t The nullptr to assign to the json object. + * @return json& A reference to the modified json object. + */ json &operator=(std::nullptr_t) noexcept { value = nullptr; return *this; } + /** + * @brief Assigns a string value to the JSON object. + * + * This assignment operator allows you to assign a string value to a JSON object. + * + * @param str The string value to assign. + * @return A reference to the modified JSON object. + */ json &operator=(const std::string &str) noexcept { value = str; return *this; } + /** + * @brief Assignment operator overload for assigning a C-style string to a json object. + * + * This operator allows you to assign a C-style string to a json object. The C-style string + * is converted to a std::string and assigned to the 'value' member variable of the json object. + * + * @param str The C-style string to assign to the json object. + * @return A reference to the modified json object. + */ json &operator=(const char *str) noexcept { value = std::string(str); return *this; } + /** + * @brief Assignment operator for assigning a boolean value to a JSON object. + * + * This operator assigns a boolean value to a JSON object and returns a reference to the modified object. + * + * @param b The boolean value to be assigned. + * @return A reference to the modified JSON object. + */ json &operator=(bool b) noexcept { value = b; return *this; } + /** + * @brief Assignment operator for assigning an integer value to a JSON object. + * + * This operator assigns the given integer value to the JSON object and returns a reference to the modified object. + * + * @param l The integer value to assign. + * @return A reference to the modified JSON object. + */ json &operator=(int l) noexcept { value = l; return *this; } + /** + * @brief Assigns an int64_t value to the JSON object. + * + * This assignment operator allows you to assign an int64_t value to a JSON object. + * + * @param l The int64_t value to assign. + * @return A reference to the modified JSON object. + */ json &operator=(int64_t l) noexcept { value = l; return *this; } + /** + * @brief Assigns an unsigned 64-bit integer value to the JSON object. + * + * This assignment operator allows you to assign an unsigned 64-bit integer value + * to a JSON object. The value is stored in the `value` member variable of the JSON object. + * + * @param l The unsigned 64-bit integer value to assign. + * @return A reference to the modified JSON object. + */ json &operator=(uint64_t l) noexcept { value = l; return *this; } + /** + * @brief Assigns a double value to the JSON object. + * + * This assignment operator allows you to assign a double value to a JSON object. + * + * @param d The double value to assign. + * @return A reference to the modified JSON object. + */ json &operator=(double d) noexcept { value = d; return *this; } + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows accessing the value associated with the specified key in the JSON object. + * The key is provided as a C-style string. + * + * @param str The key to access the value. + * @return A reference to the value associated with the key. + */ json &operator[](const char *str) { return operator[](std::string(str)); } + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows accessing the value associated with the specified key in the JSON object. + * The key is provided as a C-style string. + * + * @param str The key to access the value. + * @return A reference to the value associated with the key. + */ const json &operator[](const char *str) const { return operator[](std::string(str)); } + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows you to access the value associated with the specified key in the JSON object. + * If the key does not exist, it will be created and associated with a default-constructed JSON value. + * + * @param key The key to access the value. + * @return A reference to the value associated with the key. + */ json &operator[](const std::string &key) { return std::get>(value)[key]; } + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows you to access the value associated with the specified key in the JSON object. + * If the key does not exist, it will be created and associated with a default-constructed JSON value. + * + * @param key The key to access the value. + * @return A reference to the value associated with the key. + */ const json &operator[](const std::string &key) const { return std::get>(value).at(key); } + /** + * @brief Accesses the element at the specified index in the JSON object. + * + * This operator allows accessing the element at the specified index in the JSON object. + * The index can be either an integer or a size_t value. + * + * @param index The index of the element to access. + * @return A reference to the element at the specified index. + */ json &operator[](int index) { return operator[](static_cast(index)); } + /** + * @brief Accesses the element at the specified index in the JSON object. + * + * This operator allows accessing the element at the specified index in the JSON object. + * The index can be either an integer or a size_t value. + * + * @param index The index of the element to access. + * @return A reference to the element at the specified index. + */ const json &operator[](int index) const { return operator[](static_cast(index)); } + /** + * @brief Accesses the element at the specified index in the JSON object. + * + * This operator allows you to access the element at the specified index in the JSON object. + * It returns a reference to the element, allowing you to modify its value if needed. + * + * @param index The index of the element to access. + * @return A reference to the element at the specified index. + */ json &operator[](size_t index) { return std::get>(value)[index]; } + /** + * @brief Accesses the element at the specified index in the JSON object. + * + * This operator allows you to access the element at the specified index in the JSON object. + * It returns a reference to the element, allowing you to modify its value if needed. + * + * @param index The index of the element to access. + * @return A reference to the element at the specified index. + */ const json &operator[](size_t index) const { return std::get>(value).at(index); } + /** + * @brief Returns the type of the JSON value. + * + * This function returns the type of the JSON value as an enum value of the `json_type` enumeration. + * The possible types are: null, boolean, number, string, object, and array. + * + * @return The type of the JSON value. + */ json_type get_type() const noexcept { switch (value.index()) @@ -167,6 +434,14 @@ namespace json } } + /** + * Returns the size of the JSON value. + * For arrays, it returns the number of elements. + * For objects, it returns the number of key-value pairs. + * For other types, it returns 0. + * + * @return The size of the JSON value. + */ size_t size() const { switch (get_type()) @@ -180,11 +455,49 @@ namespace json } } + /** + * Checks if the JSON object contains a specific key. + * + * @param key The key to check for. + * @return True if the key is present in the JSON object, false otherwise. + */ bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>(value).count(key) > 0; } + /** + * @brief Overloads the equality operator for comparing two json objects. + * + * This function compares the value of two json objects and returns true if they are equal, and false otherwise. + * + * @param other The json object to compare with. + * @return true if the two json objects are equal, false otherwise. + */ bool operator==(const json &other) const noexcept { return value == other.value; } + /** + * @brief Overloads the equality operator for comparing a json object with a nullptr. + * + * This function compares the value of the json object with a nullptr and returns true if the value is null, and false otherwise. + * + * @param nullptr_t A null pointer constant. + * @return true if the json object is null, false otherwise. + */ bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } + /** + * @brief Overloads the equality operator for comparing a json object with a boolean value. + * + * This function compares the value of the json object with a boolean value and returns true if they are equal, and false otherwise. + * + * @param b The boolean value to compare with. + * @return true if the json object is equal to the boolean value, false otherwise. + */ bool operator==(bool b) const noexcept { return value.index() == 1 && std::get(value) == b; } + /** + * @brief Overloads the equality operator for comparing a json object with an integer value. + * + * This function compares the value of the json object with an integer value and returns true if they are equal, and false otherwise. + * + * @param l The integer value to compare with. + * @return true if the json object is equal to the integer value, false otherwise. + */ bool operator==(int l) const noexcept { switch (value.index()) @@ -201,22 +514,142 @@ namespace json return false; } } + /** + * @brief Overloads the equality operator for comparing a json object with an int64_t value. + * + * This function compares the value of the json object with an int64_t value and returns true if they are equal, and false otherwise. + * + * @param l The int64_t value to compare with. + * @return true if the json object is equal to the int64_t value, false otherwise. + */ bool operator==(int64_t l) const noexcept { return value.index() == 2 && std::get(value) == l; } + /** + * @brief Overloads the equality operator for comparing a json object with an unsigned 64-bit integer value. + * + * This function compares the value of the json object with an unsigned 64-bit integer value and returns true if they are equal, and false otherwise. + * + * @param l The unsigned 64-bit integer value to compare with. + * @return true if the json object is equal to the unsigned 64-bit integer value, false otherwise. + */ bool operator==(uint64_t l) const noexcept { return value.index() == 3 && std::get(value) == l; } + /** + * @brief Overloads the equality operator for comparing a json object with a double value. + * + * This function compares the value of the json object with a double value and returns true if they are equal, and false otherwise. + * + * @param d The double value to compare with. + * @return true if the json object is equal to the double value, false otherwise. + */ bool operator==(double d) const noexcept { return value.index() == 4 && std::get(value) == d; } + /** + * @brief Overloads the equality operator for comparing a json object with a string. + * + * This function compares the value of the json object with a string and returns true if they are equal, and false otherwise. + * + * @param str The string to compare with. + * @return true if the json object is equal to the string, false otherwise. + */ bool operator==(const std::string &str) const noexcept { return value.index() == 5 && std::get(value) == str; } + /** + * @brief Overloads the equality operator for comparing a json object with a C-style string. + * + * This function compares the value of the json object with a C-style string and returns true if they are equal, and false otherwise. + * + * @param str The C-style string to compare with. + * @return true if the json object is equal to the C-style string, false otherwise. + */ bool operator==(const char *str) const noexcept { return value.index() == 5 && std::get(value) == str; } + /** + * @brief Overloads the inequality operator for comparing two json objects. + * + * This function compares the value of two json objects and returns true if they are not equal, and false otherwise. + * + * @param other The json object to compare with. + * @return true if the two json objects are not equal, false otherwise. + */ bool operator!=(const json &other) const noexcept { return value != other.value; } + /** + * @brief Overloads the inequality operator for comparing a json object with a nullptr. + * + * This function compares the value of the json object with a nullptr and returns true if the value is not null, and false otherwise. + * + * @param nullptr_t A null pointer constant. + * @return true if the json object is not null, false otherwise. + */ bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } + /** + * @brief Overloads the inequality operator for comparing a json object with a boolean value. + * + * This function compares the value of the json object with a boolean value and returns true if they are not equal, and false otherwise. + * + * @param b The boolean value to compare with. + * @return true if the json object is not equal to the boolean value, false otherwise. + */ bool operator!=(bool b) const noexcept { return value.index() != 1 || std::get(value) != b; } + /** + * @brief Overloads the inequality operator for comparing a json object with an integer value. + * + * This function compares the value of the json object with an integer value and returns true if they are not equal, and false otherwise. + * + * @param l The integer value to compare with. + * @return true if the json object is not equal to the integer value, false otherwise. + */ bool operator!=(int l) const noexcept { return !(*this == l); } + /** + * @brief Overloads the inequality operator for comparing a json object with an int64_t value. + * + * This function compares the value of the json object with an int64_t value and returns true if they are not equal, and false otherwise. + * + * @param l The int64_t value to compare with. + * @return true if the json object is not equal to the int64_t value, false otherwise. + */ bool operator!=(int64_t l) const noexcept { return value.index() != 2 || std::get(value) != l; } + /** + * @brief Overloads the inequality operator for comparing a json object with an unsigned 64-bit integer value. + * + * This function compares the value of the json object with an unsigned 64-bit integer value and returns true if they are not equal, and false otherwise. + * + * @param l The unsigned 64-bit integer value to compare with. + * @return true if the json object is not equal to the unsigned 64-bit integer value, false otherwise. + */ bool operator!=(uint64_t l) const noexcept { return value.index() != 3 || std::get(value) != l; } + /** + * @brief Overloads the inequality operator for comparing a json object with a double value. + * + * This function compares the value of the json object with a double value and returns true if they are not equal, and false otherwise. + * + * @param d The double value to compare with. + * @return true if the json object is not equal to the double value, false otherwise. + */ bool operator!=(double d) const noexcept { return value.index() != 4 || std::get(value) != d; } + /** + * @brief Overloads the inequality operator for comparing a json object with a string. + * + * This function compares the value of the json object with a string and returns true if they are not equal, and false otherwise. + * + * @param str The string to compare with. + * @return true if the json object is not equal to the string, false otherwise. + */ bool operator!=(const std::string &str) const noexcept { return value.index() != 5 || std::get(value) != str; } + /** + * @brief Overloads the inequality operator for comparing a json object with a C-style string. + * + * This function compares the value of the json object with a C-style string and returns true if they are not equal, and false otherwise. + * + * @param str The C-style string to compare with. + * @return true if the json object is not equal to the C-style string, false otherwise. + */ bool operator!=(const char *str) const noexcept { return value.index() != 5 || std::get(value) != str; } + /** + * @brief Conversion operator to bool. + * + * This operator converts the JSON value to a boolean value. + * The conversion is based on the type of the JSON value. + * + * @return true if the JSON value is considered true, false otherwise. + */ operator bool() const noexcept { switch (value.index()) @@ -240,6 +673,14 @@ namespace json } } + /** + * @brief Conversion operator to int. + * + * This operator converts the JSON value to an integer value. + * The conversion is based on the type of the JSON value. + * + * @return The integer value of the JSON object. + */ operator int64_t() const noexcept { switch (value.index()) @@ -257,6 +698,14 @@ namespace json } } + /** + * @brief Conversion operator to unsigned 64-bit integer. + * + * This operator converts the JSON value to an unsigned 64-bit integer value. + * The conversion is based on the type of the JSON value. + * + * @return The unsigned 64-bit integer value of the JSON object. + */ operator uint64_t() const noexcept { switch (value.index()) @@ -274,6 +723,14 @@ namespace json } } + /** + * @brief Conversion operator to double. + * + * This operator converts the JSON value to a double value. + * The conversion is based on the type of the JSON value. + * + * @return The double value of the JSON object. + */ operator double() const noexcept { switch (value.index()) @@ -291,6 +748,14 @@ namespace json } } + /** + * @brief Conversion operator to string. + * + * This operator converts the JSON value to a string. + * The conversion is based on the type of the JSON value. + * + * @return The string value of the JSON object. + */ operator std::string() const noexcept { switch (value.index()) @@ -310,21 +775,99 @@ namespace json } } + /** + * @brief Conversion operator to std::map. + * + * This operator converts the JSON value to a std::map. + * The conversion is based on the type of the JSON value. + * + * @return The std::map value of the JSON object. + */ operator std::map() const noexcept { return value.index() == 6 ? std::get>(value) : std::map(); } + /** + * Returns a constant reference to the underlying map object if the JSON value is an object. + * + * @return A constant reference to the underlying map object. + */ const std::map &as_object() const noexcept { return std::get>(value); } + /** + * @brief Conversion operator to std::vector. + * + * This operator converts the JSON value to a std::vector. + * The conversion is based on the type of the JSON value. + * + * @return The std::vector value of the JSON object. + */ operator std::vector() const noexcept { return value.index() == 7 ? std::get>(value) : std::vector(); } + /** + * Returns a const reference to the internal vector representation of the JSON value. + * + * @return const std::vector& A const reference to the internal vector representation of the JSON value. + */ const std::vector &as_array() const noexcept { return std::get>(value); } + /** + * @brief Clears the JSON object. + * + * This function clears the JSON object by setting its value to null. + */ void clear() noexcept { value = nullptr; } + /** + * @brief Adds a new element to the end of the JSON array. + * + * This function appends a new element to the end of the JSON array. The element + * to be added should be of the same type as the elements in the array. + * + * @param val The value to be added to the array. + */ void push_back(const json &val) { std::get>(value).push_back(val); } + /** + * @brief Adds a new element to the end of the JSON array. + * + * This function appends a new element to the end of the JSON array. The element + * to be added should be of the same type as the elements in the array. + * + * @param val The value to be added to the array. + */ void push_back(json &&val) { std::get>(value).push_back(std::move(val)); } + /** + * @brief Erases the element with the specified key from the JSON object. + * + * This function removes the element with the specified key from the JSON object. + * If the key does not exist, no changes are made. + * + * @param key The key of the element to be erased. + */ void erase(const std::string &key) { std::get>(value).erase(key); } + /** + * @brief Erases an element at the specified index from the JSON array. + * + * This function removes the element at the specified index from the JSON array. + * The index must be a valid position within the array. + * + * @param index The index of the element to be erased. + */ void erase(size_t index) { std::get>(value).erase(std::get>(value).begin() + index); } + /** + * Returns a string representation of the JSON value. + * + * The `dump` function converts the JSON value into a string representation. + * The resulting string represents the JSON value according to its type: + * - For a null value, the string "null" is returned. + * - For a boolean value, the string "true" or "false" is returned. + * - For an integer value, the string representation of the integer is returned. + * - For a floating-point value, the string representation of the floating-point number is returned. + * - For a string value, the string is enclosed in double quotes and returned. + * - For an object value, the string representation of the object is returned. + * - For an array value, the string representation of the array is returned. + * + * @return A string representation of the JSON value. + */ std::string dump() const { switch (value.index()) @@ -376,9 +919,27 @@ namespace json std::variant, std::vector> value; }; + /** + * Loads a JSON object from the given input stream. + * + * @param is The input stream to read the JSON object from. + * @return The loaded JSON object. + */ json load(std::istream &is); + /** + * Loads a JSON object from a string. + * + * @param str The input string containing the JSON data. + * @return A JSON object representing the parsed data. + */ json load(const char *str); + /** + * Loads a JSON object from a string. + * + * @param str The string containing the JSON object. + * @return The loaded JSON object. + */ inline json load(const std::string &str) { return load(str.c_str()); } } // namespace json From 9ca9682ec02b871c32eb55ac914e45f3874cfd8c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 13:46:43 +0200 Subject: [PATCH 047/137] Refactor json.hpp to use uint64_t instead of u_int64_t for improved code consistency --- include/json.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json.hpp b/include/json.hpp index 7e3b28a..80cb5f4 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -659,7 +659,7 @@ namespace json case 2: return std::get(value) != 0; case 3: - return std::get(value) != 0; + return std::get(value) != 0; case 4: return !std::get(value); case 5: From 8dfae83c326580438cbc93d39b8b6a14f0f3bf49 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 13:52:49 +0200 Subject: [PATCH 048/137] Refactor json.hpp to use int64_t instead of int for improved code consistency --- include/json.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json.hpp b/include/json.hpp index 80cb5f4..dc13710 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -272,7 +272,7 @@ namespace json */ json &operator=(int l) noexcept { - value = l; + value = static_cast(l); return *this; } From a737938210ff9f63c7e12fb978efc99e87d4f27e Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 14:13:27 +0200 Subject: [PATCH 049/137] Refactor json.hpp and json.cpp to use int64_t for improved code consistency --- include/json.hpp | 2 +- src/json.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index dc13710..e8216ce 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -50,7 +50,7 @@ namespace json value = false; break; case json_type::number: - value = 0; + value = static_cast(0); break; case json_type::string: value = std::string(); diff --git a/src/json.cpp b/src/json.cpp index fe370a2..c716d2d 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -187,7 +187,7 @@ namespace json return json(std::stod(num)); } else - return json(std::stol(num)); + return json(static_cast(std::stol(num))); } case '.': { From 813ace6491b52c13dd58c1ceaf8b86d9490b24f5 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 14:19:26 +0200 Subject: [PATCH 050/137] Refactor json.hpp constructor to use int64_t for improved code consistency --- include/json.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json.hpp b/include/json.hpp index e8216ce..2cd4d56 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -103,7 +103,7 @@ namespace json * * @param l The integer value to initialize the `json` object with. */ - json(int l) noexcept : value(l) {} + json(int l) noexcept : value(static_cast(l)) {} /** * @brief Constructs a `json` object from an int64_t value. * From f02aa8dac9bac17bc9b41f47b6e86f89e27680b4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 15:48:11 +0200 Subject: [PATCH 051/137] Added vector constructor --- include/json.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/json.hpp b/include/json.hpp index 2cd4d56..fc8eed3 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -144,6 +144,15 @@ namespace json * @param str The C-style string to be converted into a `json` object. */ json(const char *str) noexcept : value(std::string(str)) {} + /** + * @brief Constructs a `json` object from a vector of `json` objects. + * + * This constructor initializes a `json` object with the given vector of `json` objects. + * + * @param arr The vector of `json` objects to initialize the `json` object with. + */ + json(std::vector &&arr) noexcept : value(std::move(arr)) {} + /** * @brief Constructs a `json` object from an initializer list. * From 64ba080f1d1f6461694c89c09d52b930e94a48f8 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 9 May 2024 17:21:39 +0200 Subject: [PATCH 052/137] Refactor code for improved consistency and readability --- include/json.hpp | 6 +++--- src/json.cpp | 40 ++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index fc8eed3..c9ea94d 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -512,13 +512,13 @@ namespace json switch (value.index()) { case 1: - return std::get(value) == l; + return std::get(value) == static_cast(l); case 2: - return std::get(value) == l; + return std::get(value) == static_cast(l); case 3: return std::get(value) == static_cast(l); case 4: - return std::get(value) == l; + return std::get(value) == static_cast(l); default: return false; } diff --git a/src/json.cpp b/src/json.cpp index c716d2d..0ba4056 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -45,7 +45,7 @@ namespace json char c; for (const auto factor : factors) { - c = is.get(); + c = static_cast(is.get()); if (c >= '0' && c <= '9') codepoint += static_cast((static_cast(c) - 0x30u) << factor); else if (c >= 'A' && c <= 'F') @@ -86,7 +86,7 @@ namespace json } } else - val += is.get(); + val += static_cast(is.get()); is.get(); return val; } @@ -154,36 +154,36 @@ namespace json case '9': { std::string num; - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '.') { - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == 'e' || is.peek() == 'E') { - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '+') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '-') - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); return json(std::stod(num)); } return json(std::stod(num)); } else if (is.peek() == 'e' || is.peek() == 'E') { - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '+') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '-') - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); return json(std::stod(num)); } else @@ -192,18 +192,18 @@ namespace json case '.': { std::string num; - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == 'e' || is.peek() == 'E') { - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '+') - num += is.get(); + num += static_cast(is.get()); if (is.peek() == '-') - num += is.get(); + num += static_cast(is.get()); while (is.peek() == '0' || is.peek() == '1' || is.peek() == '2' || is.peek() == '3' || is.peek() == '4' || is.peek() == '5' || is.peek() == '6' || is.peek() == '7' || is.peek() == '8' || is.peek() == '9') - num += is.get(); + num += static_cast(is.get()); return json(std::stod(num)); } return json(std::stod(num)); From 992483441026aa9ff590cdac80c6b4a151a4e9f7 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 13 May 2024 11:24:15 +0200 Subject: [PATCH 053/137] Refactor json.hpp constructor to use initializer list for improved code consistency and readability --- include/json.hpp | 34 ++++++---------------------------- src/json.cpp | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index c9ea94d..e3faa0e 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -154,37 +154,15 @@ namespace json json(std::vector &&arr) noexcept : value(std::move(arr)) {} /** - * @brief Constructs a `json` object from an initializer list. + * @brief Constructs a `json` object from an initializer list of `json` objects. * - * This constructor allows initializing a `json` object using an initializer list. - * It supports two types of initializer lists: - * 1. If the initializer list contains two elements and the first element is a string, - * it constructs a JSON object with a single key-value pair, where the key is the string - * and the value is the second element of the initializer list. - * 2. If the initializer list contains one or more JSON objects, it constructs a JSON object - * by merging all the key-value pairs from the objects in the initializer list. - * If there are duplicate keys, the value from the last object in the list will be used. - * Any other elements in the initializer list are treated as values in a JSON array. + * This constructor allows you to create a `json` object by providing an initializer list + * of `json` objects. Each element in the initializer list will be used to construct the + * corresponding element in the `json` object. * - * @param init The initializer list to construct the `json` object from. + * @param init The initializer list of `json` objects. */ - json(std::initializer_list init) - { - if (init.size() == 2 && init.begin()->get_type() == json_type::string) - { - value = std::map(); - operator[](static_cast(*init.begin())) = *(init.begin() + 1); - } - else if (init.begin()->get_type() == json_type::object) - { - value = std::map(); - for (auto &obj : init) - for (auto &[key, value] : obj.as_object()) - operator[](key) = value; - } - else - value = std::vector(init); - } + json(std::initializer_list init); /** * @brief Assignment operator for the json class. diff --git a/src/json.cpp b/src/json.cpp index 0ba4056..5db0f69 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,9 +1,28 @@ #include "json.hpp" #include +#include #include namespace json { + json::json(std::initializer_list init) + { + bool is_object = std::all_of(init.begin(), init.end(), [](const json &j) + { return j.get_type() == json_type::array && j.size() == 2 && j[0].get_type() == json_type::string; }); + if (is_object) + { + value = std::map(); + for (const auto &j : init) + std::get>(value)[static_cast(j[0])] = j[1]; + } + else + { + value = std::vector(); + for (const auto &j : init) + std::get>(value).push_back(j); + } + } + std::string parse_string(std::istream &is) { is.get(); From c1413c16ef6d9dc03842fdb51948c0f22307a9b4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 13 May 2024 12:32:43 +0200 Subject: [PATCH 054/137] Refactor json.hpp constructor to use initializer list for improved code consistency and readability --- include/json.hpp | 6 +++--- src/json.cpp | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index e3faa0e..580c6fb 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -157,10 +157,10 @@ namespace json * @brief Constructs a `json` object from an initializer list of `json` objects. * * This constructor allows you to create a `json` object by providing an initializer list - * of `json` objects. Each element in the initializer list will be used to construct the - * corresponding element in the `json` object. + * of `json` objects. The elements in the initializer list will be used to initialize the + * `json` object in the same order as they appear in the initializer list. * - * @param init The initializer list of `json` objects. + * @param init An initializer list of `json` objects. */ json(std::initializer_list init); diff --git a/src/json.cpp b/src/json.cpp index 5db0f69..1c9d8eb 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,4 +1,5 @@ #include "json.hpp" +#include "logging.hpp" #include #include #include @@ -7,20 +8,25 @@ namespace json { json::json(std::initializer_list init) { - bool is_object = std::all_of(init.begin(), init.end(), [](const json &j) - { return j.get_type() == json_type::array && j.size() == 2 && j[0].get_type() == json_type::string; }); - if (is_object) - { + if (init.size() == 2 && init.begin()->get_type() == json_type::string) + { // we have a key-value pair.. + value = std::map(); + std::get>(value)[static_cast(*init.begin())] = *(init.begin() + 1); + } + else if (std::all_of(init.begin(), init.end(), [](const json &j) + { return j.get_type() == json_type::object && j.size() == 1; })) + { // we have an array of key-value pairs.. value = std::map(); for (const auto &j : init) - std::get>(value)[static_cast(j[0])] = j[1]; + std::get>(value)[static_cast(j.as_object().begin()->first)] = j.as_object().begin()->second; } else - { + { // we have an array.. value = std::vector(); for (const auto &j : init) std::get>(value).push_back(j); } + LOG_INFO(*this); } std::string parse_string(std::istream &is) From 81b02a85ee103d9b984354314aacf53249635fbf Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 13 May 2024 12:34:25 +0200 Subject: [PATCH 055/137] Refactor json.cpp to remove unnecessary logging statement --- src/json.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index 1c9d8eb..13ac0c5 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,5 +1,4 @@ #include "json.hpp" -#include "logging.hpp" #include #include #include @@ -26,7 +25,6 @@ namespace json for (const auto &j : init) std::get>(value).push_back(j); } - LOG_INFO(*this); } std::string parse_string(std::istream &is) From be1db891c4092f68c0ed72e833ef8fa963d0a449 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 14 May 2024 14:24:58 +0200 Subject: [PATCH 056/137] Add badges for build status and code coverage --- .github/workflows/cmake.yml | 5 +++++ README.md | 1 + extern/utils | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index da7beee..cc26643 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -42,6 +42,11 @@ jobs: working-directory: ${{github.workspace}}/build run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage -T MemCheck + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - name: Test without coverage and memory sanitizer if: matrix.build_type == 'Release' || matrix.os != 'ubuntu-latest' working-directory: ${{github.workspace}}/build diff --git a/README.md b/README.md index b169b71..0f92e20 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # JSON ![Build Status](https://github.com/ratioSolver/json/actions/workflows/cmake.yml/badge.svg) +[![codecov](https://codecov.io/gh/ratioSolver/json/branch/master/graph/badge.svg)](https://codecov.io/gh/ratioSolver/json) This repository contains a [JSON](http://www.json.org/) parser and generator written in C++. diff --git a/extern/utils b/extern/utils index e666c7a..5c823e5 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e666c7a6e69ea56d6dff5c6a34cfad66c7e78710 +Subproject commit 5c823e5b30d50510c0a69ce80d2d6ac909471f88 From ea8cde60bee47b9b59db51d969fb3a9f97176d7c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 14 May 2024 15:09:44 +0200 Subject: [PATCH 057/137] Added tests for constructors.. --- tests/test_json.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 52ebeda..c433a8d 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -3,6 +3,47 @@ #include #include +void test_constructors() +{ + json::json j0; + assert(j0.get_type() == json::json_type::null); + assert(j0 == nullptr); + assert(j0.size() == 0); + + json::json j1 = 1; + assert(j1.get_type() == json::json_type::number); + assert(j1 == 1); + assert(j1.size() == 0); + + json::json j2 = 2.0; + assert(j2.get_type() == json::json_type::number); + assert(j2 == 2.0); + assert(j2.size() == 0); + + json::json j3 = "3"; + assert(j3.get_type() == json::json_type::string); + assert(j3 == "3"); + assert(j3.size() == 0); + + json::json j4 = true; + assert(j4.get_type() == json::json_type::boolean); + assert(j4 == true); + assert(j4.size() == 0); + + json::json j5 = nullptr; + assert(j5.get_type() == json::json_type::null); + assert(j5 == nullptr); + assert(j5.size() == 0); + + json::json j6 = json::json_type::array; + assert(j6.get_type() == json::json_type::array); + assert(j6.size() == 0); + + json::json j7 = json::json_type::object; + assert(j7.get_type() == json::json_type::object); + assert(j7.size() == 0); +} + void test_json() { std::stringstream ss; @@ -245,6 +286,8 @@ void test_array_of_scientific_numbers() int main(int, char **) { + test_constructors(); + test_json(); test_json_escapes(); test_json_special_chars(); From 747a3c7e9f90c620ba849d0aa7d20f2d32908826 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 14 May 2024 15:14:12 +0200 Subject: [PATCH 058/137] Solved minor test issue.. --- tests/test_json.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_json.cpp b/tests/test_json.cpp index c433a8d..565145a 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -6,8 +6,8 @@ void test_constructors() { json::json j0; - assert(j0.get_type() == json::json_type::null); - assert(j0 == nullptr); + assert(j0.get_type() == json::json_type::object); + assert(j0 == json::json{}); assert(j0.size() == 0); json::json j1 = 1; From e3d266bb3981ad05659886bfc3ab702f42f1d725 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 14 May 2024 15:29:11 +0200 Subject: [PATCH 059/137] Added some tests to improve coverage.. --- tests/test_json.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 565145a..3e539bb 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -4,6 +4,92 @@ #include void test_constructors() +{ + json::json j0; + assert(j0.get_type() == json::json_type::object); + assert(j0.size() == 0); + + json::json j1(1); + assert(j1.get_type() == json::json_type::number); + assert(j1 == 1); + assert(j1.size() == 0); + + json::json j2(2.0); + assert(j2.get_type() == json::json_type::number); + assert(j2 == 2.0); + assert(j2.size() == 0); + + json::json j3("3"); + assert(j3.get_type() == json::json_type::string); + assert(j3 == "3"); + assert(j3.size() == 0); + + json::json j4(true); + assert(j4.get_type() == json::json_type::boolean); + assert(j4 == true); + assert(j4.size() == 0); + + json::json j5(nullptr); + assert(j5.get_type() == json::json_type::null); + assert(j5 == nullptr); + assert(j5.size() == 0); + + json::json j6(json::json_type::array); + assert(j6.get_type() == json::json_type::array); + assert(j6.size() == 0); + + json::json j7(json::json_type::object); + assert(j7.get_type() == json::json_type::object); + assert(j7.size() == 0); +} + +void test_constructors2() +{ + json::json j0(json::json_type::null); + assert(j0.get_type() == json::json_type::null); + assert(j0 == nullptr); + assert(j0.size() == 0); + + json::json j1(json::json_type::array); + assert(j1.get_type() == json::json_type::array); + assert(j1.size() == 0); + + json::json j2(json::json_type::object); + assert(j2.get_type() == json::json_type::object); + assert(j2.size() == 0); + + json::json j3(json::json_type::number); + assert(j3.get_type() == json::json_type::number); + assert(j3 == 0); + assert(j3.size() == 0); + + json::json j4(json::json_type::string); + assert(j4.get_type() == json::json_type::string); + assert(j4 == ""); + assert(j4.size() == 0); + + json::json j5(json::json_type::boolean); + assert(j5.get_type() == json::json_type::boolean); + assert(j5 == false); + assert(j5.size() == 0); + + json::json j6(json::json_type::array); + j6.push_back(1); + j6.push_back(2); + j6.push_back(3); + assert(j6.get_type() == json::json_type::array); + assert(j6.size() == 3); + + json::json j7(std::move(j6)); + assert(j6.get_type() == json::json_type::null); + assert(j7.get_type() == json::json_type::array); + assert(j7.size() == 3); + assert(j7[0] == 1); + assert(j7[1] == 2); + assert(j7[2] == 3); +} + +void test_assignments() { json::json j0; assert(j0.get_type() == json::json_type::object); @@ -287,6 +373,8 @@ void test_array_of_scientific_numbers() int main(int, char **) { test_constructors(); + test_constructors2(); + test_assignments(); test_json(); test_json_escapes(); From 8df08274a97b95790f8406d4feaf8a49a34876dc Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 16 May 2024 16:25:37 +0200 Subject: [PATCH 060/137] Refactor extern/utils subproject commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 5c823e5..837aecd 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 5c823e5b30d50510c0a69ce80d2d6ac909471f88 +Subproject commit 837aecdb8c4b3d97db53546d353175c2c05a6a3b From 1709a6cd1d16d572be0df4472c908fceaff092ea Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 16 May 2024 16:29:17 +0200 Subject: [PATCH 061/137] Add include for cstdint in json.hpp --- include/json.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/json.hpp b/include/json.hpp index 580c6fb..a0a29e8 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace json { From 6d37db5f9a82f6921cd2b7e8aeb3a68f05a63924 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 1 Jul 2024 15:25:58 +0200 Subject: [PATCH 062/137] Added JSON schema validation.. --- include/json.hpp | 64 +++++++++++++--------- src/json.cpp | 130 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_json.cpp | 2 +- 3 files changed, 168 insertions(+), 28 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index a0a29e8..0b5380f 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -399,7 +399,7 @@ namespace json * * @return The type of the JSON value. */ - json_type get_type() const noexcept + [[nodiscard]] json_type get_type() const noexcept { switch (value.index()) { @@ -430,7 +430,7 @@ namespace json * * @return The size of the JSON value. */ - size_t size() const + [[nodiscard]] size_t size() const { switch (get_type()) { @@ -449,7 +449,7 @@ namespace json * @param key The key to check for. * @return True if the key is present in the JSON object, false otherwise. */ - bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>(value).count(key) > 0; } + [[nodiscard]] bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>(value).count(key) > 0; } /** * @brief Overloads the equality operator for comparing two json objects. @@ -459,7 +459,7 @@ namespace json * @param other The json object to compare with. * @return true if the two json objects are equal, false otherwise. */ - bool operator==(const json &other) const noexcept { return value == other.value; } + [[nodiscard]] bool operator==(const json &other) const noexcept { return value == other.value; } /** * @brief Overloads the equality operator for comparing a json object with a nullptr. * @@ -468,7 +468,7 @@ namespace json * @param nullptr_t A null pointer constant. * @return true if the json object is null, false otherwise. */ - bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } + [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } /** * @brief Overloads the equality operator for comparing a json object with a boolean value. * @@ -477,7 +477,7 @@ namespace json * @param b The boolean value to compare with. * @return true if the json object is equal to the boolean value, false otherwise. */ - bool operator==(bool b) const noexcept { return value.index() == 1 && std::get(value) == b; } + [[nodiscard]] bool operator==(bool b) const noexcept { return value.index() == 1 && std::get(value) == b; } /** * @brief Overloads the equality operator for comparing a json object with an integer value. * @@ -486,7 +486,7 @@ namespace json * @param l The integer value to compare with. * @return true if the json object is equal to the integer value, false otherwise. */ - bool operator==(int l) const noexcept + [[nodiscard]] bool operator==(int l) const noexcept { switch (value.index()) { @@ -510,7 +510,7 @@ namespace json * @param l The int64_t value to compare with. * @return true if the json object is equal to the int64_t value, false otherwise. */ - bool operator==(int64_t l) const noexcept { return value.index() == 2 && std::get(value) == l; } + [[nodiscard]] bool operator==(int64_t l) const noexcept { return value.index() == 2 && std::get(value) == l; } /** * @brief Overloads the equality operator for comparing a json object with an unsigned 64-bit integer value. * @@ -519,7 +519,7 @@ namespace json * @param l The unsigned 64-bit integer value to compare with. * @return true if the json object is equal to the unsigned 64-bit integer value, false otherwise. */ - bool operator==(uint64_t l) const noexcept { return value.index() == 3 && std::get(value) == l; } + [[nodiscard]] bool operator==(uint64_t l) const noexcept { return value.index() == 3 && std::get(value) == l; } /** * @brief Overloads the equality operator for comparing a json object with a double value. * @@ -528,7 +528,7 @@ namespace json * @param d The double value to compare with. * @return true if the json object is equal to the double value, false otherwise. */ - bool operator==(double d) const noexcept { return value.index() == 4 && std::get(value) == d; } + [[nodiscard]] bool operator==(double d) const noexcept { return value.index() == 4 && std::get(value) == d; } /** * @brief Overloads the equality operator for comparing a json object with a string. * @@ -537,7 +537,7 @@ namespace json * @param str The string to compare with. * @return true if the json object is equal to the string, false otherwise. */ - bool operator==(const std::string &str) const noexcept { return value.index() == 5 && std::get(value) == str; } + [[nodiscard]] bool operator==(const std::string &str) const noexcept { return value.index() == 5 && std::get(value) == str; } /** * @brief Overloads the equality operator for comparing a json object with a C-style string. * @@ -546,7 +546,7 @@ namespace json * @param str The C-style string to compare with. * @return true if the json object is equal to the C-style string, false otherwise. */ - bool operator==(const char *str) const noexcept { return value.index() == 5 && std::get(value) == str; } + [[nodiscard]] bool operator==(const char *str) const noexcept { return value.index() == 5 && std::get(value) == str; } /** * @brief Overloads the inequality operator for comparing two json objects. @@ -556,7 +556,7 @@ namespace json * @param other The json object to compare with. * @return true if the two json objects are not equal, false otherwise. */ - bool operator!=(const json &other) const noexcept { return value != other.value; } + [[nodiscard]] bool operator!=(const json &other) const noexcept { return value != other.value; } /** * @brief Overloads the inequality operator for comparing a json object with a nullptr. * @@ -565,7 +565,7 @@ namespace json * @param nullptr_t A null pointer constant. * @return true if the json object is not null, false otherwise. */ - bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } + [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } /** * @brief Overloads the inequality operator for comparing a json object with a boolean value. * @@ -574,7 +574,7 @@ namespace json * @param b The boolean value to compare with. * @return true if the json object is not equal to the boolean value, false otherwise. */ - bool operator!=(bool b) const noexcept { return value.index() != 1 || std::get(value) != b; } + [[nodiscard]] bool operator!=(bool b) const noexcept { return value.index() != 1 || std::get(value) != b; } /** * @brief Overloads the inequality operator for comparing a json object with an integer value. * @@ -583,7 +583,7 @@ namespace json * @param l The integer value to compare with. * @return true if the json object is not equal to the integer value, false otherwise. */ - bool operator!=(int l) const noexcept { return !(*this == l); } + [[nodiscard]] bool operator!=(int l) const noexcept { return !(*this == l); } /** * @brief Overloads the inequality operator for comparing a json object with an int64_t value. * @@ -592,7 +592,7 @@ namespace json * @param l The int64_t value to compare with. * @return true if the json object is not equal to the int64_t value, false otherwise. */ - bool operator!=(int64_t l) const noexcept { return value.index() != 2 || std::get(value) != l; } + [[nodiscard]] bool operator!=(int64_t l) const noexcept { return value.index() != 2 || std::get(value) != l; } /** * @brief Overloads the inequality operator for comparing a json object with an unsigned 64-bit integer value. * @@ -601,7 +601,7 @@ namespace json * @param l The unsigned 64-bit integer value to compare with. * @return true if the json object is not equal to the unsigned 64-bit integer value, false otherwise. */ - bool operator!=(uint64_t l) const noexcept { return value.index() != 3 || std::get(value) != l; } + [[nodiscard]] bool operator!=(uint64_t l) const noexcept { return value.index() != 3 || std::get(value) != l; } /** * @brief Overloads the inequality operator for comparing a json object with a double value. * @@ -610,7 +610,7 @@ namespace json * @param d The double value to compare with. * @return true if the json object is not equal to the double value, false otherwise. */ - bool operator!=(double d) const noexcept { return value.index() != 4 || std::get(value) != d; } + [[nodiscard]] bool operator!=(double d) const noexcept { return value.index() != 4 || std::get(value) != d; } /** * @brief Overloads the inequality operator for comparing a json object with a string. * @@ -619,7 +619,7 @@ namespace json * @param str The string to compare with. * @return true if the json object is not equal to the string, false otherwise. */ - bool operator!=(const std::string &str) const noexcept { return value.index() != 5 || std::get(value) != str; } + [[nodiscard]] bool operator!=(const std::string &str) const noexcept { return value.index() != 5 || std::get(value) != str; } /** * @brief Overloads the inequality operator for comparing a json object with a C-style string. * @@ -628,7 +628,7 @@ namespace json * @param str The C-style string to compare with. * @return true if the json object is not equal to the C-style string, false otherwise. */ - bool operator!=(const char *str) const noexcept { return value.index() != 5 || std::get(value) != str; } + [[nodiscard]] bool operator!=(const char *str) const noexcept { return value.index() != 5 || std::get(value) != str; } /** * @brief Conversion operator to bool. @@ -777,7 +777,7 @@ namespace json * * @return A constant reference to the underlying map object. */ - const std::map &as_object() const noexcept { return std::get>(value); } + [[nodiscard]] const std::map &as_object() const noexcept { return std::get>(value); } /** * @brief Conversion operator to std::vector. @@ -793,7 +793,7 @@ namespace json * * @return const std::vector& A const reference to the internal vector representation of the JSON value. */ - const std::vector &as_array() const noexcept { return std::get>(value); } + [[nodiscard]] const std::vector &as_array() const noexcept { return std::get>(value); } /** * @brief Clears the JSON object. @@ -856,7 +856,7 @@ namespace json * * @return A string representation of the JSON value. */ - std::string dump() const + [[nodiscard]] std::string dump() const { switch (value.index()) { @@ -913,7 +913,7 @@ namespace json * @param is The input stream to read the JSON object from. * @return The loaded JSON object. */ - json load(std::istream &is); + [[nodiscard]] json load(std::istream &is); /** * Loads a JSON object from a string. @@ -921,7 +921,7 @@ namespace json * @param str The input string containing the JSON data. * @return A JSON object representing the parsed data. */ - json load(const char *str); + [[nodiscard]] json load(const char *str); /** * Loads a JSON object from a string. @@ -929,5 +929,15 @@ namespace json * @param str The string containing the JSON object. * @return The loaded JSON object. */ - inline json load(const std::string &str) { return load(str.c_str()); } + [[nodiscard]] inline json load(const std::string &str) { return load(str.c_str()); } + + /** + * Validates a JSON value against a JSON schema. + * + * @param value The JSON value to be validated. + * @param schema The JSON schema to validate against. + * @param schema_refs The JSON schema references. + * @return `true` if the value is valid according to the schema, `false` otherwise. + */ + [[nodiscard]] bool validate(const json &value, const json &schema, const json &schema_refs); } // namespace json diff --git a/src/json.cpp b/src/json.cpp index 13ac0c5..f2547c7 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -300,4 +300,134 @@ namespace json ss << str; return load(ss); } + + bool validate(const json &value, const json &schema, const json &schema_refs) + { + if (schema.contains("type")) + { // we have a type.. + if (schema["type"] == "object") + { + if (value.get_type() != json_type::object) + return false; + for (const auto &property : schema["properties"].as_object()) + { + if (!value.contains(property.first)) + return false; + if (!validate(value[property.first], property.second, schema_refs)) + return false; + } + return true; + } + else if (schema["type"] == "array") + { + if (value.get_type() != json_type::array) + return false; + for (const auto &v : value.as_array()) + if (!validate(v, schema["items"], schema_refs)) + return false; + return true; + } + else if (schema["type"] == "string") + { + if (value.get_type() != json_type::string) + return false; + if (schema.contains("enum")) + return std::find(schema["enum"].as_array().begin(), schema["enum"].as_array().end(), value) != schema["enum"].as_array().end(); + } + else if (schema["type"] == "number") + { + if (value.get_type() != json_type::number) + return false; + if (schema.contains("minimum")) + { + double min = schema["minimum"]; + double v = value; + if (v < min) + return false; + } + if (schema.contains("maximum")) + { + double max = schema["maximum"]; + double v = value; + if (v > max) + return false; + } + return true; + } + else if (schema["type"] == "integer") + { + if (value.get_type() != json_type::number) + return false; + if (schema.contains("minimum")) + { + long min = schema["minimum"]; + long v = value; + if (v < min) + return false; + } + if (schema.contains("maximum")) + { + long max = schema["maximum"]; + long v = value; + if (v > max) + return false; + } + return true; + } + else if (schema["type"] == "boolean") + return value.get_type() == json_type::boolean; + else if (schema["type"] == "null") + return value.get_type() == json_type::null; + else if (schema["type"] == "any") + return true; + else + return false; + } + else if (schema.contains("$ref")) + { // we have a reference to another schema.. + if (!schema_refs.contains(schema["$ref"])) + return false; + std::string ref = schema["$ref"]; + size_t pos = 0; + std::string token; + json current = schema_refs; + while ((pos = ref.find('/')) != std::string::npos) + { + token = ref.substr(0, pos); + if (token != "#") + { + if (!current.contains(token)) + return false; + current = current[token]; + } + ref.erase(0, pos + 1); + } + return validate(value, current[ref], schema_refs); + } + else if (schema.contains("allOf")) + { // we have a list of schemas that must all be valid.. + for (const auto &s : schema["allOf"].as_array()) + if (!validate(value, s, schema_refs)) + return false; + return true; + } + else if (schema.contains("anyOf")) + { // we have a list of schemas where at least one must be valid.. + for (const auto &s : schema["anyOf"].as_array()) + if (validate(value, s, schema_refs)) + return true; + return false; + } + else if (schema.contains("oneOf")) + { // we have a list of schemas where exactly one must be valid.. + int count = 0; + for (const auto &s : schema["oneOf"].as_array()) + if (validate(value, s, schema_refs)) + count++; + return count == 1; + } + else if (schema.contains("not")) // we have a schema that must not be valid.. + return !validate(value, schema["not"], schema_refs); + return false; + } } // namespace json \ No newline at end of file diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 3e539bb..3aee2fa 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -370,7 +370,7 @@ void test_array_of_scientific_numbers() assert(j0.dump() == "[" + std::to_string(1e+10) + "," + std::to_string(1.23e+10) + "," + std::to_string(.23e+10) + "]"); } -int main(int, char **) +int main() { test_constructors(); test_constructors2(); From b1228bfef8cbac399bb5dd6e5e7b43b6c3f7c6be Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 1 Jul 2024 15:31:25 +0200 Subject: [PATCH 063/137] Refactor json.cpp to use int64_t for schema minimum and maximum values --- src/json.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index f2547c7..1739090 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -360,15 +360,15 @@ namespace json return false; if (schema.contains("minimum")) { - long min = schema["minimum"]; - long v = value; + int64_t min = schema["minimum"]; + int64_t v = value; if (v < min) return false; } if (schema.contains("maximum")) { - long max = schema["maximum"]; - long v = value; + int64_t max = schema["maximum"]; + int64_t v = value; if (v > max) return false; } From 0cd34908d023720be08359e960ad80931e10b27b Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 1 Jul 2024 16:06:02 +0200 Subject: [PATCH 064/137] Refactor JSON schema validation code for improved readability and maintainability --- src/json.cpp | 6 +-- tests/test_json.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index 1739090..490fba1 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -333,6 +333,7 @@ namespace json return false; if (schema.contains("enum")) return std::find(schema["enum"].as_array().begin(), schema["enum"].as_array().end(), value) != schema["enum"].as_array().end(); + return true; } else if (schema["type"] == "number") { @@ -385,8 +386,6 @@ namespace json } else if (schema.contains("$ref")) { // we have a reference to another schema.. - if (!schema_refs.contains(schema["$ref"])) - return false; std::string ref = schema["$ref"]; size_t pos = 0; std::string token; @@ -398,7 +397,8 @@ namespace json { if (!current.contains(token)) return false; - current = current[token]; + json next = current[token]; + current = next; } ref.erase(0, pos + 1); } diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 3aee2fa..28ab4eb 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -370,6 +370,110 @@ void test_array_of_scientific_numbers() assert(j0.dump() == "[" + std::to_string(1e+10) + "," + std::to_string(1.23e+10) + "," + std::to_string(.23e+10) + "]"); } +void test_validate() +{ + // Test case 1: Validating an object against a schema with properties + json::json value1 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema1 = { + {"type", "object"}, + {"properties", {{"name", {{"type", "string"}}}, {"age", {{"type", "number"}}}, {"city", {{"type", "string"}}}}}}; + json::json schemaRefs1 = {}; + assert(json::validate(value1, schema1, schemaRefs1)); + + // Test case 2: Validating an array against a schema with items + json::json value2 = {1, 2, 3, 4, 5}; + json::json schema2 = { + {"type", "array"}, + {"items", {{"type", "number"}}}}; + json::json schemaRefs2 = {}; + assert(json::validate(value2, schema2, schemaRefs2)); + + // Test case 3: Validating a string against a schema with enum values + json::json value3 = "apple"; + json::json schema3 = { + {"type", "string"}, + {"enum", {"apple", "banana", "orange"}}}; + json::json schemaRefs3 = {}; + assert(json::validate(value3, schema3, schemaRefs3)); + + // Test case 4: Validating a number against a schema with minimum and maximum values + json::json value4 = 10.5; + json::json schema4 = { + {"type", "number"}, + {"minimum", 0}, + {"maximum", 20}}; + json::json schemaRefs4 = {}; + assert(json::validate(value4, schema4, schemaRefs4)); + + // Test case 5: Validating a boolean against a schema with type boolean + json::json value5 = true; + json::json schema5 = { + {"type", "boolean"}}; + json::json schemaRefs5 = {}; + assert(json::validate(value5, schema5, schemaRefs5)); + + // Test case 6: Validating null against a schema with type null + json::json value6 = nullptr; + json::json schema6 = { + {"type", "null"}}; + json::json schemaRefs6 = {}; + assert(json::validate(value6, schema6, schemaRefs6)); + + // Test case 7: Validating an object against a schema with a reference + json::json value7 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema7 = { + {"$ref", "#/definitions/person"}}; + json::json schemaRefs7 = { + {"definitions", {{"person", {{"type", "object"}, {"properties", {{"name", {{"type", "string"}}}, {"age", {{"type", "number"}}}, {"city", {{"type", "string"}}}}}}}}}}; + assert(json::validate(value7, schema7, schemaRefs7)); + + // Test case 8: Validating an object against a schema with allOf + json::json value8 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema8 = { + {"allOf", std::vector{{{"type", "object"}, {"properties", {{"name", {{"type", "string"}}}, {"age", {{"type", "number"}}}, {"city", {{"type", "string"}}}}}}}}}; + json::json schemaRefs8 = {}; + assert(json::validate(value8, schema8, schemaRefs8)); + + // Test case 9: Validating an object against a schema with anyOf + json::json value9 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema9 = { + {"anyOf", std::vector{{{"type", "object"}, {"properties", {{"name", {{"type", "string"}}}, {"age", {{"type", "number"}}}, {"city", {{"type", "string"}}}}}}}}}; + json::json schemaRefs9 = {}; + assert(json::validate(value9, schema9, schemaRefs9)); + + // Test case 10: Validating an object against a schema with oneOf + json::json value10 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema10 = { + {"oneOf", std::vector{{{"type", "object"}, {"properties", {{"name", {{"type", "string"}}}, {"age", {{"type", "number"}}}, {"city", {{"type", "string"}}}}}}}}}; + json::json schemaRefs10 = {}; + assert(json::validate(value10, schema10, schemaRefs10)); + + // Test case 11: Validating an object against a schema with not + json::json value11 = { + {"name", "John"}, + {"age", 30}, + {"city", "New York"}}; + json::json schema11 = { + {"not", {{"type", "string"}}}}; + json::json schemaRefs11 = {}; + assert(json::validate(value11, schema11, schemaRefs11)); +} + int main() { test_constructors(); @@ -393,5 +497,7 @@ int main() test_scientific_numbers(); test_array_of_scientific_numbers(); + test_validate(); + return 0; } \ No newline at end of file From 6c2d033a1b5bd983641041d7a57ad9ddfa517c64 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 7 Sep 2024 16:44:11 +0200 Subject: [PATCH 065/137] Refactor JSON object access and initialization --- include/json.hpp | 69 +++++++++++++++++++++++++++++++++++++----------- src/json.cpp | 8 +++--- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 0b5380f..0223137 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -60,7 +60,7 @@ namespace json value = std::vector(); break; case json_type::object: - value = std::map(); + value = std::map>(); break; } } @@ -137,6 +137,22 @@ namespace json * @param str The string to initialize the `json` object with. */ json(const std::string &str) noexcept : value(str) {} + /** + * @brief Constructs a `json` object from a string rvalue reference. + * + * This constructor initializes a `json` object with the given string rvalue reference. + * + * @param str The string rvalue reference to initialize the `json` object with. + */ + json(std::string &&str) noexcept : value(std::move(str)) {} + /** + * @brief Constructs a `json` object from a string view. + * + * This constructor initializes a `json` object with the given string view. + * + * @param str The string view to initialize the `json` object with. + */ + json(std::string_view str) noexcept : value(std::string(str)) {} /** * @brief Constructs a `json` object from a C-style string. * @@ -316,7 +332,7 @@ namespace json * @param str The key to access the value. * @return A reference to the value associated with the key. */ - json &operator[](const char *str) { return operator[](std::string(str)); } + json &operator[](const char *str) { return std::get>>(value)[str]; } /** * @brief Accesses the value associated with the specified key in the JSON object. * @@ -326,7 +342,28 @@ namespace json * @param str The key to access the value. * @return A reference to the value associated with the key. */ - const json &operator[](const char *str) const { return operator[](std::string(str)); } + const json &operator[](const char *str) const { return std::get>>(value).at(str); } + + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows you to access the value associated with the specified key in the JSON object. + * If the key does not exist, it will be created and associated with a default-constructed JSON value. + * + * @param key The key to access the value. + * @return A reference to the value associated with the key. + */ + json &operator[](std::string_view key) { return std::get>>(value)[std::string(key)]; } + /** + * @brief Accesses the value associated with the specified key in the JSON object. + * + * This operator allows you to access the value associated with the specified key in the JSON object. + * If the key does not exist, it will be created and associated with a default-constructed JSON value. + * + * @param key The key to access the value. + * @return A reference to the value associated with the key. + */ + const json &operator[](std::string_view key) const { return std::get>>(value).at(std::string(key)); } /** * @brief Accesses the value associated with the specified key in the JSON object. @@ -337,7 +374,7 @@ namespace json * @param key The key to access the value. * @return A reference to the value associated with the key. */ - json &operator[](const std::string &key) { return std::get>(value)[key]; } + json &operator[](const std::string &key) { return std::get>>(value)[key]; } /** * @brief Accesses the value associated with the specified key in the JSON object. * @@ -347,7 +384,7 @@ namespace json * @param key The key to access the value. * @return A reference to the value associated with the key. */ - const json &operator[](const std::string &key) const { return std::get>(value).at(key); } + const json &operator[](const std::string &key) const { return std::get>>(value).at(key); } /** * @brief Accesses the element at the specified index in the JSON object. @@ -437,7 +474,7 @@ namespace json case json_type::array: return std::get>(value).size(); case json_type::object: - return std::get>(value).size(); + return std::get>>(value).size(); default: return 0; } @@ -449,7 +486,7 @@ namespace json * @param key The key to check for. * @return True if the key is present in the JSON object, false otherwise. */ - [[nodiscard]] bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>(value).count(key) > 0; } + [[nodiscard]] bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>>(value).count(key) > 0; } /** * @brief Overloads the equality operator for comparing two json objects. @@ -653,7 +690,7 @@ namespace json case 5: return !std::get(value).empty(); case 6: - return !std::get>(value).empty(); + return !std::get>>(value).empty(); case 7: return !std::get>(value).empty(); default: @@ -764,20 +801,20 @@ namespace json } /** - * @brief Conversion operator to std::map. + * @brief Conversion operator to std::map>. * - * This operator converts the JSON value to a std::map. + * This operator converts the JSON value to a std::map>. * The conversion is based on the type of the JSON value. * - * @return The std::map value of the JSON object. + * @return The std::map> value of the JSON object. */ - operator std::map() const noexcept { return value.index() == 6 ? std::get>(value) : std::map(); } + operator std::map>() const noexcept { return value.index() == 6 ? std::get>>(value) : std::map>(); } /** * Returns a constant reference to the underlying map object if the JSON value is an object. * * @return A constant reference to the underlying map object. */ - [[nodiscard]] const std::map &as_object() const noexcept { return std::get>(value); } + [[nodiscard]] const std::map> &as_object() const noexcept { return std::get>>(value); } /** * @brief Conversion operator to std::vector. @@ -829,7 +866,7 @@ namespace json * * @param key The key of the element to be erased. */ - void erase(const std::string &key) { std::get>(value).erase(key); } + void erase(const std::string &key) { std::get>>(value).erase(key); } /** * @brief Erases an element at the specified index from the JSON array. @@ -875,7 +912,7 @@ namespace json case 6: { std::string str = "{"; - const auto &m = std::get>(value); + const auto &m = std::get>>(value); for (auto it = m.begin(); it != m.end(); ++it) { if (it != m.begin()) @@ -904,7 +941,7 @@ namespace json friend std::ostream &operator<<(std::ostream &os, const json &j) { return os << j.dump(); } private: - std::variant, std::vector> value; + std::variant>, std::vector> value; }; /** diff --git a/src/json.cpp b/src/json.cpp index 490fba1..57dcb0e 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -9,15 +9,15 @@ namespace json { if (init.size() == 2 && init.begin()->get_type() == json_type::string) { // we have a key-value pair.. - value = std::map(); - std::get>(value)[static_cast(*init.begin())] = *(init.begin() + 1); + value = std::map>(); + std::get>>(value)[static_cast(*init.begin())] = *(init.begin() + 1); } else if (std::all_of(init.begin(), init.end(), [](const json &j) { return j.get_type() == json_type::object && j.size() == 1; })) { // we have an array of key-value pairs.. - value = std::map(); + value = std::map>(); for (const auto &j : init) - std::get>(value)[static_cast(j.as_object().begin()->first)] = j.as_object().begin()->second; + std::get>>(value)[static_cast(j.as_object().begin()->first)] = j.as_object().begin()->second; } else { // we have an array.. From 55cf7d7f6baffebce479bd0deecbb746cdc33635 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 4 Oct 2024 18:16:51 +0200 Subject: [PATCH 066/137] Refactor JSON object access and initialization to include schema validation for array items count --- src/json.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/json.cpp b/src/json.cpp index 57dcb0e..a7a36f6 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -322,6 +322,18 @@ namespace json { if (value.get_type() != json_type::array) return false; + if (schema.contains("minItems")) + { + size_t min = schema["minItems"]; + if (value.size() < min) + return false; + } + if (schema.contains("maxItems")) + { + size_t max = schema["maxItems"]; + if (value.size() > max) + return false; + } for (const auto &v : value.as_array()) if (!validate(v, schema["items"], schema_refs)) return false; From 3aa6a4c662a8602cf56c8f679a896f836e824879 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 4 Oct 2024 18:23:54 +0200 Subject: [PATCH 067/137] Refactor JSON schema validation code to use uint64_t for minimum and maximum values --- src/json.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index a7a36f6..265c1f5 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -324,13 +324,13 @@ namespace json return false; if (schema.contains("minItems")) { - size_t min = schema["minItems"]; + uint64_t min = schema["minItems"]; if (value.size() < min) return false; } if (schema.contains("maxItems")) { - size_t max = schema["maxItems"]; + uint64_t max = schema["maxItems"]; if (value.size() > max) return false; } From 2e5ca0eab73591d0e673be2a95b2ee7378647842 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 21 Oct 2024 11:12:54 +0200 Subject: [PATCH 068/137] Refactor JSON value to string conversion for improved readability and maintainability --- include/json.hpp | 52 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 0223137..c3c63ef 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -897,19 +897,51 @@ namespace json { switch (value.index()) { - case 0: + case 0: // null return "null"; - case 1: + case 1: // boolean return std::get(value) ? "true" : "false"; - case 2: + case 2: // int64_t return std::to_string(std::get(value)); - case 3: + case 3: // uint64_t return std::to_string(std::get(value)); - case 4: + case 4: // double return std::to_string(std::get(value)); - case 5: - return "\"" + std::get(value) + "\""; - case 6: + case 5: // string + { + std::string escaped; + escaped.reserve(std::get(value).size()); + for (char c : std::get(value)) + switch (c) + { + case '"': + escaped += "\\\""; + break; + case '\\': + escaped += "\\\\"; + break; + case '\b': + escaped += "\\b"; + break; + case '\f': + escaped += "\\f"; + break; + case '\n': + escaped += "\\n"; + break; + case '\r': + escaped += "\\r"; + break; + case '\t': + escaped += "\\t"; + break; + default: + escaped += c; + break; + } + return "\"" + escaped + "\""; + } + case 6: // object { std::string str = "{"; const auto &m = std::get>>(value); @@ -921,7 +953,7 @@ namespace json } return str + "}"; } - case 7: + case 7: // array { std::string str = "["; const auto &v = std::get>(value); @@ -933,7 +965,7 @@ namespace json } return str + "]"; } - default: + default: // should never happen return ""; } } From 50e558f34ad37d6f4cd92637496680107574822c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 21 Oct 2024 14:07:58 +0200 Subject: [PATCH 069/137] Refactor JSON value to string conversion for improved readability and maintainability --- tests/test_json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 28ab4eb..34f4caf 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -183,6 +183,7 @@ void test_json_escapes() } )"; json::json j = json::load(ss); + assert(j["choices"][0]["message"]["function_call"]["arguments"].dump() == "\"{\\n \\\"purpose\\\": \\\"attività personalizzate\\\"\\n}\""); } void test_json_special_chars() From 159dc90991f1bb0a7964363bb5d56a276608748d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 24 Oct 2024 16:10:24 +0200 Subject: [PATCH 070/137] Refactor extern/utils submodule to use updated commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 837aecd..eaf3ddd 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 837aecdb8c4b3d97db53546d353175c2c05a6a3b +Subproject commit eaf3dddc25f4f27a9b0cd627838a4ce27598f72a From dbbbf3f6c80d396ba63c8d023641c96dd2aa335f Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 24 Oct 2024 21:22:11 +0200 Subject: [PATCH 071/137] Update subproject commit reference --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 837aecd..eaf3ddd 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 837aecdb8c4b3d97db53546d353175c2c05a6a3b +Subproject commit eaf3dddc25f4f27a9b0cd627838a4ce27598f72a From 336435c16119465bf029d964389c1a0ca16632eb Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 31 Oct 2024 15:30:11 +0100 Subject: [PATCH 072/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index eaf3ddd..9478632 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit eaf3dddc25f4f27a9b0cd627838a4ce27598f72a +Subproject commit 94786320c7a306f4b0f9bbb62ed9e9d03d99c923 From 3c5562f1c33a8c3a0e1f27736d1409aaef2c9e88 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 5 Nov 2024 12:55:40 +0100 Subject: [PATCH 073/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 9478632..e2220f6 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 94786320c7a306f4b0f9bbb62ed9e9d03d99c923 +Subproject commit e2220f63930a23c49295efcc0041f46ff89d637c From aa602f21c850ecdd159825bbf1c97f118b2140f4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Nov 2024 12:40:55 +0100 Subject: [PATCH 074/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index e2220f6..e7ee03d 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e2220f63930a23c49295efcc0041f46ff89d637c +Subproject commit e7ee03da05c126908ca5deff0089a9feaaa03387 From 4656f8c4c52cebddbf718d91a0a9af7fb13b7ad2 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Nov 2024 12:49:07 +0100 Subject: [PATCH 075/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index e7ee03d..01bb5be 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e7ee03da05c126908ca5deff0089a9feaaa03387 +Subproject commit 01bb5be2f79dcc4e70f8e37ede554fe05eaf2644 From 6da0457c738547a92e75904e2e4cf8784ed35de9 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Nov 2024 14:21:11 +0100 Subject: [PATCH 076/137] Add include to json.hpp --- include/json.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/json.hpp b/include/json.hpp index c3c63ef..6e39eb7 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include From bf350db66fbfbfdb301f626fd457d141d0c9724a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 11 Nov 2024 15:04:11 +0100 Subject: [PATCH 077/137] Remove redundant include from json.cpp and add it to json.hpp --- include/json.hpp | 1 + src/json.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/json.hpp b/include/json.hpp index 6e39eb7..185b3d1 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace json { diff --git a/src/json.cpp b/src/json.cpp index 265c1f5..bb8f0d3 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,5 +1,4 @@ #include "json.hpp" -#include #include #include From 66b2a9d9277f90a3382814f30085573079314b57 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 20 Nov 2024 14:30:47 +0100 Subject: [PATCH 078/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 01bb5be..e088f59 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 01bb5be2f79dcc4e70f8e37ede554fe05eaf2644 +Subproject commit e088f59cd2f4b90b413748cb515f9cb8179f6f7a From 485fb4cd7baf9c77eeb95545d5134d3009f9fc22 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 16 Dec 2024 11:53:12 +0100 Subject: [PATCH 079/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index e088f59..1a86028 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e088f59cd2f4b90b413748cb515f9cb8179f6f7a +Subproject commit 1a86028d0765d6d1a0436f2a3709ae424b6caf0e From 99964c982cf450d247ff2075e41eb62a1b1cfff3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 7 Jan 2025 16:43:33 +0100 Subject: [PATCH 080/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1a86028..121447a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1a86028d0765d6d1a0436f2a3709ae424b6caf0e +Subproject commit 121447a4b78076c36bd467b17cb14e5de87a42eb From 9467b8b491a33043f9784f9408ed5314a5edb323 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 14 Jan 2025 18:05:45 +0100 Subject: [PATCH 081/137] Enhance test executable configuration with compiler warnings and coverage options --- tests/CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 78225ce..38508cd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,16 @@ add_executable(json_lib_tests test_json.cpp) add_dependencies(json_lib_tests json) target_link_libraries(json_lib_tests PRIVATE json) +if(MSVC) + target_compile_options(json_lib_tests PRIVATE /W4) +else() + target_compile_options(json_lib_tests PRIVATE -Wall -Wextra -Wpedantic -fsanitize=undefined) + if (ENABLE_COVERAGE) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(json_lib_tests PRIVATE --coverage) + target_link_libraries(json_lib_tests PUBLIC gcov) + endif() + endif() +endif() add_test(NAME JSON_LibTest COMMAND json_lib_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file From 1eaba66293e32b99a006826b4e94dd38222fe2c1 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 04:43:38 +0100 Subject: [PATCH 082/137] Refactor CMake configuration to use setup_sanitizers and update extern/utils submodule --- CMakeLists.txt | 12 +----------- extern/utils | 2 +- tests/CMakeLists.txt | 12 +----------- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa8a669..19cb59e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,17 +19,7 @@ if(BUILD_TESTING) add_subdirectory(tests) endif() -if(MSVC) - target_compile_options(json PRIVATE /W4) -else() - target_compile_options(json PRIVATE -Wall -Wextra -Wpedantic) - if (ENABLE_COVERAGE) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(json PRIVATE --coverage) - target_link_libraries(json PUBLIC gcov) - endif() - endif() -endif() +setup_sanitizers(json) set(CPACK_PROJECT_NAME json) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) diff --git a/extern/utils b/extern/utils index 121447a..8bb4edd 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 121447a4b78076c36bd467b17cb14e5de87a42eb +Subproject commit 8bb4edda15fa653dcd0474effdb41972954c95f4 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 38508cd..c74e5d3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,16 +1,6 @@ add_executable(json_lib_tests test_json.cpp) add_dependencies(json_lib_tests json) target_link_libraries(json_lib_tests PRIVATE json) -if(MSVC) - target_compile_options(json_lib_tests PRIVATE /W4) -else() - target_compile_options(json_lib_tests PRIVATE -Wall -Wextra -Wpedantic -fsanitize=undefined) - if (ENABLE_COVERAGE) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(json_lib_tests PRIVATE --coverage) - target_link_libraries(json_lib_tests PUBLIC gcov) - endif() - endif() -endif() +setup_sanitizers(json_lib_tests) add_test(NAME JSON_LibTest COMMAND json_lib_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file From 616b008fc73fa939c6d35823a47f28a90bbeff71 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 04:44:17 +0100 Subject: [PATCH 083/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 8bb4edd..06faeeb 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 8bb4edda15fa653dcd0474effdb41972954c95f4 +Subproject commit 06faeebe4d79864c98fea70bdf2c6c6e826b00f8 From d9c835b2a32c4a6edb9ab92d9baa67fd9b6925c0 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 05:12:41 +0100 Subject: [PATCH 084/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 06faeeb..e5e1586 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 06faeebe4d79864c98fea70bdf2c6c6e826b00f8 +Subproject commit e5e1586a445dfab72f555c0cf0a977d73e0133ec From 436acb02dba00f32bac1d9f650b57bf85122c148 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 10:27:45 +0100 Subject: [PATCH 085/137] Update extern/utils submodule and refactor json class constructors to use string_view --- extern/utils | 2 +- include/json.hpp | 53 +++++++++++------------------------------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/extern/utils b/extern/utils index e5e1586..3a26208 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e5e1586a445dfab72f555c0cf0a977d73e0133ec +Subproject commit 3a2620896a420c06de9bbca9f262bdfd11954319 diff --git a/include/json.hpp b/include/json.hpp index 185b3d1..7b5a750 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -131,14 +131,6 @@ namespace json * @param d The double value to initialize the `json` object with. */ json(double d) noexcept : value(d) {} - /** - * @brief Constructs a `json` object from a string. - * - * This constructor initializes a `json` object with the given string. - * - * @param str The string to initialize the `json` object with. - */ - json(const std::string &str) noexcept : value(str) {} /** * @brief Constructs a `json` object from a string rvalue reference. * @@ -154,7 +146,7 @@ namespace json * * @param str The string view to initialize the `json` object with. */ - json(std::string_view str) noexcept : value(std::string(str)) {} + json(std::string_view str) noexcept : value(str.data()) {} /** * @brief Constructs a `json` object from a C-style string. * @@ -162,7 +154,7 @@ namespace json * * @param str The C-style string to be converted into a `json` object. */ - json(const char *str) noexcept : value(std::string(str)) {} + json(const char *str) noexcept : value(str) {} /** * @brief Constructs a `json` object from a vector of `json` objects. * @@ -233,9 +225,9 @@ namespace json * @param str The string value to assign. * @return A reference to the modified JSON object. */ - json &operator=(const std::string &str) noexcept + json &operator=(std::string_view str) noexcept { - value = str; + value = str.data(); return *this; } @@ -250,7 +242,7 @@ namespace json */ json &operator=(const char *str) noexcept { - value = std::string(str); + value = str; return *this; } @@ -355,28 +347,7 @@ namespace json * @param key The key to access the value. * @return A reference to the value associated with the key. */ - json &operator[](std::string_view key) { return std::get>>(value)[std::string(key)]; } - /** - * @brief Accesses the value associated with the specified key in the JSON object. - * - * This operator allows you to access the value associated with the specified key in the JSON object. - * If the key does not exist, it will be created and associated with a default-constructed JSON value. - * - * @param key The key to access the value. - * @return A reference to the value associated with the key. - */ - const json &operator[](std::string_view key) const { return std::get>>(value).at(std::string(key)); } - - /** - * @brief Accesses the value associated with the specified key in the JSON object. - * - * This operator allows you to access the value associated with the specified key in the JSON object. - * If the key does not exist, it will be created and associated with a default-constructed JSON value. - * - * @param key The key to access the value. - * @return A reference to the value associated with the key. - */ - json &operator[](const std::string &key) { return std::get>>(value)[key]; } + json &operator[](std::string_view key) { return std::get>>(value)[key.data()]; } /** * @brief Accesses the value associated with the specified key in the JSON object. * @@ -386,7 +357,7 @@ namespace json * @param key The key to access the value. * @return A reference to the value associated with the key. */ - const json &operator[](const std::string &key) const { return std::get>>(value).at(key); } + const json &operator[](std::string_view key) const { return std::get>>(value).at(key.data()); } /** * @brief Accesses the element at the specified index in the JSON object. @@ -488,7 +459,7 @@ namespace json * @param key The key to check for. * @return True if the key is present in the JSON object, false otherwise. */ - [[nodiscard]] bool contains(const std::string &key) const { return get_type() == json_type::object && std::get>>(value).count(key) > 0; } + [[nodiscard]] bool contains(std::string_view key) const { return get_type() == json_type::object && std::get>>(value).count(key) > 0; } /** * @brief Overloads the equality operator for comparing two json objects. @@ -576,7 +547,7 @@ namespace json * @param str The string to compare with. * @return true if the json object is equal to the string, false otherwise. */ - [[nodiscard]] bool operator==(const std::string &str) const noexcept { return value.index() == 5 && std::get(value) == str; } + [[nodiscard]] bool operator==(std::string_view str) const noexcept { return value.index() == 5 && std::get(value) == str; } /** * @brief Overloads the equality operator for comparing a json object with a C-style string. * @@ -658,7 +629,7 @@ namespace json * @param str The string to compare with. * @return true if the json object is not equal to the string, false otherwise. */ - [[nodiscard]] bool operator!=(const std::string &str) const noexcept { return value.index() != 5 || std::get(value) != str; } + [[nodiscard]] bool operator!=(std::string_view str) const noexcept { return value.index() != 5 || std::get(value) != str; } /** * @brief Overloads the inequality operator for comparing a json object with a C-style string. * @@ -868,7 +839,7 @@ namespace json * * @param key The key of the element to be erased. */ - void erase(const std::string &key) { std::get>>(value).erase(key); } + void erase(std::string_view key) { std::get>>(value).erase(key.data()); } /** * @brief Erases an element at the specified index from the JSON array. @@ -1000,7 +971,7 @@ namespace json * @param str The string containing the JSON object. * @return The loaded JSON object. */ - [[nodiscard]] inline json load(const std::string &str) { return load(str.c_str()); } + [[nodiscard]] inline json load(const std::string &str); /** * Validates a JSON value against a JSON schema. From e79c1a6f63faadc86ac5ca7d6351f12a5c1565ed Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 10:28:08 +0100 Subject: [PATCH 086/137] Add overload for load function to accept std::string_view --- src/json.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/json.cpp b/src/json.cpp index bb8f0d3..f3be01c 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -300,6 +300,13 @@ namespace json return load(ss); } + json load(std::string_view str) + { + std::stringstream ss; + ss << str; + return load(ss); + } + bool validate(const json &value, const json &schema, const json &schema_refs) { if (schema.contains("type")) From 31845b6e8df55e9710eb71700ae8c5040afeceb3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 15 Jan 2025 12:02:25 +0100 Subject: [PATCH 087/137] Refactor load function to accept std::string_view and remove char* overload --- include/json.hpp | 10 +--------- src/json.cpp | 7 ------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 7b5a750..a306bf8 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -957,21 +957,13 @@ namespace json */ [[nodiscard]] json load(std::istream &is); - /** - * Loads a JSON object from a string. - * - * @param str The input string containing the JSON data. - * @return A JSON object representing the parsed data. - */ - [[nodiscard]] json load(const char *str); - /** * Loads a JSON object from a string. * * @param str The string containing the JSON object. * @return The loaded JSON object. */ - [[nodiscard]] inline json load(const std::string &str); + [[nodiscard]] json load(std::string_view str); /** * Validates a JSON value against a JSON schema. diff --git a/src/json.cpp b/src/json.cpp index f3be01c..915bb47 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -293,13 +293,6 @@ namespace json } } - json load(const char *str) - { - std::stringstream ss; - ss << str; - return load(ss); - } - json load(std::string_view str) { std::stringstream ss; From 1bb445153a385604e6982b5a08304bdbc6d196fb Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 16:31:02 +0100 Subject: [PATCH 088/137] Update GitHub Actions workflow for multi-platform builds and enhance testing steps --- .github/workflows/cmake.yml | 180 +++++++++++++++++++++++++++++------- extern/utils | 2 +- 2 files changed, 147 insertions(+), 35 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index cc26643..0c5174d 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -3,58 +3,170 @@ name: CMake on: [push, pull_request] jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - build_type: [Debug, Release] - - env: - BUILD_TYPE: ${{ matrix.build_type }} + ubuntu-address-undefined-coverage: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: Install dependencies (Ubuntu) - if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' + - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y lcov valgrind - - - name: Configure CMake (Debug) - if: matrix.build_type == 'Debug' - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON + sudo apt-get install -y cmake lcov - name: Configure CMake - if: matrix.build_type != 'Debug' - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBASAN=ON -DENABLE_COV=ON - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build build --config Debug - - name: Test with coverage and memory sanitizer - if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' - working-directory: ${{github.workspace}}/build - run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test -T Coverage -T MemCheck + - name: Test with address sanitizer, undefined behavior sanitizer and coverage + working-directory: build + run: ctest -C Debug --rerun-failed --output-on-failure -T Test -T Coverage + + - name: Make coverage report + run: | + lcov --capture --directory build --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + lcov --list coverage.info - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} - - name: Test without coverage and memory sanitizer - if: matrix.build_type == 'Release' || matrix.os != 'ubuntu-latest' - working-directory: ${{github.workspace}}/build - run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure -T Test + ubuntu-memcheck: + runs-on: ubuntu-latest - - name: Make coverage report - if: matrix.build_type == 'Debug' && matrix.os == 'ubuntu-latest' + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies run: | - lcov --capture --directory ${{github.workspace}}/build --output-file coverage.info - lcov --remove coverage.info '/usr/*' --output-file coverage.info - lcov --list coverage.info \ No newline at end of file + sudo apt-get update + sudo apt-get install -y cmake valgrind + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build --config Debug + + - name: Test with memory checker + working-directory: build + run: ctest -C Debug --rerun-failed --output-on-failure -T Test -T MemCheck + + windows-debug: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + choco install cmake + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build --config Debug + + - name: Test with address sanitizer + working-directory: build + run: ctest -C Debug --rerun-failed --output-on-failure -T Test + + macos-address-undefined: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + brew install cmake + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBSAN=ON + + - name: Build + run: cmake --build build --config Debug + + - name: Test with address sanitizer and undefined behavior sanitizer + working-directory: build + run: ctest -C Debug --rerun-failed --output-on-failure -T Test + + ubuntu-release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build --config Release + + - name: Test + working-directory: build + run: ctest -C Release --rerun-failed --output-on-failure -T Test + + windows-release: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + choco install cmake + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build --config Release + + - name: Test + working-directory: build + run: ctest -C Release --rerun-failed --output-on-failure -T Test + + macos-release: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + brew install cmake + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build --config Release + + - name: Test + working-directory: build + run: ctest -C Release --rerun-failed --output-on-failure -T Test diff --git a/extern/utils b/extern/utils index 3a26208..ec9708e 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 3a2620896a420c06de9bbca9f262bdfd11954319 +Subproject commit ec9708e14c39ef0793df14e77401ae53f3a3d2e4 From ff7f369069d057343f3381c4a88e48ccbddad363 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 17:27:20 +0100 Subject: [PATCH 089/137] Update CMakeLists.txt to ensure utils dependency is always added and refresh extern/utils submodule --- CMakeLists.txt | 6 ++---- extern/utils | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19cb59e..7318df5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,21 +6,19 @@ include(CTest) enable_testing() add_library(json src/json.cpp) -add_dependencies(json utils) target_compile_features(json PUBLIC cxx_std_17) target_include_directories(json PUBLIC $) if(NOT TARGET utils) add_subdirectory(extern/utils) - add_dependencies(json utils) endif() +add_dependencies(json utils) target_link_libraries(json PUBLIC utils) +setup_sanitizers(json) if(BUILD_TESTING) add_subdirectory(tests) endif() -setup_sanitizers(json) - set(CPACK_PROJECT_NAME json) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/extern/utils b/extern/utils index ec9708e..4bf3ca3 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit ec9708e14c39ef0793df14e77401ae53f3a3d2e4 +Subproject commit 4bf3ca3ba4b33c4eb09911ea20baf1e6e6e81722 From a2759823a636c421600ed356287841fb4302e8b6 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 17:34:29 +0100 Subject: [PATCH 090/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 4bf3ca3..5a85f89 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 4bf3ca3ba4b33c4eb09911ea20baf1e6e6e81722 +Subproject commit 5a85f89d511bd82bf7673945d1b6c1925517b762 From df276307dd34ba76b369e587197e57207c7161b0 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 17:41:47 +0100 Subject: [PATCH 091/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 5a85f89..04719b0 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 5a85f89d511bd82bf7673945d1b6c1925517b762 +Subproject commit 04719b0a43d9430fbc5f32e7b74ab71e6ddaf811 From 8ede303f598a3d26a9a821001225e4597d01f024 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 17:56:43 +0100 Subject: [PATCH 092/137] Update CMake minimum version and refresh extern/utils submodule --- .github/workflows/cmake.yml | 2 +- CMakeLists.txt | 2 +- extern/utils | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 0c5174d..d99eeb6 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -17,7 +17,7 @@ jobs: sudo apt-get install -y cmake lcov - name: Configure CMake - run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBASAN=ON -DENABLE_COV=ON + run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBSAN=ON -DENABLE_COV=ON - name: Build run: cmake --build build --config Debug diff --git a/CMakeLists.txt b/CMakeLists.txt index 7318df5..5fe61b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5.0) +cmake_minimum_required(VERSION 3.21) project(json VERSION 0.2.0 LANGUAGES CXX) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") diff --git a/extern/utils b/extern/utils index 04719b0..991c56f 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 04719b0a43d9430fbc5f32e7b74ab71e6ddaf811 +Subproject commit 991c56f5165cab18be36e0c179f474e18d418590 From 01b04e1785f6e2ea1037b613aa58c541f7e79a6b Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 17 Jan 2025 18:20:12 +0100 Subject: [PATCH 093/137] Remove undefined behavior sanitizer flag from CMake and update extern/utils submodule to latest commit --- CMakeLists.txt | 1 - extern/utils | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fe61b6..69733c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.21) project(json VERSION 0.2.0 LANGUAGES CXX) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") include(CTest) enable_testing() diff --git a/extern/utils b/extern/utils index 991c56f..7c7a299 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 991c56f5165cab18be36e0c179f474e18d418590 +Subproject commit 7c7a299f568c6a7fcdebee15f05271c5e5955be0 From 4525aad8628174835862ef35361599ec3b8ee89b Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 23 Jan 2025 11:38:02 +0100 Subject: [PATCH 094/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 7c7a299..3bbd8b8 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 7c7a299f568c6a7fcdebee15f05271c5e5955be0 +Subproject commit 3bbd8b81ecf522833a09c8a81ca644574e9fdada From f8d12b19fcd882641c4992ad50676b976676f238 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 23 Jan 2025 18:06:49 +0100 Subject: [PATCH 095/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 3bbd8b8..22991ef 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 3bbd8b81ecf522833a09c8a81ca644574e9fdada +Subproject commit 22991ef0dd6fd318f194252b942dadc2816c7c6e From 583354981c294a86e4c09287ac7ae269896a51e3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 25 Jan 2025 16:31:20 +0100 Subject: [PATCH 096/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 22991ef..4c6d3d1 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 22991ef0dd6fd318f194252b942dadc2816c7c6e +Subproject commit 4c6d3d18afdb7d87ad981e48c5779fd688cdf92e From fabeb881fdd516ffce19f3266f6f00d3418906b8 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 25 Jan 2025 16:59:21 +0100 Subject: [PATCH 097/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 4c6d3d1..f98852e 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 4c6d3d18afdb7d87ad981e48c5779fd688cdf92e +Subproject commit f98852e88cbf5ac8180572558a2a3ffd68d323a2 From 19fb7637b52510c16b273779ba3bdcccc09dfb59 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 25 Jan 2025 17:32:12 +0100 Subject: [PATCH 098/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index f98852e..ef97b35 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f98852e88cbf5ac8180572558a2a3ffd68d323a2 +Subproject commit ef97b35648e9660d09a30ae4515661662d1a1935 From 043441b2c4cbbb9cdaed954d6439bfdd6fc2f735 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 27 Jan 2025 11:37:48 +0100 Subject: [PATCH 099/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index ef97b35..82a6b79 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit ef97b35648e9660d09a30ae4515661662d1a1935 +Subproject commit 82a6b7946efd39806cfdf6bc3cfc7c1734033be1 From 8030eb30b5c6db08228425f92065e0cad68cfe0c Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 27 Jan 2025 12:07:10 +0100 Subject: [PATCH 100/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 82a6b79..60c9c49 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 82a6b7946efd39806cfdf6bc3cfc7c1734033be1 +Subproject commit 60c9c49b9133676d81534a158d56960d7b53956a From 449e0ee40e81d7e67d74db9914a763768662efa6 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 27 Jan 2025 12:14:16 +0100 Subject: [PATCH 101/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 60c9c49..1d833f5 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 60c9c49b9133676d81534a158d56960d7b53956a +Subproject commit 1d833f584be039ab126b7dc78993d30920dce5d1 From 69f2f7f7d853bcfce858a3974f7bce3943653462 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 27 Jan 2025 18:11:03 +0100 Subject: [PATCH 102/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1d833f5..1e5ddc6 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1d833f584be039ab126b7dc78993d30920dce5d1 +Subproject commit 1e5ddc63ea9b53e4e5a2fc0d5a2b7d924d822828 From 9e6839417364a7143a46a707a240afe96f5e8acb Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 29 Jan 2025 15:56:09 +0100 Subject: [PATCH 103/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1e5ddc6..c77a0fa 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1e5ddc63ea9b53e4e5a2fc0d5a2b7d924d822828 +Subproject commit c77a0fac6246b486fc78815ba81392fb20f64f6e From d1d768d95b67591f977ca0a06e105183de215b0a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 29 Jan 2025 17:46:47 +0100 Subject: [PATCH 104/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index c77a0fa..507feff 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit c77a0fac6246b486fc78815ba81392fb20f64f6e +Subproject commit 507feffca8947b8ca58ac797246300ab8b6b4331 From 20cd1c48673d10e3bcc26d9adcd5b87a3df22c67 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 31 Jan 2025 11:08:46 +0100 Subject: [PATCH 105/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 507feff..ff961d5 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 507feffca8947b8ca58ac797246300ab8b6b4331 +Subproject commit ff961d56ceaef86c28e7b9b5af1a2ab3c8cc9cac From e9aad3e6913034f67cabd56dcdc198c66ae7421a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 4 Feb 2025 12:04:45 +0100 Subject: [PATCH 106/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index ff961d5..6c39826 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit ff961d56ceaef86c28e7b9b5af1a2ab3c8cc9cac +Subproject commit 6c39826870782b474c6fcf79f136c5bfb4905aec From f3fd8efcef30486f8818f3cc221dbedd1ae03d83 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 7 Feb 2025 12:08:45 +0100 Subject: [PATCH 107/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 6c39826..0cf2846 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 6c39826870782b474c6fcf79f136c5bfb4905aec +Subproject commit 0cf2846b79d11fda91ce971f3fc4010808a0e361 From 2d7068d7f487135d7c0d0beed6be2c1e535c82e4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 7 Feb 2025 18:23:56 +0100 Subject: [PATCH 108/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 0cf2846..df8588a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 0cf2846b79d11fda91ce971f3fc4010808a0e361 +Subproject commit df8588ae0ed58b36350b6513c919eabd8fdf28d4 From b694d70c1dd14ebb7be881e1396eb1167c1b41a4 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 8 Feb 2025 04:16:31 +0100 Subject: [PATCH 109/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index df8588a..7881246 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit df8588ae0ed58b36350b6513c919eabd8fdf28d4 +Subproject commit 78812461c40c129c8392a882919e8c67dde5abba From bca0e46803fd670d11e238851c8cdfda8ee60ffc Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 8 Feb 2025 04:17:51 +0100 Subject: [PATCH 110/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 7881246..00b4b76 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 78812461c40c129c8392a882919e8c67dde5abba +Subproject commit 00b4b768a99090b4705f5f1829e09a8c93b4412a From fdec1d1d188ffbe999c09374a54ae512398aca60 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 10 Feb 2025 17:10:59 +0100 Subject: [PATCH 111/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 00b4b76..9871f72 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 00b4b768a99090b4705f5f1829e09a8c93b4412a +Subproject commit 9871f72994659c3b27b8a0a0514cae7c2d3be954 From ba9ee6cc84f48794f2eb1f39db50e551ce678f84 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 13 Feb 2025 12:13:05 +0100 Subject: [PATCH 112/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 9871f72..b66441e 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 9871f72994659c3b27b8a0a0514cae7c2d3be954 +Subproject commit b66441e50324d12172879821d443f99fee4242a5 From 998b825f1a1a28bb817f160e8bbf91e0ac3a4811 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 21 Feb 2025 16:18:41 +0100 Subject: [PATCH 113/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index b66441e..807d05d 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit b66441e50324d12172879821d443f99fee4242a5 +Subproject commit 807d05d60982623b6d70f95acbca52157f9db6ef From 0a54930917c1ab992956573be87e4c7cd32f6491 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 27 Mar 2025 17:48:44 +0100 Subject: [PATCH 114/137] Add whitespace handling for JSON array parsing --- src/json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/json.cpp b/src/json.cpp index 915bb47..c8a2c90 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -149,6 +149,7 @@ namespace json { // we have a json array.. is.get(); json vals(json_type::array); + is >> std::ws; if (is.peek() == ']') { // we have an empty array.. is.get(); From 322501540ccfa7ff401b30f8528153afaa44e215 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 1 Apr 2025 19:13:22 +0200 Subject: [PATCH 115/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 807d05d..76277cf 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 807d05d60982623b6d70f95acbca52157f9db6ef +Subproject commit 76277cfe4592fd78874217e94e43e2a90f118fb0 From 1964cf0e8c5c2bdcccda9a150893d9f5901fc2e8 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 1 Apr 2025 19:33:54 +0200 Subject: [PATCH 116/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 76277cf..d2fac4e 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 76277cfe4592fd78874217e94e43e2a90f118fb0 +Subproject commit d2fac4e780df8dc00c4f7335fb1524bda852f373 From 553256ca370be9276e7cf3e934e15c53cfe4bbe3 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 14 Apr 2025 11:53:30 +0200 Subject: [PATCH 117/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index d2fac4e..bbd9276 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit d2fac4e780df8dc00c4f7335fb1524bda852f373 +Subproject commit bbd927626a3372583cdbbb74f3f9dfa841f6f28a From 036cfd3eba42030ad4f7fa2e3aa1fa3fbc6b5941 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 14 May 2025 11:56:46 +0200 Subject: [PATCH 118/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index bbd9276..f208317 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit bbd927626a3372583cdbbb74f3f9dfa841f6f28a +Subproject commit f208317aeaaa1196a4fe80573da3d4772dcf1dcf From e61e23bd0657278426cba01ed5c91cdf51cd293b Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 8 Jul 2025 17:32:23 +0200 Subject: [PATCH 119/137] Add type-checking methods to json class and update tests --- include/json.hpp | 77 ++++++++++++++++++++++++++++++++++++++++++++- src/json.cpp | 18 +++++------ tests/test_json.cpp | 54 +++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 10 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index a306bf8..c8089ad 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -401,6 +401,81 @@ namespace json */ const json &operator[](size_t index) const { return std::get>(value).at(index); } + /** + * @brief Checks if the JSON value is null. + * + * @return true if the JSON value is null, false otherwise. + */ + [[nodiscard]] bool is_null() const noexcept { return value.index() == 0; } + /** + * @brief Checks if the JSON value is a boolean. + * + * @return true if the JSON value is a boolean, false otherwise. + */ + [[nodiscard]] bool is_boolean() const noexcept { return value.index() == 1; } + /** + * @brief Checks if the JSON value is an integer. + * + * @return true if the JSON value is an integer, false otherwise. + */ + [[nodiscard]] bool is_integer() const noexcept { return value.index() == 2; } + /** + * @brief Checks if the JSON value is an unsigned integer. + * + * @return true if the JSON value is an unsigned integer, false otherwise. + */ + [[nodiscard]] bool is_unsigned() const noexcept { return value.index() == 3; } + /** + * @brief Checks if the JSON value is a floating-point number. + * + * @return true if the JSON value is a floating-point number, false otherwise. + */ + [[nodiscard]] bool is_float() const noexcept { return value.index() == 4; } + /** + * @brief Checks if the JSON value is a number. + * + * This function checks if the JSON value is of type integer, unsigned integer, or floating-point number. + * + * @return true if the JSON value is a number, false otherwise. + */ + [[nodiscard]] bool is_number() const noexcept { return value.index() == 2 || value.index() == 3 || value.index() == 4; } + /** + * @brief Checks if the JSON value is a string. + * + * @return true if the JSON value is a string, false otherwise. + */ + [[nodiscard]] bool is_string() const noexcept { return value.index() == 5; } + /** + * @brief Checks if the JSON value is an object. + * + * @return true if the JSON value is an object, false otherwise. + */ + [[nodiscard]] bool is_object() const noexcept { return value.index() == 6; } + /** + * @brief Checks if the JSON value is an array. + * + * @return true if the JSON value is an array, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { return value.index() == 7; } + + /** + * @brief Checks if the JSON value is a primitive type. + * + * This function checks if the JSON value is of a primitive type, which includes null, boolean, integer, unsigned integer, + * floating-point number, or string. + * + * @return true if the JSON value is a primitive type, false otherwise. + */ + [[nodiscard]] bool is_primitive() const noexcept { return is_null() || is_boolean() || is_integer() || is_unsigned() || is_float() || is_string(); } + /** + * @brief Checks if the JSON value is a structured type. + * + * This function checks if the JSON value is either an object or an array. + * + * @return true if the JSON value is a structured type (object or array), false otherwise. + */ + [[nodiscard]] bool is_structured() const noexcept { return is_object() || is_array(); } + /** * @brief Returns the type of the JSON value. * @@ -459,7 +534,7 @@ namespace json * @param key The key to check for. * @return True if the key is present in the JSON object, false otherwise. */ - [[nodiscard]] bool contains(std::string_view key) const { return get_type() == json_type::object && std::get>>(value).count(key) > 0; } + [[nodiscard]] bool contains(std::string_view key) const { return is_object() && std::get>>(value).count(key) > 0; } /** * @brief Overloads the equality operator for comparing two json objects. diff --git a/src/json.cpp b/src/json.cpp index c8a2c90..9ad2943 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -6,13 +6,13 @@ namespace json { json::json(std::initializer_list init) { - if (init.size() == 2 && init.begin()->get_type() == json_type::string) + if (init.size() == 2 && init.begin()->is_string()) { // we have a key-value pair.. value = std::map>(); std::get>>(value)[static_cast(*init.begin())] = *(init.begin() + 1); } else if (std::all_of(init.begin(), init.end(), [](const json &j) - { return j.get_type() == json_type::object && j.size() == 1; })) + { return j.is_object() && j.size() == 1; })) { // we have an array of key-value pairs.. value = std::map>(); for (const auto &j : init) @@ -307,7 +307,7 @@ namespace json { // we have a type.. if (schema["type"] == "object") { - if (value.get_type() != json_type::object) + if (!value.is_object()) return false; for (const auto &property : schema["properties"].as_object()) { @@ -320,7 +320,7 @@ namespace json } else if (schema["type"] == "array") { - if (value.get_type() != json_type::array) + if (!value.is_array()) return false; if (schema.contains("minItems")) { @@ -341,7 +341,7 @@ namespace json } else if (schema["type"] == "string") { - if (value.get_type() != json_type::string) + if (!value.is_string()) return false; if (schema.contains("enum")) return std::find(schema["enum"].as_array().begin(), schema["enum"].as_array().end(), value) != schema["enum"].as_array().end(); @@ -349,7 +349,7 @@ namespace json } else if (schema["type"] == "number") { - if (value.get_type() != json_type::number) + if (!value.is_number()) return false; if (schema.contains("minimum")) { @@ -369,7 +369,7 @@ namespace json } else if (schema["type"] == "integer") { - if (value.get_type() != json_type::number) + if (!value.is_number()) return false; if (schema.contains("minimum")) { @@ -388,9 +388,9 @@ namespace json return true; } else if (schema["type"] == "boolean") - return value.get_type() == json_type::boolean; + return value.is_boolean(); else if (schema["type"] == "null") - return value.get_type() == json_type::null; + return value.is_null(); else if (schema["type"] == "any") return true; else diff --git a/tests/test_json.cpp b/tests/test_json.cpp index 34f4caf..f3428b3 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -7,39 +7,51 @@ void test_constructors() { json::json j0; assert(j0.get_type() == json::json_type::object); + assert(j0.is_object()); assert(j0.size() == 0); json::json j1(1); assert(j1.get_type() == json::json_type::number); + assert(j1.is_number()); + assert(j1.is_integer()); + assert(!j1.is_float()); assert(j1 == 1); assert(j1.size() == 0); json::json j2(2.0); assert(j2.get_type() == json::json_type::number); + assert(j2.is_number()); + assert(!j2.is_integer()); + assert(j2.is_float()); assert(j2 == 2.0); assert(j2.size() == 0); json::json j3("3"); assert(j3.get_type() == json::json_type::string); + assert(j3.is_string()); assert(j3 == "3"); assert(j3.size() == 0); json::json j4(true); assert(j4.get_type() == json::json_type::boolean); + assert(j4.is_boolean()); assert(j4 == true); assert(j4.size() == 0); json::json j5(nullptr); assert(j5.get_type() == json::json_type::null); + assert(j5.is_null()); assert(j5 == nullptr); assert(j5.size() == 0); json::json j6(json::json_type::array); assert(j6.get_type() == json::json_type::array); + assert(j6.is_array()); assert(j6.size() == 0); json::json j7(json::json_type::object); assert(j7.get_type() == json::json_type::object); + assert(j7.is_object()); assert(j7.size() == 0); } @@ -47,29 +59,36 @@ void test_constructors2() { json::json j0(json::json_type::null); assert(j0.get_type() == json::json_type::null); + assert(j0.is_null()); assert(j0 == nullptr); assert(j0.size() == 0); json::json j1(json::json_type::array); assert(j1.get_type() == json::json_type::array); + assert(j1.is_array()); assert(j1.size() == 0); json::json j2(json::json_type::object); assert(j2.get_type() == json::json_type::object); + assert(j2.is_object()); assert(j2.size() == 0); json::json j3(json::json_type::number); assert(j3.get_type() == json::json_type::number); + assert(j3.is_number()); + assert(j3.is_integer() || j3.is_float()); assert(j3 == 0); assert(j3.size() == 0); json::json j4(json::json_type::string); assert(j4.get_type() == json::json_type::string); + assert(j4.is_string()); assert(j4 == ""); assert(j4.size() == 0); json::json j5(json::json_type::boolean); assert(j5.get_type() == json::json_type::boolean); + assert(j5.is_boolean()); assert(j5 == false); assert(j5.size() == 0); @@ -78,11 +97,14 @@ void test_constructors2() j6.push_back(2); j6.push_back(3); assert(j6.get_type() == json::json_type::array); + assert(j6.is_array()); assert(j6.size() == 3); json::json j7(std::move(j6)); assert(j6.get_type() == json::json_type::null); + assert(j6.is_null()); assert(j7.get_type() == json::json_type::array); + assert(j7.is_array()); assert(j7.size() == 3); assert(j7[0] == 1); assert(j7[1] == 2); @@ -98,35 +120,46 @@ void test_assignments() json::json j1 = 1; assert(j1.get_type() == json::json_type::number); + assert(j1.is_number()); + assert(j1.is_integer()); + assert(!j1.is_float()); assert(j1 == 1); assert(j1.size() == 0); json::json j2 = 2.0; assert(j2.get_type() == json::json_type::number); + assert(j2.is_number()); + assert(!j2.is_integer()); + assert(j2.is_float()); assert(j2 == 2.0); assert(j2.size() == 0); json::json j3 = "3"; assert(j3.get_type() == json::json_type::string); + assert(j3.is_string()); assert(j3 == "3"); assert(j3.size() == 0); json::json j4 = true; assert(j4.get_type() == json::json_type::boolean); + assert(j4.is_boolean()); assert(j4 == true); assert(j4.size() == 0); json::json j5 = nullptr; assert(j5.get_type() == json::json_type::null); + assert(j5.is_null()); assert(j5 == nullptr); assert(j5.size() == 0); json::json j6 = json::json_type::array; assert(j6.get_type() == json::json_type::array); + assert(j6.is_array()); assert(j6.size() == 0); json::json j7 = json::json_type::object; assert(j7.get_type() == json::json_type::object); + assert(j7.is_object()); assert(j7.size() == 0); } @@ -329,6 +362,7 @@ void test_null() json::json j0 = nullptr; assert(j0 == nullptr); assert(j0.get_type() == json::json_type::null); + assert(j0.is_null()); assert(j0.dump() == "null"); } @@ -336,6 +370,7 @@ void test_empty_array() { json::json j0 = json::json_type::array; assert(j0.get_type() == json::json_type::array); + assert(j0.is_array()); assert(j0.dump() == "[]"); } @@ -343,6 +378,7 @@ void test_empty_object() { json::json j0 = json::json_type::object; assert(j0.get_type() == json::json_type::object); + assert(j0.is_object()); assert(j0.dump() == "{}"); } @@ -350,14 +386,23 @@ void test_scientific_numbers() { json::json j0 = 1e+10; assert(j0.get_type() == json::json_type::number); + assert(j0.is_number()); + assert(!j0.is_integer()); + assert(j0.is_float()); assert(j0.dump() == std::to_string(1e+10)); json::json j1 = 1.23e+10; assert(j1.get_type() == json::json_type::number); + assert(j1.is_number()); + assert(!j1.is_integer()); + assert(j1.is_float()); assert(j1.dump() == std::to_string(1.23e+10)); json::json j2 = .23e+10; assert(j2.get_type() == json::json_type::number); + assert(j2.is_number()); + assert(!j2.is_integer()); + assert(j2.is_float()); assert(j2.dump() == std::to_string(.23e+10)); } @@ -368,6 +413,15 @@ void test_array_of_scientific_numbers() j0.push_back(1.23e+10); j0.push_back(.23e+10); assert(j0.get_type() == json::json_type::array); + assert(j0[0].is_number()); + assert(!j0[0].is_integer()); + assert(j0[0].is_float()); + assert(j0[1].is_number()); + assert(!j0[1].is_integer()); + assert(j0[1].is_float()); + assert(j0[2].is_number()); + assert(!j0[2].is_integer()); + assert(j0[2].is_float()); assert(j0.dump() == "[" + std::to_string(1e+10) + "," + std::to_string(1.23e+10) + "," + std::to_string(.23e+10) + "]"); } From 3226084159aee8e410435bd5db786a3aa3ac4fb1 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 8 Jul 2025 17:35:44 +0200 Subject: [PATCH 120/137] Fix brew install command to use --formula for cmake --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d99eeb6..2b29021 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -92,7 +92,7 @@ jobs: - name: Install dependencies run: | - brew install cmake + brew install --formula cmake - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBSAN=ON @@ -159,7 +159,7 @@ jobs: - name: Install dependencies run: | - brew install cmake + brew install --formula cmake - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Release From 12a0ff8431b0c66cb2285e1b65a28d4390d4e93d Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Tue, 8 Jul 2025 17:37:07 +0200 Subject: [PATCH 121/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index f208317..757a30a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f208317aeaaa1196a4fe80573da3d4772dcf1dcf +Subproject commit 757a30a63f53e47556f66878a110e804842a941b From 20c43198d84d23d79ed47b2d9a12dd92393d72b0 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 10 Jul 2025 12:03:14 +0200 Subject: [PATCH 122/137] Update extern/utils submodule to latest commit --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 757a30a..a13fb2a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 757a30a63f53e47556f66878a110e804842a941b +Subproject commit a13fb2ad563ce69492fa42d26f2c4369556fe9a8 From abe237eb98ae2e970cc7bf1d949691f22a4b82fd Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 17 Jul 2025 18:23:03 +0200 Subject: [PATCH 123/137] Initialize json value to nullptr in constructor for json_type::object --- include/json.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index c8089ad..f239a4b 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -42,12 +42,11 @@ namespace json * * @param type The type of the JSON object to be constructed. */ - json(json_type type = json_type::object) noexcept + json(json_type type = json_type::object) noexcept : value(nullptr) { switch (type) { case json_type::null: - value = nullptr; break; case json_type::boolean: value = false; From 2c83ac0dac795a57aca4a858b8e4784b151c5436 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 17 Jul 2025 18:56:44 +0200 Subject: [PATCH 124/137] Initialize json value to nullptr in constructor and handle null case in switch statement --- include/json.hpp | 1 + src/json.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/json.hpp b/include/json.hpp index f239a4b..1928f38 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -47,6 +47,7 @@ namespace json switch (type) { case json_type::null: + value = nullptr; break; case json_type::boolean: value = false; diff --git a/src/json.cpp b/src/json.cpp index 9ad2943..2ee06a1 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -4,7 +4,7 @@ namespace json { - json::json(std::initializer_list init) + json::json(std::initializer_list init) : value(nullptr) { if (init.size() == 2 && init.begin()->is_string()) { // we have a key-value pair.. From 65ab59d6a1d8b5de0fbaa771593191135631d5e9 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 17 Jul 2025 19:39:03 +0200 Subject: [PATCH 125/137] Refactor JSON array initialization in tests to use json constructor --- include/json.hpp | 517 +++++++------------------------------------- tests/test_json.cpp | 8 +- 2 files changed, 85 insertions(+), 440 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 1928f38..b70698b 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -66,103 +66,26 @@ namespace json break; } } + /** * @brief Copy constructor for the json class. * - * This constructor creates a new json object by copying the contents of another json object. + * This constructor creates a new json object as a copy of another json object. * * @param other The json object to be copied. */ - json(const json &other) noexcept : value(other.value) {} - /** - * @brief Move constructor for the json class. - * - * This constructor moves the contents of the `other` object into the newly created `json` object. - * The `other` object is left in a valid but unspecified state. - * - * @param other The `json` object to be moved from. - */ - json(json &&other) noexcept : value(std::move(other.value)) { other.value = nullptr; } - /** - * @brief Constructs a `json` object from a `nullptr_t`. - * - * This constructor creates a `json` object with a null value. - * - * @param[in] nullptr_t A null pointer constant. - */ - json(std::nullptr_t) noexcept : value(nullptr) {} - /** - * @brief Constructs a `json` object from a boolean value. - * - * This constructor initializes a `json` object with the given boolean value. - * - * @param b The boolean value to initialize the `json` object with. - */ - json(bool b) noexcept : value(b) {} - /** - * @brief Constructs a `json` object with an integer value. - * - * This constructor initializes a `json` object with the given integer value. - * - * @param l The integer value to initialize the `json` object with. - */ - json(int l) noexcept : value(static_cast(l)) {} - /** - * @brief Constructs a `json` object from an int64_t value. - * - * This constructor initializes a `json` object with the given int64_t value. - * - * @param l The int64_t value to initialize the `json` object with. - */ - json(int64_t l) noexcept : value(l) {} - /** - * @brief Constructs a `json` object with an unsigned 64-bit integer value. - * - * This constructor initializes a `json` object with the provided unsigned 64-bit integer value. - * - * @param l The unsigned 64-bit integer value to initialize the `json` object with. - */ - json(uint64_t l) noexcept : value(l) {} - /** - * @brief Constructs a `json` object from a double value. - * - * This constructor initializes a `json` object with the given double value. - * - * @param d The double value to initialize the `json` object with. - */ - json(double d) noexcept : value(d) {} - /** - * @brief Constructs a `json` object from a string rvalue reference. - * - * This constructor initializes a `json` object with the given string rvalue reference. - * - * @param str The string rvalue reference to initialize the `json` object with. - */ - json(std::string &&str) noexcept : value(std::move(str)) {} - /** - * @brief Constructs a `json` object from a string view. - * - * This constructor initializes a `json` object with the given string view. - * - * @param str The string view to initialize the `json` object with. - */ - json(std::string_view str) noexcept : value(str.data()) {} - /** - * @brief Constructs a `json` object from a C-style string. - * - * This constructor creates a `json` object by converting the given C-style string into a `std::string`. - * - * @param str The C-style string to be converted into a `json` object. - */ - json(const char *str) noexcept : value(str) {} + json(const json &other) : value(other.value) {} + /** - * @brief Constructs a `json` object from a vector of `json` objects. + * @brief Constructs a JSON object from a value of type T. * - * This constructor initializes a `json` object with the given vector of `json` objects. + * This constructor allows you to create a `json` object from any type that can be converted to a JSON value. + * The type T must be convertible to one of the supported JSON types (null, boolean, number, string, array, or object). * - * @param arr The vector of `json` objects to initialize the `json` object with. + * @param v The value to be converted to a JSON object. */ - json(std::vector &&arr) noexcept : value(std::move(arr)) {} + template , json>, int> = 0> + json(T &&v) : value(std::forward(v)) {} /** * @brief Constructs a `json` object from an initializer list of `json` objects. @@ -190,216 +113,75 @@ namespace json } /** - * @brief Move assignment operator for the json class. - * - * This operator allows the json object to be assigned the contents of another json object using move semantics. - * - * @param other The json object to be moved from. - * @return A reference to the modified json object. - */ - json &operator=(json &&other) noexcept - { - value = std::move(other.value); - return *this; - } - - /** - * @brief Assignment operator overload for assigning a nullptr to a json object. - * - * This operator allows assigning a nullptr to a json object. It sets the value of the json object to nullptr. - * - * @param[in] nullptr_t The nullptr to assign to the json object. - * @return json& A reference to the modified json object. - */ - json &operator=(std::nullptr_t) noexcept - { - value = nullptr; - return *this; - } - - /** - * @brief Assigns a string value to the JSON object. - * - * This assignment operator allows you to assign a string value to a JSON object. - * - * @param str The string value to assign. - * @return A reference to the modified JSON object. - */ - json &operator=(std::string_view str) noexcept - { - value = str.data(); - return *this; - } - - /** - * @brief Assignment operator overload for assigning a C-style string to a json object. - * - * This operator allows you to assign a C-style string to a json object. The C-style string - * is converted to a std::string and assigned to the 'value' member variable of the json object. - * - * @param str The C-style string to assign to the json object. - * @return A reference to the modified json object. - */ - json &operator=(const char *str) noexcept - { - value = str; - return *this; - } - - /** - * @brief Assignment operator for assigning a boolean value to a JSON object. - * - * This operator assigns a boolean value to a JSON object and returns a reference to the modified object. - * - * @param b The boolean value to be assigned. - * @return A reference to the modified JSON object. - */ - json &operator=(bool b) noexcept - { - value = b; - return *this; - } - - /** - * @brief Assignment operator for assigning an integer value to a JSON object. + * @brief Assignment operator for the json class. * - * This operator assigns the given integer value to the JSON object and returns a reference to the modified object. + * This operator allows you to assign a value of type T to the current json object. + * The type T must be convertible to one of the supported JSON types (null, boolean, number, string, array, or object). * - * @param l The integer value to assign. - * @return A reference to the modified JSON object. + * @param v The value to be assigned to the json object. + * @return A reference to the current json object after assignment. */ - json &operator=(int l) noexcept + template , json>, int> = 0> + json &operator=(T &&v) { - value = static_cast(l); + value = std::forward(v); return *this; } /** - * @brief Assigns an int64_t value to the JSON object. + * @brief Accesses the JSON value associated with the given key. * - * This assignment operator allows you to assign an int64_t value to a JSON object. + * This operator allows access to the JSON object element corresponding to the specified key. + * If the key does not exist, a new element is created and returned. * - * @param l The int64_t value to assign. - * @return A reference to the modified JSON object. + * @tparam Key Type of the key, which must be convertible to std::string. + * @param key The key to access in the JSON object. + * @return Reference to the JSON value associated with the key. */ - json &operator=(int64_t l) noexcept - { - value = l; - return *this; - } + template , const char *> || std::is_same_v, std::string> || std::is_same_v, std::string_view>, int> = 0> + json &operator[](Key &&key) { return std::get>>(value)[std::string(std::forward(key))]; } /** - * @brief Assigns an unsigned 64-bit integer value to the JSON object. + * @brief Accesses the JSON value associated with the given key. * - * This assignment operator allows you to assign an unsigned 64-bit integer value - * to a JSON object. The value is stored in the `value` member variable of the JSON object. + * This operator allows access to the JSON object element corresponding to the specified key. + * If the key does not exist, an exception is thrown. * - * @param l The unsigned 64-bit integer value to assign. - * @return A reference to the modified JSON object. + * @tparam Key Type of the key, which must be convertible to std::string. + * @param key The key to access in the JSON object. + * @return A constant reference to the JSON value associated with the key. */ - json &operator=(uint64_t l) noexcept - { - value = l; - return *this; - } + template , const char *> || std::is_same_v, std::string> || std::is_same_v, std::string_view>, int> = 0> + const json &operator[](Key &&key) const { return std::get>>(value).at(std::string(std::forward(key))); } /** - * @brief Assigns a double value to the JSON object. + * @brief Accesses the JSON value at the specified index. * - * This assignment operator allows you to assign a double value to a JSON object. + * This operator allows access to the JSON array element at the specified index. + * If the index is out of bounds, an exception is thrown. * - * @param d The double value to assign. - * @return A reference to the modified JSON object. + * @tparam Index Type of the index, which must be an integral type. + * @param idx The index to access in the JSON array. + * @return Reference to the JSON value at the specified index. */ - json &operator=(double d) noexcept - { - value = d; - return *this; - } + template , int> = 0> + json &operator[](Index idx) { return std::get>(value)[static_cast(idx)]; } /** - * @brief Accesses the value associated with the specified key in the JSON object. + * @brief Accesses the JSON value at the specified index. * - * This operator allows accessing the value associated with the specified key in the JSON object. - * The key is provided as a C-style string. + * This operator allows access to the JSON array element at the specified index. + * If the index is out of bounds, an exception is thrown. * - * @param str The key to access the value. - * @return A reference to the value associated with the key. + * @tparam Index Type of the index, which must be an integral type. + * @param idx The index to access in the JSON array. + * @return A constant reference to the JSON value at the specified index. */ - json &operator[](const char *str) { return std::get>>(value)[str]; } - /** - * @brief Accesses the value associated with the specified key in the JSON object. - * - * This operator allows accessing the value associated with the specified key in the JSON object. - * The key is provided as a C-style string. - * - * @param str The key to access the value. - * @return A reference to the value associated with the key. - */ - const json &operator[](const char *str) const { return std::get>>(value).at(str); } + template , int> = 0> + const json &operator[](Index idx) const { return std::get>(value).at(static_cast(idx)); } - /** - * @brief Accesses the value associated with the specified key in the JSON object. - * - * This operator allows you to access the value associated with the specified key in the JSON object. - * If the key does not exist, it will be created and associated with a default-constructed JSON value. - * - * @param key The key to access the value. - * @return A reference to the value associated with the key. - */ - json &operator[](std::string_view key) { return std::get>>(value)[key.data()]; } - /** - * @brief Accesses the value associated with the specified key in the JSON object. - * - * This operator allows you to access the value associated with the specified key in the JSON object. - * If the key does not exist, it will be created and associated with a default-constructed JSON value. - * - * @param key The key to access the value. - * @return A reference to the value associated with the key. - */ - const json &operator[](std::string_view key) const { return std::get>>(value).at(key.data()); } - - /** - * @brief Accesses the element at the specified index in the JSON object. - * - * This operator allows accessing the element at the specified index in the JSON object. - * The index can be either an integer or a size_t value. - * - * @param index The index of the element to access. - * @return A reference to the element at the specified index. - */ - json &operator[](int index) { return operator[](static_cast(index)); } - /** - * @brief Accesses the element at the specified index in the JSON object. - * - * This operator allows accessing the element at the specified index in the JSON object. - * The index can be either an integer or a size_t value. - * - * @param index The index of the element to access. - * @return A reference to the element at the specified index. - */ - const json &operator[](int index) const { return operator[](static_cast(index)); } - - /** - * @brief Accesses the element at the specified index in the JSON object. - * - * This operator allows you to access the element at the specified index in the JSON object. - * It returns a reference to the element, allowing you to modify its value if needed. - * - * @param index The index of the element to access. - * @return A reference to the element at the specified index. - */ - json &operator[](size_t index) { return std::get>(value)[index]; } - /** - * @brief Accesses the element at the specified index in the JSON object. - * - * This operator allows you to access the element at the specified index in the JSON object. - * It returns a reference to the element, allowing you to modify its value if needed. - * - * @param index The index of the element to access. - * @return A reference to the element at the specified index. - */ - const json &operator[](size_t index) const { return std::get>(value).at(index); } + [[nodiscard]] bool operator==(const json &other) const noexcept { return value == other.value; } + [[nodiscard]] bool operator!=(const json &other) const noexcept { return !(*this == other); } /** * @brief Checks if the JSON value is null. @@ -536,184 +318,47 @@ namespace json */ [[nodiscard]] bool contains(std::string_view key) const { return is_object() && std::get>>(value).count(key) > 0; } - /** - * @brief Overloads the equality operator for comparing two json objects. - * - * This function compares the value of two json objects and returns true if they are equal, and false otherwise. - * - * @param other The json object to compare with. - * @return true if the two json objects are equal, false otherwise. - */ - [[nodiscard]] bool operator==(const json &other) const noexcept { return value == other.value; } - /** - * @brief Overloads the equality operator for comparing a json object with a nullptr. - * - * This function compares the value of the json object with a nullptr and returns true if the value is null, and false otherwise. - * - * @param nullptr_t A null pointer constant. - * @return true if the json object is null, false otherwise. - */ - [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } - /** - * @brief Overloads the equality operator for comparing a json object with a boolean value. - * - * This function compares the value of the json object with a boolean value and returns true if they are equal, and false otherwise. - * - * @param b The boolean value to compare with. - * @return true if the json object is equal to the boolean value, false otherwise. - */ - [[nodiscard]] bool operator==(bool b) const noexcept { return value.index() == 1 && std::get(value) == b; } - /** - * @brief Overloads the equality operator for comparing a json object with an integer value. - * - * This function compares the value of the json object with an integer value and returns true if they are equal, and false otherwise. - * - * @param l The integer value to compare with. - * @return true if the json object is equal to the integer value, false otherwise. - */ - [[nodiscard]] bool operator==(int l) const noexcept + template , int> = 0> + [[nodiscard]] bool operator==(T num) const noexcept { switch (value.index()) { case 1: - return std::get(value) == static_cast(l); + return std::get(value) == static_cast(num); case 2: - return std::get(value) == static_cast(l); + return std::get(value) == static_cast(num); case 3: - return std::get(value) == static_cast(l); + return std::get(value) == static_cast(num); case 4: - return std::get(value) == static_cast(l); + return std::get(value) == static_cast(num); default: return false; } } - /** - * @brief Overloads the equality operator for comparing a json object with an int64_t value. - * - * This function compares the value of the json object with an int64_t value and returns true if they are equal, and false otherwise. - * - * @param l The int64_t value to compare with. - * @return true if the json object is equal to the int64_t value, false otherwise. - */ - [[nodiscard]] bool operator==(int64_t l) const noexcept { return value.index() == 2 && std::get(value) == l; } - /** - * @brief Overloads the equality operator for comparing a json object with an unsigned 64-bit integer value. - * - * This function compares the value of the json object with an unsigned 64-bit integer value and returns true if they are equal, and false otherwise. - * - * @param l The unsigned 64-bit integer value to compare with. - * @return true if the json object is equal to the unsigned 64-bit integer value, false otherwise. - */ - [[nodiscard]] bool operator==(uint64_t l) const noexcept { return value.index() == 3 && std::get(value) == l; } - /** - * @brief Overloads the equality operator for comparing a json object with a double value. - * - * This function compares the value of the json object with a double value and returns true if they are equal, and false otherwise. - * - * @param d The double value to compare with. - * @return true if the json object is equal to the double value, false otherwise. - */ - [[nodiscard]] bool operator==(double d) const noexcept { return value.index() == 4 && std::get(value) == d; } - /** - * @brief Overloads the equality operator for comparing a json object with a string. - * - * This function compares the value of the json object with a string and returns true if they are equal, and false otherwise. - * - * @param str The string to compare with. - * @return true if the json object is equal to the string, false otherwise. - */ - [[nodiscard]] bool operator==(std::string_view str) const noexcept { return value.index() == 5 && std::get(value) == str; } - /** - * @brief Overloads the equality operator for comparing a json object with a C-style string. - * - * This function compares the value of the json object with a C-style string and returns true if they are equal, and false otherwise. - * - * @param str The C-style string to compare with. - * @return true if the json object is equal to the C-style string, false otherwise. - */ - [[nodiscard]] bool operator==(const char *str) const noexcept { return value.index() == 5 && std::get(value) == str; } - /** - * @brief Overloads the inequality operator for comparing two json objects. - * - * This function compares the value of two json objects and returns true if they are not equal, and false otherwise. - * - * @param other The json object to compare with. - * @return true if the two json objects are not equal, false otherwise. - */ - [[nodiscard]] bool operator!=(const json &other) const noexcept { return value != other.value; } - /** - * @brief Overloads the inequality operator for comparing a json object with a nullptr. - * - * This function compares the value of the json object with a nullptr and returns true if the value is not null, and false otherwise. - * - * @param nullptr_t A null pointer constant. - * @return true if the json object is not null, false otherwise. - */ - [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } - /** - * @brief Overloads the inequality operator for comparing a json object with a boolean value. - * - * This function compares the value of the json object with a boolean value and returns true if they are not equal, and false otherwise. - * - * @param b The boolean value to compare with. - * @return true if the json object is not equal to the boolean value, false otherwise. - */ - [[nodiscard]] bool operator!=(bool b) const noexcept { return value.index() != 1 || std::get(value) != b; } - /** - * @brief Overloads the inequality operator for comparing a json object with an integer value. - * - * This function compares the value of the json object with an integer value and returns true if they are not equal, and false otherwise. - * - * @param l The integer value to compare with. - * @return true if the json object is not equal to the integer value, false otherwise. - */ - [[nodiscard]] bool operator!=(int l) const noexcept { return !(*this == l); } - /** - * @brief Overloads the inequality operator for comparing a json object with an int64_t value. - * - * This function compares the value of the json object with an int64_t value and returns true if they are not equal, and false otherwise. - * - * @param l The int64_t value to compare with. - * @return true if the json object is not equal to the int64_t value, false otherwise. - */ - [[nodiscard]] bool operator!=(int64_t l) const noexcept { return value.index() != 2 || std::get(value) != l; } - /** - * @brief Overloads the inequality operator for comparing a json object with an unsigned 64-bit integer value. - * - * This function compares the value of the json object with an unsigned 64-bit integer value and returns true if they are not equal, and false otherwise. - * - * @param l The unsigned 64-bit integer value to compare with. - * @return true if the json object is not equal to the unsigned 64-bit integer value, false otherwise. - */ - [[nodiscard]] bool operator!=(uint64_t l) const noexcept { return value.index() != 3 || std::get(value) != l; } - /** - * @brief Overloads the inequality operator for comparing a json object with a double value. - * - * This function compares the value of the json object with a double value and returns true if they are not equal, and false otherwise. - * - * @param d The double value to compare with. - * @return true if the json object is not equal to the double value, false otherwise. - */ - [[nodiscard]] bool operator!=(double d) const noexcept { return value.index() != 4 || std::get(value) != d; } - /** - * @brief Overloads the inequality operator for comparing a json object with a string. - * - * This function compares the value of the json object with a string and returns true if they are not equal, and false otherwise. - * - * @param str The string to compare with. - * @return true if the json object is not equal to the string, false otherwise. - */ - [[nodiscard]] bool operator!=(std::string_view str) const noexcept { return value.index() != 5 || std::get(value) != str; } - /** - * @brief Overloads the inequality operator for comparing a json object with a C-style string. - * - * This function compares the value of the json object with a C-style string and returns true if they are not equal, and false otherwise. - * - * @param str The C-style string to compare with. - * @return true if the json object is not equal to the C-style string, false otherwise. - */ - [[nodiscard]] bool operator!=(const char *str) const noexcept { return value.index() != 5 || std::get(value) != str; } + template , std::string> || std::is_same_v, std::string_view> || std::is_same_v, const char *>, int> = 0> + [[nodiscard]] bool operator==(T &&str) const noexcept { return value.index() == 5 && std::get(value) == std::string(str); } + + template && !std::is_same_v, bool>, int> = 0> + [[nodiscard]] bool operator!=(T num) const noexcept + { + switch (value.index()) + { + case 1: + return std::get(value) != static_cast(num); + case 2: + return std::get(value) != static_cast(num); + case 3: + return std::get(value) != static_cast(num); + case 4: + return std::get(value) != static_cast(num); + default: + return true; + } + } + + template , std::string> || std::is_same_v, std::string_view> || std::is_same_v, const char *>, int> = 0> + [[nodiscard]] bool operator!=(T &&str) const noexcept { return value.index() != 5 || std::get(value) != std::string(str); } /** * @brief Conversion operator to bool. diff --git a/tests/test_json.cpp b/tests/test_json.cpp index f3428b3..77a7083 100644 --- a/tests/test_json.cpp +++ b/tests/test_json.cpp @@ -274,11 +274,11 @@ void test_json_comparison() assert(j0 == j1); - j0["f"] = json::json_type::array; + j0["f"] = json::json(json::json_type::array); j0["f"].push_back(1); j0["f"].push_back(2); - j1["f"] = json::json_type::array; + j1["f"] = json::json(json::json_type::array); j1["f"].push_back(1); j1["f"].push_back(2); @@ -296,7 +296,7 @@ void test_move_semantics() j0["c"] = "3"; j0["d"] = true; j0["e"] = nullptr; - j0["f"] = json::json_type::array; + j0["f"] = json::json(json::json_type::array); j0["f"].push_back(1); j0["f"].push_back(2); j0["f"].push_back(3); @@ -339,7 +339,7 @@ void test_iterate() j0["c"] = "3"; j0["d"] = true; j0["e"] = nullptr; - j0["f"] = json::json_type::array; + j0["f"] = json::json(json::json_type::array); j0["f"].push_back(1); j0["f"].push_back(2); j0["f"].push_back(3); From e7cf44d9de6d19e3e71d3c1ce873295415f2e78a Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 17 Jul 2025 22:59:29 +0200 Subject: [PATCH 126/137] Add move constructor to json class for efficient resource transfer --- include/json.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/json.hpp b/include/json.hpp index b70698b..94f25fd 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -75,6 +75,14 @@ namespace json * @param other The json object to be copied. */ json(const json &other) : value(other.value) {} + /** + * @brief Move constructor for the json class. + * + * This constructor transfers ownership of the value from another json object to the new json object. + * + * @param other The json object to be moved. + */ + json(json &&other) noexcept : value(std::move(other.value)) { other.value = nullptr; } /** * @brief Constructs a JSON object from a value of type T. From a44ea4675750f5f5f0b9b12168413b66fff1c338 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 19 Jul 2025 09:20:44 +0200 Subject: [PATCH 127/137] Add constexpr specifier to JSON type check functions for improved performance --- include/json.hpp | 63 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 94f25fd..78bf5c1 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -196,31 +196,31 @@ namespace json * * @return true if the JSON value is null, false otherwise. */ - [[nodiscard]] bool is_null() const noexcept { return value.index() == 0; } + [[nodiscard]] constexpr bool is_null() const noexcept { return value.index() == 0; } /** * @brief Checks if the JSON value is a boolean. * * @return true if the JSON value is a boolean, false otherwise. */ - [[nodiscard]] bool is_boolean() const noexcept { return value.index() == 1; } + [[nodiscard]] constexpr bool is_boolean() const noexcept { return value.index() == 1; } /** * @brief Checks if the JSON value is an integer. * * @return true if the JSON value is an integer, false otherwise. */ - [[nodiscard]] bool is_integer() const noexcept { return value.index() == 2; } + [[nodiscard]] constexpr bool is_integer() const noexcept { return value.index() == 2; } /** * @brief Checks if the JSON value is an unsigned integer. * * @return true if the JSON value is an unsigned integer, false otherwise. */ - [[nodiscard]] bool is_unsigned() const noexcept { return value.index() == 3; } + [[nodiscard]] constexpr bool is_unsigned() const noexcept { return value.index() == 3; } /** * @brief Checks if the JSON value is a floating-point number. * * @return true if the JSON value is a floating-point number, false otherwise. */ - [[nodiscard]] bool is_float() const noexcept { return value.index() == 4; } + [[nodiscard]] constexpr bool is_float() const noexcept { return value.index() == 4; } /** * @brief Checks if the JSON value is a number. * @@ -228,25 +228,25 @@ namespace json * * @return true if the JSON value is a number, false otherwise. */ - [[nodiscard]] bool is_number() const noexcept { return value.index() == 2 || value.index() == 3 || value.index() == 4; } + [[nodiscard]] constexpr bool is_number() const noexcept { return value.index() == 2 || value.index() == 3 || value.index() == 4; } /** * @brief Checks if the JSON value is a string. * * @return true if the JSON value is a string, false otherwise. */ - [[nodiscard]] bool is_string() const noexcept { return value.index() == 5; } + [[nodiscard]] constexpr bool is_string() const noexcept { return value.index() == 5; } /** * @brief Checks if the JSON value is an object. * * @return true if the JSON value is an object, false otherwise. */ - [[nodiscard]] bool is_object() const noexcept { return value.index() == 6; } + [[nodiscard]] constexpr bool is_object() const noexcept { return value.index() == 6; } /** * @brief Checks if the JSON value is an array. * * @return true if the JSON value is an array, false otherwise. */ - [[nodiscard]] bool is_array() const noexcept { return value.index() == 7; } + [[nodiscard]] constexpr bool is_array() const noexcept { return value.index() == 7; } /** * @brief Checks if the JSON value is a primitive type. @@ -256,7 +256,7 @@ namespace json * * @return true if the JSON value is a primitive type, false otherwise. */ - [[nodiscard]] bool is_primitive() const noexcept { return is_null() || is_boolean() || is_integer() || is_unsigned() || is_float() || is_string(); } + [[nodiscard]] constexpr bool is_primitive() const noexcept { return is_null() || is_boolean() || is_integer() || is_unsigned() || is_float() || is_string(); } /** * @brief Checks if the JSON value is a structured type. * @@ -264,7 +264,7 @@ namespace json * * @return true if the JSON value is a structured type (object or array), false otherwise. */ - [[nodiscard]] bool is_structured() const noexcept { return is_object() || is_array(); } + [[nodiscard]] constexpr bool is_structured() const noexcept { return is_object() || is_array(); } /** * @brief Returns the type of the JSON value. @@ -274,7 +274,7 @@ namespace json * * @return The type of the JSON value. */ - [[nodiscard]] json_type get_type() const noexcept + [[nodiscard]] constexpr json_type get_type() const noexcept { switch (value.index()) { @@ -326,6 +326,8 @@ namespace json */ [[nodiscard]] bool contains(std::string_view key) const { return is_object() && std::get>>(value).count(key) > 0; } + [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { return value.index() == 0; } + template , int> = 0> [[nodiscard]] bool operator==(T num) const noexcept { @@ -347,7 +349,9 @@ namespace json template , std::string> || std::is_same_v, std::string_view> || std::is_same_v, const char *>, int> = 0> [[nodiscard]] bool operator==(T &&str) const noexcept { return value.index() == 5 && std::get(value) == std::string(str); } - template && !std::is_same_v, bool>, int> = 0> + [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return value.index() != 0; } + + template , int> = 0> [[nodiscard]] bool operator!=(T num) const noexcept { switch (value.index()) @@ -368,6 +372,37 @@ namespace json template , std::string> || std::is_same_v, std::string_view> || std::is_same_v, const char *>, int> = 0> [[nodiscard]] bool operator!=(T &&str) const noexcept { return value.index() != 5 || std::get(value) != std::string(str); } + /** + * @brief Get the JSON value as the specified type T. + * + * This templated function provides explicit conversion of the JSON value to type T. + * Use this when you want to be explicit about the conversion or when implicit + * conversion operators might be ambiguous. + * + * @tparam T The type to convert the JSON value to. + * @return The JSON value converted to type T. + */ + template + [[nodiscard]] T get() const noexcept + { + if constexpr (std::is_same_v) + return static_cast(*this); + else if constexpr (std::is_integral_v && std::is_signed_v) + return static_cast(*this); + else if constexpr (std::is_integral_v && std::is_unsigned_v) + return static_cast(*this); + else if constexpr (std::is_floating_point_v) + return static_cast(*this); + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + return static_cast(*this); + else if constexpr (std::is_same_v>>) + return static_cast>>(*this); + else if constexpr (std::is_same_v>) + return static_cast>(*this); + else + static_assert(std::is_arithmetic_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v>> || std::is_same_v>, "Unsupported type for json::get()"); + } + /** * @brief Conversion operator to bool. * @@ -387,7 +422,7 @@ namespace json case 3: return std::get(value) != 0; case 4: - return !std::get(value); + return std::get(value) != 0.0; case 5: return !std::get(value).empty(); case 6: From f8faf04e2e4822cbd32438cc7cce94627d8bba4e Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 4 Sep 2025 13:55:46 +0200 Subject: [PATCH 128/137] Remove cmake installation from CI workflows to streamline dependency management --- .github/workflows/cmake.yml | 25 ++----------------------- extern/utils | 2 +- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 2b29021..d5ffd5e 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -14,7 +14,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y cmake lcov + sudo apt-get install -y lcov - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBSAN=ON -DENABLE_COV=ON @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y cmake valgrind + sudo apt-get install -y valgrind - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Debug @@ -68,10 +68,6 @@ jobs: with: submodules: recursive - - name: Install dependencies - run: | - choco install cmake - - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Debug @@ -90,10 +86,6 @@ jobs: with: submodules: recursive - - name: Install dependencies - run: | - brew install --formula cmake - - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -DENABLE_UBSAN=ON @@ -112,11 +104,6 @@ jobs: with: submodules: recursive - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake - - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Release @@ -135,10 +122,6 @@ jobs: with: submodules: recursive - - name: Install dependencies - run: | - choco install cmake - - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Release @@ -157,10 +140,6 @@ jobs: with: submodules: recursive - - name: Install dependencies - run: | - brew install --formula cmake - - name: Configure CMake run: cmake -B build -DCMAKE_BUILD_TYPE=Release diff --git a/extern/utils b/extern/utils index a13fb2a..4462481 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit a13fb2ad563ce69492fa42d26f2c4369556fe9a8 +Subproject commit 44624810ff83bb47e063e91998d8654a6e39fa56 From 89b68054168b03ab812e10537048e90a31762a3e Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 4 Sep 2025 13:58:13 +0200 Subject: [PATCH 129/137] Update test step name in CI workflow for clarity --- .github/workflows/cmake.yml | 2 +- extern/utils | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d5ffd5e..6099556 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -74,7 +74,7 @@ jobs: - name: Build run: cmake --build build --config Debug - - name: Test with address sanitizer + - name: Test working-directory: build run: ctest -C Debug --rerun-failed --output-on-failure -T Test diff --git a/extern/utils b/extern/utils index 4462481..ba0c4e7 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 44624810ff83bb47e063e91998d8654a6e39fa56 +Subproject commit ba0c4e7e421e92ffdf546026c3828b43fdd37dbb From 6b2f6cbd79d8b4d6ca7fc6bf7020c356d8d61243 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Wed, 17 Sep 2025 12:03:11 +0200 Subject: [PATCH 130/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index ba0c4e7..6d2b323 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit ba0c4e7e421e92ffdf546026c3828b43fdd37dbb +Subproject commit 6d2b32305715c785600fc6b5a8d576c340326583 From 4a38e92bf31d57cc4b6d1a202c3d98e79f2982bb Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 2 Oct 2025 17:08:02 +0200 Subject: [PATCH 131/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 6d2b323..55f7155 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 6d2b32305715c785600fc6b5a8d576c340326583 +Subproject commit 55f7155eaf375470e470d014b422378cd5c48912 From 174d0ba13ee35696114a81aeb086a7685e77a976 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Thu, 23 Oct 2025 17:55:36 +0200 Subject: [PATCH 132/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 55f7155..f44b6f6 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 55f7155eaf375470e470d014b422378cd5c48912 +Subproject commit f44b6f637d5772ccadf9caaa762cc11f552844da From 91a22286b93d5757803f44de54fa401dc7f03b90 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Mon, 27 Oct 2025 17:21:25 +0100 Subject: [PATCH 133/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index f44b6f6..1bd406a 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit f44b6f637d5772ccadf9caaa762cc11f552844da +Subproject commit 1bd406a6a0d6a7175b991fd3256aaaa457cf04c8 From dc550ec0c21727a14907d4562a955bbb800ed1b7 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 31 Oct 2025 14:04:37 +0100 Subject: [PATCH 134/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 1bd406a..4656806 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 1bd406a6a0d6a7175b991fd3256aaaa457cf04c8 +Subproject commit 4656806579e0a3c59dbb4aff52e67350b1b25631 From 0216df705d6ded4f00b468170ca5b2b3e591bcaa Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 31 Oct 2025 15:30:55 +0100 Subject: [PATCH 135/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index 4656806..e4aeb7c 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit 4656806579e0a3c59dbb4aff52e67350b1b25631 +Subproject commit e4aeb7c538cd6fa5d5d98a27badcf4b1e770d142 From 27a9ee70a99b1c56dd289729cfa2e65a69638a18 Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Fri, 7 Nov 2025 16:27:19 +0100 Subject: [PATCH 136/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index e4aeb7c..d3cc24f 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit e4aeb7c538cd6fa5d5d98a27badcf4b1e770d142 +Subproject commit d3cc24f408f66672787af26e0488e90cdf67c42d From 6e3bc250f1f5515ce8095d79ab99303fac44e87f Mon Sep 17 00:00:00 2001 From: Riccardo De Benedictis Date: Sat, 15 Nov 2025 12:15:02 +0100 Subject: [PATCH 137/137] Update subproject commit reference in extern/utils --- extern/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/utils b/extern/utils index d3cc24f..7856090 160000 --- a/extern/utils +++ b/extern/utils @@ -1 +1 @@ -Subproject commit d3cc24f408f66672787af26e0488e90cdf67c42d +Subproject commit 7856090935c4a168c2921fa29d005edb3b286eae