Imported Upstream version 1.9.4 upstream/1.9.4
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 17 Dec 2021 01:44:59 +0000 (10:44 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 17 Dec 2021 01:44:59 +0000 (10:44 +0900)
46 files changed:
.clang-tidy
.gitignore
.travis_scripts/cmake_builder.sh
AUTHORS
CMakeLists.txt
CONTRIBUTING.md
README.md
amalgamate.py
cmake/JoinPaths.cmake [new file with mode: 0644]
example/CMakeLists.txt
example/readFromString/readFromString.cpp
include/json/allocator.h
include/json/assertions.h
include/json/autolink.h [deleted file]
include/json/config.h
include/json/forwards.h
include/json/json.h
include/json/json_features.h
include/json/reader.h
include/json/value.h
include/json/version.h
include/json/writer.h
meson.build
meson_options.txt [new file with mode: 0644]
pkg-config/jsoncpp.pc.in
src/jsontestrunner/CMakeLists.txt
src/jsontestrunner/main.cpp
src/lib_json/CMakeLists.txt
src/lib_json/json_reader.cpp
src/lib_json/json_tool.h
src/lib_json/json_value.cpp
src/lib_json/json_valueiterator.inl
src/lib_json/json_writer.cpp
src/test_lib_json/CMakeLists.txt
src/test_lib_json/fuzz.cpp
src/test_lib_json/jsontest.cpp
src/test_lib_json/jsontest.h
src/test_lib_json/main.cpp
test/data/fail_invalid_quote.json [new file with mode: 0644]
test/data/fail_test_array_02.json [new file with mode: 0644]
test/data/fail_test_object_01.json [new file with mode: 0644]
test/data/test_array_08.expected [new file with mode: 0644]
test/data/test_array_08.json [new file with mode: 0644]
test/data/test_object_05.expected [new file with mode: 0644]
test/data/test_object_05.json [new file with mode: 0644]
test/runjsontests.py

index 6b5e801..be3d06a 100644 (file)
@@ -1,5 +1,5 @@
 ---
-Checks:          'google-readability-casting,modernize-use-default-member-init,modernize-use-using,readability-redundant-member-init'
+Checks:          'google-readability-casting,modernize-deprecated-headers,modernize-loop-convert,modernize-use-auto,modernize-use-default-member-init,modernize-use-using,readability-else-after-return,readability-redundant-member-init,readability-redundant-string-cstr'
 WarningsAsErrors: ''
 HeaderFilterRegex: ''
 AnalyzeTemporaryDtors: false
index 91121c2..68f40b0 100644 (file)
@@ -28,7 +28,6 @@
 
 # CMake-generated files:
 CMakeFiles/
-*.cmake
 /pkg-config/jsoncpp.pc
 jsoncpp_lib_static.dir/
 
index ccb3331..f3d4e46 100755 (executable)
@@ -66,7 +66,7 @@ cmake --version
 echo ${CXX}
 ${CXX} --version
 _COMPILER_NAME=`basename ${CXX}`
-if [ "${BUILD_TYPE}" == "shared" ]; then
+if [ "${LIB_TYPE}" = "shared" ]; then
   _CMAKE_BUILD_SHARED_LIBS=ON
 else
   _CMAKE_BUILD_SHARED_LIBS=OFF
diff --git a/AUTHORS b/AUTHORS
index 3723d54..e1fa0fc 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,6 +21,7 @@ Braden McDorman <bmcdorman@gmail.com>
 Brandon Myers <bmyers1788@gmail.com>
 Brendan Drew <brendan.drew@daqri.com>
 chason <cxchao802@gmail.com>
+chenguoping <chenguopingdota@163.com>
 Chris Gilling <cgilling@iparadigms.com>
 Christopher Dawes <christopher.dawes.1981@googlemail.com>
 Christopher Dunn <cdunn2001@gmail.com>
@@ -97,6 +98,7 @@ selaselah <selah@outlook.com>
 Sergiy80 <sil2004@gmail.com>
 sergzub <sergzub@gmail.com>
 Stefan Schweter <stefan@schweter.it>
+Stefano Fiorentino <stefano.fiore84@gmail.com>
 Steffen Kieß <Steffen.Kiess@ipvs.uni-stuttgart.de>
 Steven Hahn <hahnse@ornl.gov>
 Stuart Eichert <stuart@fivemicro.com>
index 8a7d3ef..51b74fc 100644 (file)
@@ -37,44 +37,43 @@ foreach(pold "") # Currently Empty
     endif()
 endforeach()
 
-# ==== Define language standard configurations requiring at least c++11 standard
-if(CMAKE_CXX_STANDARD EQUAL "98" )
-    message(FATAL_ERROR "CMAKE_CXX_STANDARD:STRING=98 is not supported.")
-endif()
-
-#####
-##  Set the default target properties
-if(NOT CMAKE_CXX_STANDARD)
-    set(CMAKE_CXX_STANDARD 11) # Supported values are ``11``, ``14``, and ``17``.
-endif()
-if(NOT CMAKE_CXX_STANDARD_REQUIRED)
-    set(CMAKE_CXX_STANDARD_REQUIRED ON)
-endif()
-if(NOT CMAKE_CXX_EXTENSIONS)
-    set(CMAKE_CXX_EXTENSIONS OFF)
+# Build the library with C++11 standard support, independent from other including
+# software which may use a different CXX_STANDARD or CMAKE_CXX_STANDARD.
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Ensure that CMAKE_BUILD_TYPE has a value specified for single configuration generators.
+if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
+    set(CMAKE_BUILD_TYPE Release CACHE STRING
+        "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage.")
 endif()
 
-# ====
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
-# Ensures that CMAKE_BUILD_TYPE has a default value
-if(NOT DEFINED CMAKE_BUILD_TYPE)
-    set(CMAKE_BUILD_TYPE Release CACHE STRING
-        "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage.")
+# ---------------------------------------------------------------------------
+# use ccache if found, has to be done before project()
+# ---------------------------------------------------------------------------
+find_program(CCACHE_EXECUTABLE "ccache" HINTS /usr/local/bin /opt/local/bin)
+if(CCACHE_EXECUTABLE)
+    message(STATUS "use ccache")
+    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache" FORCE)
+    set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache" FORCE)
 endif()
 
-project(JSONCPP
+project(jsoncpp
         # Note: version must be updated in three places when doing a release. This
         # annoying process ensures that amalgamate, CMake, and meson all report the
         # correct version.
-        # 1. /meson.build
-        # 2. /include/json/version.h
-        # 3. /CMakeLists.txt
-        # IMPORTANT: also update the SOVERSION!!
-        VERSION 1.9.2 # <major>[.<minor>[.<patch>[.<tweak>]]]
+        # 1. ./meson.build
+        # 2. ./include/json/version.h
+        # 3. ./CMakeLists.txt
+        # IMPORTANT: also update the PROJECT_SOVERSION!!
+        VERSION 1.9.4 # <major>[.<minor>[.<patch>[.<tweak>]]]
         LANGUAGES CXX)
 
-message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
-set( JSONCPP_SOVERSION 22 )
+message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
+set(PROJECT_SOVERSION 24)
 
 option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
 option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
@@ -82,120 +81,80 @@ option(JSONCPP_WITH_WARNING_AS_ERROR "Force compilation to fail if a warning occ
 option(JSONCPP_WITH_STRICT_ISO "Issue all the warnings demanded by strict ISO C and ISO C++" ON)
 option(JSONCPP_WITH_PKGCONFIG_SUPPORT "Generate and install .pc files" ON)
 option(JSONCPP_WITH_CMAKE_PACKAGE "Generate and install cmake package files" ON)
-option(BUILD_SHARED_LIBS "Build jsoncpp_lib as a shared library." OFF)
-
-# Enable runtime search path support for dynamic libraries on OSX
-if(APPLE)
-    set(CMAKE_MACOSX_RPATH 1)
-endif()
+option(JSONCPP_WITH_EXAMPLE "Compile JsonCpp example" OFF)
+option(BUILD_SHARED_LIBS "Build jsoncpp_lib as a shared library." ON)
+option(BUILD_STATIC_LIBS "Build jsoncpp_lib as a static library." ON)
+option(BUILD_OBJECT_LIBS "Build jsoncpp_lib as a object library." ON)
 
 # Adhere to GNU filesystem layout conventions
 include(GNUInstallDirs)
 
-set(DEBUG_LIBNAME_SUFFIX "" CACHE STRING "Optional suffix to append to the library name for a debug build")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Archive output dir.")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Library output dir.")
+set(CMAKE_PDB_OUTPUT_DIRECTORY     "${CMAKE_BINARY_DIR}/bin" CACHE PATH "PDB (MSVC debug symbol)output dir.")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Executable/dll output dir.")
 
-set(JSONCPP_USE_SECURE_MEMORY "0" CACHE STRING "-D...=1 to use memory-wiping allocator for STL" )
+set(JSONCPP_USE_SECURE_MEMORY "0" CACHE STRING "-D...=1 to use memory-wiping allocator for STL")
 
-configure_file( "${PROJECT_SOURCE_DIR}/version.in"
-                "${PROJECT_BINARY_DIR}/version"
-                NEWLINE_STYLE UNIX )
+configure_file("${PROJECT_SOURCE_DIR}/version.in"
+    "${PROJECT_BINARY_DIR}/version"
+    NEWLINE_STYLE UNIX)
 
-macro(UseCompilationWarningAsError)
+macro(use_compilation_warning_as_error)
     if(MSVC)
         # Only enabled in debug because some old versions of VS STL generate
         # warnings when compiled in release configuration.
-        if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-          add_compile_options($<$<CONFIG:Debug>:/WX>)
-        else()
-          set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /WX ")
-        endif()
+        add_compile_options($<$<CONFIG:Debug>:/WX>)
     elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-        if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-          add_compile_options(-Werror)
-        else()
-          set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
-        endif()
+        add_compile_options(-Werror)
         if(JSONCPP_WITH_STRICT_ISO)
-          if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
             add_compile_options(-pedantic-errors)
-          else()
-            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors")
-          endif()
         endif()
     endif()
 endmacro()
 
 # Include our configuration header
-include_directories( ${jsoncpp_SOURCE_DIR}/include )
+include_directories(${jsoncpp_SOURCE_DIR}/include)
 
 if(MSVC)
     # Only enabled in debug because some old versions of VS STL generate
     # unreachable code warning when compiled in release configuration.
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_options($<$<CONFIG:Debug>:/W4>)
-    else()
-      set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 ")
-    endif()
+    add_compile_options($<$<CONFIG:Debug>:/W4>)
 endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
     # using regular Clang or AppleClang
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_options(-Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare)
-    else()
-      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare")
-    endif()
+    add_compile_options(-Wall -Wconversion -Wshadow -Werror=conversion -Werror=sign-compare)
 elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     # using GCC
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_options(-Wall -Wconversion -Wshadow -Wextra)
-    else()
-      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Wextra")
-    endif()
+    add_compile_options(-Wall -Wconversion -Wshadow -Wextra)
     # not yet ready for -Wsign-conversion
 
     if(JSONCPP_WITH_STRICT_ISO)
-      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-        add_compile_options(-pedantic)
-      else()
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
-      endif()
+        add_compile_options(-Wpedantic)
     endif()
     if(JSONCPP_WITH_WARNING_AS_ERROR)
-      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
         add_compile_options(-Werror=conversion)
-      else()
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=conversion")
-      endif()
     endif()
 elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
     # using Intel compiler
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion)
-    else()
-      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wshadow -Wextra -Werror=conversion")
-    endif()
+    add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion)
 
     if(JSONCPP_WITH_STRICT_ISO AND NOT JSONCPP_WITH_WARNING_AS_ERROR)
-      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-        add_compile_options(-pedantic)
-      else()
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
-      endif()
+        add_compile_options(-Wpedantic)
     endif()
 endif()
 
-find_program(CCACHE_FOUND ccache)
-if(CCACHE_FOUND)
-    set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
-    set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
-endif(CCACHE_FOUND)
-
 if(JSONCPP_WITH_WARNING_AS_ERROR)
-    UseCompilationWarningAsError()
+    use_compilation_warning_as_error()
 endif()
 
 if(JSONCPP_WITH_PKGCONFIG_SUPPORT)
+    include(JoinPaths)
+
+    join_paths(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
+    join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
+
     configure_file(
         "pkg-config/jsoncpp.pc.in"
         "pkg-config/jsoncpp.pc"
@@ -205,27 +164,29 @@ if(JSONCPP_WITH_PKGCONFIG_SUPPORT)
 endif()
 
 if(JSONCPP_WITH_CMAKE_PACKAGE)
-        include (CMakePackageConfigHelpers)
-        install(EXPORT jsoncpp
-                DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp
-                FILE        jsoncppConfig.cmake)
-        write_basic_package_version_file ("${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake"
-                VERSION ${PROJECT_VERSION}
-                COMPATIBILITY SameMajorVersion)
-        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake
-                DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp)
+    include(CMakePackageConfigHelpers)
+    install(EXPORT jsoncpp
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp
+        FILE        jsoncppConfig.cmake)
+    write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake"
+        VERSION ${PROJECT_VERSION}
+        COMPATIBILITY SameMajorVersion)
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp)
 endif()
 
 if(JSONCPP_WITH_TESTS)
-  enable_testing()
-  include(CTest)
+    enable_testing()
+    include(CTest)
 endif()
 
 # Build the different applications
-add_subdirectory( src )
+add_subdirectory(src)
 
 #install the includes
-add_subdirectory( include )
+add_subdirectory(include)
 
 #install the example
-add_subdirectory( example )
+if(JSONCPP_WITH_EXAMPLE)
+    add_subdirectory(example)
+endif()
index 08428b6..9c9fc6a 100644 (file)
@@ -27,8 +27,13 @@ Then,
     #LIB_TYPE=static
     meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . build-${LIB_TYPE}
     ninja -v -C build-${LIB_TYPE}
-    cd build-${LIB_TYPE}
-    meson test --no-rebuild --print-errorlogs
+
+    ninja -C build-static/ test
+
+    # Or
+    #cd build-${LIB_TYPE}
+    #meson test --no-rebuild --print-errorlogs
+
     sudo ninja install
 
 ## Building and testing with other build systems
index b6fc769..5bff8dc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -30,8 +30,14 @@ format to store user input files.
 
 * `1.y.z` is built with C++11.
 * `0.y.z` can be used with older compilers.
+* `00.11.z` can be used both in old and new compilers.
 * Major versions maintain binary-compatibility.
 
+### Special note
+The branch `00.11.z`is a new branch, its major version number `00` is to show that it is
+different from `0.y.z` and `1.y.z`, the main purpose of this branch is to make a balance
+between the other two branches. Thus, users can use some new features in this new branch
+that introduced in 1.y.z, but can hardly applied into 0.y.z.
 
 ## Using JsonCpp in your project
 
@@ -42,7 +48,7 @@ You can download and install JsonCpp using the [vcpkg](https://github.com/Micros
     cd vcpkg
     ./bootstrap-vcpkg.sh
     ./vcpkg integrate install
-    vcpkg install jsoncpp
+    ./vcpkg install jsoncpp
 
 The JsonCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
 
index fedee1e..4a328ab 100755 (executable)
@@ -99,6 +99,8 @@ def amalgamate_source(source_top_dir=None,
     header.add_text("/// If defined, indicates that the source file is amalgamated")
     header.add_text("/// to prevent private header inclusion.")
     header.add_text("#define JSON_IS_AMALGAMATION")
+    header.add_file(os.path.join(INCLUDE_PATH, "version.h"))
+    header.add_file(os.path.join(INCLUDE_PATH, "allocator.h"))
     header.add_file(os.path.join(INCLUDE_PATH, "config.h"))
     header.add_file(os.path.join(INCLUDE_PATH, "forwards.h"))
     header.add_text("#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED")
diff --git a/cmake/JoinPaths.cmake b/cmake/JoinPaths.cmake
new file mode 100644 (file)
index 0000000..2b376b7
--- /dev/null
@@ -0,0 +1,23 @@
+# This module provides a function for joining paths
+# known from most languages
+#
+# SPDX-License-Identifier: (MIT OR CC0-1.0)
+# Copyright 2020 Jan Tojnar
+# https://github.com/jtojnar/cmake-snips
+#
+# Modelled after Python’s os.path.join
+# https://docs.python.org/3.7/library/os.path.html#os.path.join
+# Windows not supported
+function(join_paths joined_path first_path_segment)
+    set(temp_path "${first_path_segment}")
+    foreach(current_segment IN LISTS ARGN)
+        if(NOT ("${current_segment}" STREQUAL ""))
+            if(IS_ABSOLUTE "${current_segment}")
+                set(temp_path "${current_segment}")
+            else()
+                set(temp_path "${temp_path}/${current_segment}")
+            endif()
+        endif()
+    endforeach()
+    set(${joined_path} "${temp_path}" PARENT_SCOPE)
+endfunction()
index c8eba53..230d1bd 100644 (file)
@@ -1,29 +1,27 @@
 #vim: et ts =4 sts = 4 sw = 4 tw = 0
-cmake_minimum_required(VERSION 3.1)
-
 set(EXAMPLES
-       readFromString
-       readFromStream
-       stringWrite
-       streamWrite
-   )
+    readFromString
+    readFromStream
+    stringWrite
+    streamWrite
+)
 add_definitions(-D_GLIBCXX_USE_CXX11_ABI)
-set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS})
 
-if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra ")
-else() 
-       add_definitions(
-       -D_SCL_SECURE_NO_WARNINGS
-       -D_CRT_SECURE_NO_WARNINGS
-       -D_WIN32_WINNT=0x601
-       -D_WINSOCK_DEPRECATED_NO_WARNINGS)
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+    add_compile_options(-Wall -Wextra)
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+    add_definitions(
+        -D_SCL_SECURE_NO_WARNINGS
+        -D_CRT_SECURE_NO_WARNINGS
+        -D_WIN32_WINNT=0x601
+        -D_WINSOCK_DEPRECATED_NO_WARNINGS
+    )
 endif()
 
-foreach (example ${EXAMPLES})
-       add_executable(${example} ${example}/${example}.cpp)
-       target_include_directories(${example} PUBLIC ${CMAKE_SOURCE_DIR}/include)
-       target_link_libraries(${example} jsoncpp_lib)
+foreach(example ${EXAMPLES})
+    add_executable(${example} ${example}/${example}.cpp)
+    target_include_directories(${example} PUBLIC ${CMAKE_SOURCE_DIR}/include)
+    target_link_libraries(${example} jsoncpp_lib)
 endforeach()
 
 add_custom_target(examples ALL DEPENDS ${EXAMPLES})
index ce32236..c27bbd5 100644 (file)
@@ -11,7 +11,7 @@
  */
 int main() {
   const std::string rawJson = R"({"Age": 20, "Name": "colin"})";
-  const int rawJsonLength = static_cast<int>(rawJson.length());
+  const auto rawJsonLength = static_cast<int>(rawJson.length());
   constexpr bool shouldUseOldWay = false;
   JSONCPP_STRING err;
   Json::Value root;
index d5f987e..0f5c224 100644 (file)
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED
-#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
+#ifndef JSON_ALLOCATOR_H_INCLUDED
+#define JSON_ALLOCATOR_H_INCLUDED
 
 #include <cstring>
 #include <memory>
@@ -86,4 +86,4 @@ bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
 
 #pragma pack(pop)
 
-#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
+#endif // JSON_ALLOCATOR_H_INCLUDED
index 20716b0..666fa7f 100644 (file)
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
-#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#ifndef JSON_ASSERTIONS_H_INCLUDED
+#define JSON_ASSERTIONS_H_INCLUDED
 
 #include <cstdlib>
 #include <sstream>
 
 // @todo <= add detail about condition in exception
 #define JSON_ASSERT(condition)                                                 \
-  {                                                                            \
+  do {                                                                         \
     if (!(condition)) {                                                        \
       Json::throwLogicError("assert json failed");                             \
     }                                                                          \
-  }
+  } while (0)
 
 #define JSON_FAIL_MESSAGE(message)                                             \
-  {                                                                            \
+  do {                                                                         \
     OStringStream oss;                                                         \
     oss << message;                                                            \
     Json::throwLogicError(oss.str());                                          \
     abort();                                                                   \
-  }
+  } while (0)
 
 #else // JSON_USE_EXCEPTION
 
 #endif
 
 #define JSON_ASSERT_MESSAGE(condition, message)                                \
-  if (!(condition)) {                                                          \
-    JSON_FAIL_MESSAGE(message);                                                \
-  }
+  do {                                                                         \
+    if (!(condition)) {                                                        \
+      JSON_FAIL_MESSAGE(message);                                              \
+    }                                                                          \
+  } while (0)
 
-#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#endif // JSON_ASSERTIONS_H_INCLUDED
diff --git a/include/json/autolink.h b/include/json/autolink.h
deleted file mode 100644 (file)
index b2c0f00..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_AUTOLINK_H_INCLUDED
-#define JSON_AUTOLINK_H_INCLUDED
-
-#include "config.h"
-
-#ifdef JSON_IN_CPPTL
-#include <cpptl/cpptl_autolink.h>
-#endif
-
-#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) &&                  \
-    !defined(JSON_IN_CPPTL)
-#define CPPTL_AUTOLINK_NAME "json"
-#undef CPPTL_AUTOLINK_DLL
-#ifdef JSON_DLL
-#define CPPTL_AUTOLINK_DLL
-#endif
-#include "autolink.h"
-#endif
-
-#endif // JSON_AUTOLINK_H_INCLUDED
index cbb5950..6359273 100644 (file)
 #include <string>
 #include <type_traits>
 
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//#  define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of
-/// std::map
-/// as Value container.
-//#  define JSON_USE_CPPTL_SMALLMAP 1
-
 // If non-zero, the library uses exceptions to report bad input instead of C
 // assertion macros. The default is to use exceptions.
 #ifndef JSON_USE_EXCEPTION
 /// Remarks: it is automatically defined in the generated amalgamated header.
 // #define JSON_IS_AMALGAMATION
 
-#ifdef JSON_IN_CPPTL
-#include <cpptl/config.h>
-#ifndef JSON_USE_CPPTL
-#define JSON_USE_CPPTL 1
-#endif
-#endif
-
-#ifdef JSON_IN_CPPTL
-#define JSON_API CPPTL_API
-#elif defined(JSON_DLL_BUILD)
+// Export macros for DLL visibility
+#if defined(JSON_DLL_BUILD)
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #define JSON_API __declspec(dllexport)
 #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 #elif defined(__GNUC__) || defined(__clang__)
 #define JSON_API __attribute__((visibility("default")))
 #endif // if defined(_MSC_VER)
+
 #elif defined(JSON_DLL)
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #define JSON_API __declspec(dllimport)
 #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 #endif // if defined(_MSC_VER)
-#endif // ifdef JSON_IN_CPPTL
+#endif // ifdef JSON_DLL_BUILD
+
 #if !defined(JSON_API)
 #define JSON_API
 #endif
@@ -90,20 +74,6 @@ extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
 // C++11 should be used directly in JSONCPP.
 #define JSONCPP_OVERRIDE override
 
-#if __cplusplus >= 201103L
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER < 1900
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER >= 1900
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#else
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT
-#endif
-
 #ifdef __clang__
 #if __has_extension(attribute_deprecated_with_message)
 #define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
@@ -135,23 +105,23 @@ extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
 #endif // if !defined(JSON_IS_AMALGAMATION)
 
 namespace Json {
-typedef int Int;
-typedef unsigned int UInt;
+using Int = int;
+using UInt = unsigned int;
 #if defined(JSON_NO_INT64)
-typedef int LargestInt;
-typedef unsigned int LargestUInt;
+using LargestInt = int;
+using LargestUInt = unsigned int;
 #undef JSON_HAS_INT64
 #else                 // if defined(JSON_NO_INT64)
 // For Microsoft Visual use specific types as long long is not supported
 #if defined(_MSC_VER) // Microsoft Visual Studio
-typedef __int64 Int64;
-typedef unsigned __int64 UInt64;
+using Int64 = __int64;
+using UInt64 = unsigned __int64;
 #else                 // if defined(_MSC_VER) // Other platforms, use long long
-typedef int64_t Int64;
-typedef uint64_t UInt64;
+using Int64 = int64_t;
+using UInt64 = uint64_t;
 #endif                // if defined(_MSC_VER)
-typedef Int64 LargestInt;
-typedef UInt64 LargestUInt;
+using LargestInt = Int64;
+using LargestUInt = UInt64;
 #define JSON_HAS_INT64
 #endif // if defined(JSON_NO_INT64)
 
index b0d981b..affe33a 100644 (file)
@@ -29,7 +29,7 @@ class CharReaderBuilder;
 class Features;
 
 // value.h
-typedef unsigned int ArrayIndex;
+using ArrayIndex = unsigned int;
 class StaticString;
 class Path;
 class PathArgument;
index f1b679a..5c776a1 100644 (file)
@@ -6,7 +6,7 @@
 #ifndef JSON_JSON_H_INCLUDED
 #define JSON_JSON_H_INCLUDED
 
-#include "autolink.h"
+#include "config.h"
 #include "json_features.h"
 #include "reader.h"
 #include "value.h"
index ba25e8d..7c7e9f5 100644 (file)
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
-#define CPPTL_JSON_FEATURES_H_INCLUDED
+#ifndef JSON_FEATURES_H_INCLUDED
+#define JSON_FEATURES_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "forwards.h"
@@ -58,4 +58,4 @@ public:
 
 #pragma pack(pop)
 
-#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+#endif // JSON_FEATURES_H_INCLUDED
index 359c1eb..9175466 100644 (file)
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_READER_H_INCLUDED
-#define CPPTL_JSON_READER_H_INCLUDED
+#ifndef JSON_READER_H_INCLUDED
+#define JSON_READER_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "json_features.h"
@@ -36,8 +36,8 @@ namespace Json {
 class JSONCPP_DEPRECATED(
     "Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
 public:
-  typedef char Char;
-  typedef const Char* Location;
+  using Char = char;
+  using Location = const Char*;
 
   /** \brief An error tagged with where in the JSON text it was encountered.
    *
@@ -187,7 +187,7 @@ private:
     Location extra_;
   };
 
-  typedef std::deque<ErrorInfo> Errors;
+  using Errors = std::deque<ErrorInfo>;
 
   bool readToken(Token& token);
   void skipSpaces();
@@ -226,7 +226,7 @@ private:
   static bool containsNewLine(Location begin, Location end);
   static String normalizeEOL(Location begin, Location end);
 
-  typedef std::stack<Value*> Nodes;
+  using Nodes = std::stack<Value*>;
   Nodes nodes_;
   Errors errors_;
   String document_;
@@ -299,6 +299,8 @@ public:
    *     if allowComments is false.
    * - `"allowComments": false or true`
    *   - true if comments are allowed.
+   * - `"allowTrailingCommas": false or true`
+   *   - true if trailing commas in objects and arrays are allowed.
    * - `"strictRoot": false or true`
    *   - true if root must be either an array or an object value
    * - `"allowDroppedNullPlaceholders": false or true`
@@ -398,4 +400,4 @@ JSON_API IStream& operator>>(IStream&, Value&);
 #pragma warning(pop)
 #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
 
-#endif // CPPTL_JSON_READER_H_INCLUDED
+#endif // JSON_READER_H_INCLUDED
index bf4f9c4..df1eba6 100644 (file)
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_H_INCLUDED
-#define CPPTL_JSON_H_INCLUDED
+#ifndef JSON_H_INCLUDED
+#define JSON_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "forwards.h"
 #endif
 #endif
 
+// Support for '= delete' with template declarations was a late addition
+// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
+// even though these declare themselves to be c++11 compilers.
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#if defined(__clang__) && defined(__apple_build_version__)
+#if __apple_build_version__ <= 8000042
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#elif defined(__clang__)
+#if __clang_major__ == 3 && __clang_minor__ <= 8
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#endif
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#define JSONCPP_TEMPLATE_DELETE = delete
+#endif
+#endif
+
 #include <array>
 #include <exception>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
 
-#ifndef JSON_USE_CPPTL_SMALLMAP
-#include <map>
-#else
-#include <cpptl/smallmap.h>
-#endif
-#ifdef JSON_USE_CPPTL
-#include <cpptl/forwards.h>
-#endif
-
 // Disable warning C4251: <data member>: <type> needs to have dll-interface to
 // be used by...
 #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
@@ -57,8 +67,8 @@ namespace Json {
 class JSON_API Exception : public std::exception {
 public:
   Exception(String msg);
-  ~Exception() JSONCPP_NOEXCEPT override;
-  char const* what() const JSONCPP_NOEXCEPT override;
+  ~Exception() noexcept override;
+  char const* what() const noexcept override;
 
 protected:
   String msg_;
@@ -120,11 +130,6 @@ enum PrecisionType {
   decimalPlaces          ///< we set max number of digits after "." in string
 };
 
-//# ifdef JSON_USE_CPPTL
-//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
-//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
-//# endif
-
 /** \brief Lightweight wrapper to tag static string.
  *
  * Value constructor and objectValue member assignment takes advantage of the
@@ -189,21 +194,21 @@ class JSON_API Value {
   friend class ValueIteratorBase;
 
 public:
-  typedef std::vector<String> Members;
-  typedef ValueIterator iterator;
-  typedef ValueConstIterator const_iterator;
-  typedef Json::UInt UInt;
-  typedef Json::Int Int;
+  using Members = std::vector<String>;
+  using iterator = ValueIterator;
+  using const_iterator = ValueConstIterator;
+  using UInt = Json::UInt;
+  using Int = Json::Int;
 #if defined(JSON_HAS_INT64)
-  typedef Json::UInt64 UInt64;
-  typedef Json::Int64 Int64;
+  using UInt64 = Json::UInt64;
+  using Int64 = Json::Int64;
 #endif // defined(JSON_HAS_INT64)
-  typedef Json::LargestInt LargestInt;
-  typedef Json::LargestUInt LargestUInt;
-  typedef Json::ArrayIndex ArrayIndex;
+  using LargestInt = Json::LargestInt;
+  using LargestUInt = Json::LargestUInt;
+  using ArrayIndex = Json::ArrayIndex;
 
   // Required for boost integration, e. g. BOOST_TEST
-  typedef std::string value_type;
+  using value_type = std::string;
 
 #if JSON_USE_NULLREF
   // Binary compatibility kludges, do not use.
@@ -287,11 +292,7 @@ private:
   };
 
 public:
-#ifndef JSON_USE_CPPTL_SMALLMAP
   typedef std::map<CZString, Value> ObjectValues;
-#else
-  typedef CppTL::SmallMap<CZString, Value> ObjectValues;
-#endif // ifndef JSON_USE_CPPTL_SMALLMAP
 #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
 
 public:
@@ -340,10 +341,8 @@ public:
    */
   Value(const StaticString& value);
   Value(const String& value);
-#ifdef JSON_USE_CPPTL
-  Value(const CppTL::ConstString& value);
-#endif
   Value(bool value);
+  Value(std::nullptr_t ptr) = delete;
   Value(const Value& other);
   Value(Value&& other);
   ~Value();
@@ -384,9 +383,6 @@ public:
    *  \return false if !string. (Seg-fault if str or end are NULL.)
    */
   bool getString(char const** begin, char const** end) const;
-#ifdef JSON_USE_CPPTL
-  CppTL::ConstString asConstString() const;
-#endif
   Int asInt() const;
   UInt asUInt() const;
 #if defined(JSON_HAS_INT64)
@@ -413,8 +409,8 @@ public:
   bool isObject() const;
 
   /// The `as<T>` and `is<T>` member function templates and specializations.
-  template <typename T> T as() const = delete;
-  template <typename T> bool is() const = delete;
+  template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
+  template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
 
   bool isConvertibleTo(ValueType other) const;
 
@@ -426,7 +422,7 @@ public:
   bool empty() const;
 
   /// Return !isNull()
-  JSONCPP_OP_EXPLICIT operator bool() const;
+  explicit operator bool() const;
 
   /// Remove all object members and array elements.
   /// \pre type() is arrayValue, objectValue, or nullValue
@@ -468,8 +464,10 @@ public:
   /// Equivalent to jsonvalue[jsonvalue.size()] = value;
   Value& append(const Value& value);
   Value& append(Value&& value);
+
   /// \brief Insert value in array at specific index
-  bool insert(ArrayIndex index, Value newValue);
+  bool insert(ArrayIndex index, const Value& newValue);
+  bool insert(ArrayIndex index, Value&& newValue);
 
   /// Access an object value by name, create a null member if it does not exist.
   /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
@@ -498,13 +496,6 @@ public:
    *   \endcode
    */
   Value& operator[](const StaticString& key);
-#ifdef JSON_USE_CPPTL
-  /// Access an object value by name, create a null member if it does not exist.
-  Value& operator[](const CppTL::ConstString& key);
-  /// Access an object value by name, returns null if there is no member with
-  /// that name.
-  const Value& operator[](const CppTL::ConstString& key) const;
-#endif
   /// Return the member named key if it exist, defaultValue otherwise.
   /// \note deep copy
   Value get(const char* key, const Value& defaultValue) const;
@@ -517,11 +508,6 @@ public:
   /// \note deep copy
   /// \param key may contain embedded nulls.
   Value get(const String& key, const Value& defaultValue) const;
-#ifdef JSON_USE_CPPTL
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
-#endif
   /// Most general and efficient version of isMember()const, get()const,
   /// and operator[]const
   /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
@@ -567,10 +553,6 @@ public:
   bool isMember(const String& key) const;
   /// Same as isMember(String const& key)const
   bool isMember(const char* begin, const char* end) const;
-#ifdef JSON_USE_CPPTL
-  /// Return true if the object has a member named key.
-  bool isMember(const CppTL::ConstString& key) const;
-#endif
 
   /// \brief Return a list of the member names.
   ///
@@ -579,11 +561,6 @@ public:
   /// \post if type() was nullValue, it remains nullValue
   Members getMemberNames() const;
 
-  //# ifdef JSON_USE_CPPTL
-  //      EnumMemberNames enumMemberNames() const;
-  //      EnumValues enumValues() const;
-  //# endif
-
   /// \deprecated Always pass len.
   JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
   void setComment(const char* comment, CommentPlacement placement) {
@@ -706,11 +683,6 @@ template <> inline float Value::as<float>() const { return asFloat(); }
 template <> inline const char* Value::as<const char*>() const {
   return asCString();
 }
-#ifdef JSON_USE_CPPTL
-template <> inline CppTL::ConstString Value::as<CppTL::ConstString>() const {
-  return asConstString();
-}
-#endif
 
 /** \brief Experimental and untested: represents an element of the "path" to
  * access a node.
@@ -757,8 +729,8 @@ public:
   Value& make(Value& root) const;
 
 private:
-  typedef std::vector<const PathArgument*> InArgs;
-  typedef std::vector<PathArgument> Args;
+  using InArgs = std::vector<const PathArgument*>;
+  using Args = std::vector<PathArgument>;
 
   void makePath(const String& path, const InArgs& in);
   void addPathInArg(const String& path, const InArgs& in,
@@ -773,10 +745,10 @@ private:
  */
 class JSON_API ValueIteratorBase {
 public:
-  typedef std::bidirectional_iterator_tag iterator_category;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef ValueIteratorBase SelfType;
+  using iterator_category = std::bidirectional_iterator_tag;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using SelfType = ValueIteratorBase;
 
   bool operator==(const SelfType& other) const { return isEqual(other); }
 
@@ -849,12 +821,12 @@ class JSON_API ValueConstIterator : public ValueIteratorBase {
   friend class Value;
 
 public:
-  typedef const Value value_type;
+  using value_type = const Value;
   // typedef unsigned int size_t;
   // typedef int difference_type;
-  typedef const Value& reference;
-  typedef const Value* pointer;
-  typedef ValueConstIterator SelfType;
+  using reference = const Value&;
+  using pointer = const Value*;
+  using SelfType = ValueConstIterator;
 
   ValueConstIterator();
   ValueConstIterator(ValueIterator const& other);
@@ -900,12 +872,12 @@ class JSON_API ValueIterator : public ValueIteratorBase {
   friend class Value;
 
 public:
-  typedef Value value_type;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef Value& reference;
-  typedef Value* pointer;
-  typedef ValueIterator SelfType;
+  using value_type = Value;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using reference = Value&;
+  using pointer = Value*;
+  using SelfType = ValueIterator;
 
   ValueIterator();
   explicit ValueIterator(const ValueConstIterator& other);
@@ -960,4 +932,4 @@ inline void swap(Value& a, Value& b) { a.swap(b); }
 #pragma warning(pop)
 #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
 
-#endif // CPPTL_JSON_H_INCLUDED
+#endif // JSON_H_INCLUDED
index ff94372..5b9783d 100644 (file)
@@ -9,10 +9,10 @@
 // 3. /CMakeLists.txt
 // IMPORTANT: also update the SOVERSION!!
 
-#define JSONCPP_VERSION_STRING "1.9.2"
+#define JSONCPP_VERSION_STRING "1.9.4"
 #define JSONCPP_VERSION_MAJOR 1
 #define JSONCPP_VERSION_MINOR 9
-#define JSONCPP_VERSION_PATCH 2
+#define JSONCPP_VERSION_PATCH 3
 #define JSONCPP_VERSION_QUALIFIER
 #define JSONCPP_VERSION_HEXA                                                   \
   ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) |             \
index a72c06a..fb0852a 100644 (file)
@@ -252,7 +252,7 @@ private:
   static bool hasCommentForValue(const Value& value);
   static String normalizeEOL(const String& text);
 
-  typedef std::vector<String> ChildValues;
+  using ChildValues = std::vector<String>;
 
   ChildValues childValues_;
   String document_;
@@ -326,7 +326,7 @@ private:
   static bool hasCommentForValue(const Value& value);
   static String normalizeEOL(const String& text);
 
-  typedef std::vector<String> ChildValues;
+  using ChildValues = std::vector<String>;
 
   ChildValues childValues_;
   OStream* document_;
index 8531dce..08e0f29 100644 (file)
@@ -9,7 +9,7 @@ project(
   # 2. /include/json/version.h
   # 3. /CMakeLists.txt
   # IMPORTANT: also update the SOVERSION!!
-  version : '1.9.2',
+  version : '1.9.4',
   default_options : [
     'buildtype=release',
     'cpp_std=c++11',
@@ -18,10 +18,9 @@ project(
   meson_version : '>= 0.49.0')
 
 
-jsoncpp_headers = [
+jsoncpp_headers = files([
   'include/json/allocator.h',
   'include/json/assertions.h',
-  'include/json/autolink.h',
   'include/json/config.h',
   'include/json/json_features.h',
   'include/json/forwards.h',
@@ -29,7 +28,8 @@ jsoncpp_headers = [
   'include/json/reader.h',
   'include/json/value.h',
   'include/json/version.h',
-  'include/json/writer.h']
+  'include/json/writer.h',
+])
 jsoncpp_include_directories = include_directories('include')
 
 install_headers(
@@ -45,13 +45,12 @@ else
 endif
 
 jsoncpp_lib = library(
-  'jsoncpp',
-  [ jsoncpp_headers,
-    'src/lib_json/json_tool.h',
+  'jsoncpp', files([
     'src/lib_json/json_reader.cpp',
     'src/lib_json/json_value.cpp',
-    'src/lib_json/json_writer.cpp'],
-  soversion : 22,
+    'src/lib_json/json_writer.cpp',
+  ]),
+  soversion : 24,
   install : true,
   include_directories : jsoncpp_include_directories,
   cpp_args: dll_export_flag)
@@ -67,18 +66,21 @@ import('pkgconfig').generate(
 jsoncpp_dep = declare_dependency(
   include_directories : jsoncpp_include_directories,
   link_with : jsoncpp_lib,
-  version : meson.project_version(),
-  )
+  version : meson.project_version())
 
 # tests
+if meson.is_subproject() or not get_option('tests')
+  subdir_done()
+endif
+
 python = import('python').find_installation()
 
 jsoncpp_test = executable(
-  'jsoncpp_test',
-  [ 'src/test_lib_json/jsontest.cpp',
-    'src/test_lib_json/jsontest.h',
+  'jsoncpp_test', files([
+    'src/test_lib_json/jsontest.cpp',
     'src/test_lib_json/main.cpp',
-    'src/test_lib_json/fuzz.cpp'],
+    'src/test_lib_json/fuzz.cpp',
+  ]),
   include_directories : jsoncpp_include_directories,
   link_with : jsoncpp_lib,
   install : false,
@@ -101,5 +103,17 @@ test(
     '-B',
     join_paths(meson.current_source_dir(), 'test/runjsontests.py'),
     jsontestrunner,
-    join_paths(meson.current_source_dir(), 'test/data')]
+    join_paths(meson.current_source_dir(), 'test/data')],
+  )
+test(
+  'jsonchecker_jsontestrunner',
+  python,
+  is_parallel : false,
+  args : [
+    '-B',
+    join_paths(meson.current_source_dir(), 'test/runjsontests.py'),
+    '--with-json-checker',
+    jsontestrunner,
+    join_paths(meson.current_source_dir(), 'test/data')],
+    workdir : join_paths(meson.current_source_dir(), 'test/data'),
   )
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..9c215ae
--- /dev/null
@@ -0,0 +1,5 @@
+option(
+  'tests',
+  type : 'boolean',
+  value : true,
+  description : 'Enable building tests')
index d4fa9ef..632a377 100644 (file)
@@ -1,7 +1,7 @@
 prefix=@CMAKE_INSTALL_PREFIX@
 exec_prefix=@CMAKE_INSTALL_PREFIX@
-libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
-includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=@libdir_for_pc_file@
+includedir=@includedir_for_pc_file@
 
 Name: jsoncpp
 Description: A C++ library for interacting with JSON
index 210e090..1fc71ea 100644 (file)
@@ -1,26 +1,28 @@
 if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-  # The new Python3 module is much more robust than the previous PythonInterp
-  find_package (Python3 COMPONENTS Interpreter)
-  # Set variables for backwards compatibility with cmake < 3.12.0
-  set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND})
-  set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
+    # The new Python3 module is much more robust than the previous PythonInterp
+    find_package(Python3 COMPONENTS Interpreter)
+    # Set variables for backwards compatibility with cmake < 3.12.0
+    set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND})
+    set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
 else()
-  set(Python_ADDITIONAL_VERSIONS 3.8)
-  find_package(PythonInterp 3)
+    set(Python_ADDITIONAL_VERSIONS 3.8)
+    find_package(PythonInterp 3)
 endif()
 
 add_executable(jsontestrunner_exe
-               main.cpp
-               )
+    main.cpp
+)
 
 if(BUILD_SHARED_LIBS)
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-    add_compile_definitions( JSON_DLL )
-  else()
-    add_definitions( -DJSON_DLL )
-  endif()
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+        add_compile_definitions( JSON_DLL )
+    else()
+        add_definitions(-DJSON_DLL)
+    endif()
+    target_link_libraries(jsontestrunner_exe jsoncpp_lib)
+else()
+    target_link_libraries(jsontestrunner_exe jsoncpp_static)
 endif()
-target_link_libraries(jsontestrunner_exe jsoncpp_lib)
 
 set_target_properties(jsontestrunner_exe PROPERTIES OUTPUT_NAME jsontestrunner_exe)
 
@@ -32,18 +34,18 @@ if(PYTHONINTERP_FOUND)
     # Run unit tests in post-build
     # (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
     add_custom_target(jsoncpp_readerwriter_tests
-                      "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
-                      DEPENDS jsontestrunner_exe jsoncpp_test
-                      )
+        "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
+        DEPENDS jsontestrunner_exe jsoncpp_test
+    )
     add_custom_target(jsoncpp_check DEPENDS jsoncpp_readerwriter_tests)
 
     ## Create tests for dashboard submission, allows easy review of CI results https://my.cdash.org/index.php?project=jsoncpp
     add_test(NAME jsoncpp_readerwriter
-             COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
-             WORKING_DIRECTORY "${TEST_DIR}/data"
+        COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
+        WORKING_DIRECTORY "${TEST_DIR}/data"
     )
     add_test(NAME jsoncpp_readerwriter_json_checker
-             COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" --with-json-checker  $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
-             WORKING_DIRECTORY "${TEST_DIR}/data"
+        COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" --with-json-checker  $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
+        WORKING_DIRECTORY "${TEST_DIR}/data"
     )
 endif()
index dbfbe98..3452c59 100644 (file)
@@ -57,10 +57,10 @@ static Json::String readInputTestFile(const char* path) {
   if (!file)
     return "";
   fseek(file, 0, SEEK_END);
-  long const size = ftell(file);
-  size_t const usize = static_cast<unsigned long>(size);
+  auto const size = ftell(file);
+  auto const usize = static_cast<size_t>(size);
   fseek(file, 0, SEEK_SET);
-  char* buffer = new char[size + 1];
+  auto buffer = new char[size + 1];
   buffer[size] = 0;
   Json::String text;
   if (fread(buffer, 1, usize, file) == usize)
@@ -111,7 +111,7 @@ static void printValueTree(FILE* fout, Json::Value& value,
     Json::Value::Members members(value.getMemberNames());
     std::sort(members.begin(), members.end());
     Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
-    for (auto name : members) {
+    for (const auto& name : members) {
       printValueTree(fout, value[name], path + suffix + name);
     }
   } break;
index b56788e..af26476 100644 (file)
@@ -1,13 +1,7 @@
-if( CMAKE_COMPILER_IS_GNUCXX )
-    #Get compiler version.
-    execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion
-                     OUTPUT_VARIABLE GNUCXX_VERSION )
-
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.1.2)
     #-Werror=* was introduced -after- GCC 4.1.2
-    if( GNUCXX_VERSION VERSION_GREATER 4.1.2 )
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=strict-aliasing")
-    endif()
-endif( CMAKE_COMPILER_IS_GNUCXX )
+    add_compile_options("-Werror=strict-aliasing")
+endif()
 
 include(CheckIncludeFileCXX)
 include(CheckTypeSize)
@@ -35,15 +29,15 @@ endif()
 if(NOT (HAVE_CLOCALE AND HAVE_LCONV_SIZE AND HAVE_DECIMAL_POINT AND HAVE_LOCALECONV))
     message(WARNING "Locale functionality is not supported")
     if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT)
+        add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT)
     else()
-      add_definitions(-DJSONCPP_NO_LOCALE_SUPPORT)
+        add_definitions(-DJSONCPP_NO_LOCALE_SUPPORT)
     endif()
 endif()
 
-set( JSONCPP_INCLUDE_DIR ../../include )
+set(JSONCPP_INCLUDE_DIR ../../include)
 
-set( PUBLIC_HEADERS
+set(PUBLIC_HEADERS
     ${JSONCPP_INCLUDE_DIR}/json/config.h
     ${JSONCPP_INCLUDE_DIR}/json/forwards.h
     ${JSONCPP_INCLUDE_DIR}/json/json_features.h
@@ -52,49 +46,29 @@ set( PUBLIC_HEADERS
     ${JSONCPP_INCLUDE_DIR}/json/version.h
     ${JSONCPP_INCLUDE_DIR}/json/writer.h
     ${JSONCPP_INCLUDE_DIR}/json/assertions.h
-    )
+)
 
-source_group( "Public API" FILES ${PUBLIC_HEADERS} )
+source_group("Public API" FILES ${PUBLIC_HEADERS})
 
-set(jsoncpp_sources
-                json_tool.h
-                json_reader.cpp
-                json_valueiterator.inl
-                json_value.cpp
-                json_writer.cpp)
+set(JSONCPP_SOURCES
+    json_tool.h
+    json_reader.cpp
+    json_valueiterator.inl
+    json_value.cpp
+    json_writer.cpp
+)
 
 # Install instructions for this target
 if(JSONCPP_WITH_CMAKE_PACKAGE)
     set(INSTALL_EXPORT EXPORT jsoncpp)
-else(JSONCPP_WITH_CMAKE_PACKAGE)
+else()
     set(INSTALL_EXPORT)
 endif()
 
-
-if(BUILD_SHARED_LIBS)
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-      add_compile_definitions( JSON_DLL_BUILD )
-    else()
-      add_definitions( -DJSON_DLL_BUILD )
-    endif()
-endif()
-
-
-add_library(jsoncpp_lib ${PUBLIC_HEADERS} ${jsoncpp_sources})
-set_target_properties( jsoncpp_lib PROPERTIES VERSION ${JSONCPP_VERSION} SOVERSION ${JSONCPP_SOVERSION})
-set_target_properties( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp
-                        DEBUG_OUTPUT_NAME jsoncpp${DEBUG_LIBNAME_SUFFIX} )
-set_target_properties( jsoncpp_lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
-
-# Set library's runtime search path on OSX
-if(APPLE)
-    set_target_properties( jsoncpp_lib PROPERTIES INSTALL_RPATH "@loader_path/." )
-endif()
-
 # Specify compiler features required when compiling a given target.
 # See https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html#prop_gbl:CMAKE_CXX_KNOWN_FEATURES
 # for complete list of features available
-target_compile_features(jsoncpp_lib PUBLIC
+list(APPEND REQUIRED_FEATURES
         cxx_std_11 # Compiler mode is aware of C++ 11.
         #MSVC 1900 cxx_alignas # Alignment control alignas, as defined in N2341.
         #MSVC 1900 cxx_alignof # Alignment control alignof, as defined in N2341.
@@ -141,14 +115,106 @@ target_compile_features(jsoncpp_lib PUBLIC
         cxx_variadic_templates # Variadic templates, as defined in N2242.
 )
 
-install( TARGETS jsoncpp_lib ${INSTALL_EXPORT}
-          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-          LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-          ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
 
-if(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
-    target_include_directories( jsoncpp_lib PUBLIC
-                                $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-                                $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
-                                $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>)
+if(BUILD_SHARED_LIBS)
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+        add_compile_definitions(JSON_DLL_BUILD)
+    else()
+        add_definitions(-DJSON_DLL_BUILD)
+    endif()
+
+    set(SHARED_LIB ${PROJECT_NAME}_lib)
+    add_library(${SHARED_LIB} SHARED ${PUBLIC_HEADERS} ${JSONCPP_SOURCES})
+    set_target_properties(${SHARED_LIB} PROPERTIES
+        OUTPUT_NAME jsoncpp
+        VERSION ${PROJECT_VERSION}
+        SOVERSION ${PROJECT_SOVERSION}
+        POSITION_INDEPENDENT_CODE ON
+    )
+
+    # Set library's runtime search path on OSX
+    if(APPLE)
+        set_target_properties(${SHARED_LIB} PROPERTIES INSTALL_RPATH "@loader_path/.")
+    endif()
+
+    target_compile_features(${SHARED_LIB} PUBLIC ${REQUIRED_FEATURES})
+
+    if(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
+        target_include_directories(${SHARED_LIB} PUBLIC
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
+            $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>
+        )
+    endif()
+
+    list(APPEND CMAKE_TARGETS ${SHARED_LIB})
+endif()
+
+if(BUILD_STATIC_LIBS)
+    set(STATIC_LIB ${PROJECT_NAME}_static)
+    add_library(${STATIC_LIB} STATIC ${PUBLIC_HEADERS} ${JSONCPP_SOURCES})
+
+    # avoid name clashes on windows as the shared import lib is alse named jsoncpp.lib
+    if(NOT DEFINED STATIC_SUFFIX AND BUILD_SHARED_LIBS)
+        set(STATIC_SUFFIX "_static")
+    endif()
+
+    set_target_properties(${STATIC_LIB} PROPERTIES
+        OUTPUT_NAME jsoncpp${STATIC_SUFFIX}
+        VERSION ${PROJECT_VERSION}
+    )
+
+    # Set library's runtime search path on OSX
+    if(APPLE)
+        set_target_properties(${STATIC_LIB} PROPERTIES INSTALL_RPATH "@loader_path/.")
+    endif()
+
+    target_compile_features(${STATIC_LIB} PUBLIC ${REQUIRED_FEATURES})
+
+    if(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
+        target_include_directories(${STATIC_LIB} PUBLIC
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
+            $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>
+        )
+    endif()
+
+    list(APPEND CMAKE_TARGETS ${STATIC_LIB})
 endif()
+
+if(BUILD_OBJECT_LIBS)
+    set(OBJECT_LIB ${PROJECT_NAME}_object)
+    add_library(${OBJECT_LIB} OBJECT ${PUBLIC_HEADERS} ${JSONCPP_SOURCES})
+
+    set_target_properties(${OBJECT_LIB} PROPERTIES
+        OUTPUT_NAME jsoncpp
+        VERSION ${PROJECT_VERSION}
+        SOVERSION ${PROJECT_SOVERSION}
+        POSITION_INDEPENDENT_CODE ON
+    )
+
+    # Set library's runtime search path on OSX
+    if(APPLE)
+        set_target_properties(${OBJECT_LIB} PROPERTIES INSTALL_RPATH "@loader_path/.")
+    endif()
+
+    target_compile_features(${OBJECT_LIB} PUBLIC ${REQUIRED_FEATURES})
+
+    if(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
+        target_include_directories(${OBJECT_LIB} PUBLIC
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
+            $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>
+        )
+    endif()
+
+    list(APPEND CMAKE_TARGETS ${OBJECT_LIB})
+endif()
+
+install(TARGETS ${CMAKE_TARGETS} ${INSTALL_EXPORT}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
index 0c1e88d..19922a8 100644 (file)
@@ -10,6 +10,7 @@
 #include <json/reader.h>
 #include <json/value.h>
 #endif // if !defined(JSON_IS_AMALGAMATION)
+#include <algorithm>
 #include <cassert>
 #include <cstring>
 #include <iostream>
@@ -54,7 +55,7 @@ namespace Json {
 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
 using CharReaderPtr = std::unique_ptr<CharReader>;
 #else
-typedef std::auto_ptr<CharReader> CharReaderPtr;
+using CharReaderPtr = std::auto_ptr<CharReader>;
 #endif
 
 // Implementation of class Features
@@ -77,10 +78,7 @@ Features Features::strictMode() {
 // ////////////////////////////////
 
 bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
 // Class Reader
@@ -464,7 +462,7 @@ bool Reader::readObject(Token& token) {
       Value numberName;
       if (!decodeNumber(tokenName, numberName))
         return recoverFromError(tokenObjectEnd);
-      name = String(numberName.asCString());
+      name = numberName.asString();
     } else {
       break;
     }
@@ -629,7 +627,7 @@ bool Reader::decodeString(Token& token, String& decoded) {
     Char c = *current++;
     if (c == '"')
       break;
-    else if (c == '\\') {
+    if (c == '\\') {
       if (current == end)
         return addError("Empty escape sequence in string", token, current);
       Char escape = *current++;
@@ -863,6 +861,7 @@ class OurFeatures {
 public:
   static OurFeatures all();
   bool allowComments_;
+  bool allowTrailingCommas_;
   bool strictRoot_;
   bool allowDroppedNullPlaceholders_;
   bool allowNumericKeys_;
@@ -870,6 +869,7 @@ public:
   bool failIfExtra_;
   bool rejectDupKeys_;
   bool allowSpecialFloats_;
+  bool skipBom_;
   size_t stackLimit_;
 }; // OurFeatures
 
@@ -938,6 +938,7 @@ private:
 
   bool readToken(Token& token);
   void skipSpaces();
+  void skipBom(bool skipBom);
   bool match(const Char* pattern, int patternLength);
   bool readComment();
   bool readCStyleComment(bool* containsNewLineResult);
@@ -995,10 +996,7 @@ private:
 
 bool OurReader::containsNewLine(OurReader::Location begin,
                                 OurReader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
 OurReader::OurReader(OurFeatures const& features) : features_(features) {}
@@ -1021,6 +1019,8 @@ bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
     nodes_.pop();
   nodes_.push(&root);
 
+  // skip byte order mark if it exists at the beginning of the UTF-8 text.
+  skipBom(features_.skipBom_);
   bool successful = readValue();
   nodes_.pop();
   Token token;
@@ -1175,8 +1175,11 @@ bool OurReader::readToken(Token& token) {
     if (features_.allowSingleQuotes_) {
       token.type_ = tokenString;
       ok = readStringSingleQuote();
-      break;
-    } // else fall through
+    } else {
+      // If we don't allow single quotes, this is a failure case.
+      ok = false;
+    }
+    break;
   case '/':
     token.type_ = tokenComment;
     ok = readComment();
@@ -1267,6 +1270,16 @@ void OurReader::skipSpaces() {
   }
 }
 
+void OurReader::skipBom(bool skipBom) {
+  // The default behavior is to skip BOM.
+  if (skipBom) {
+    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
+      begin_ += 3;
+      current_ = begin_;
+    }
+  }
+}
+
 bool OurReader::match(const Char* pattern, int patternLength) {
   if (end_ - current_ < patternLength)
     return false;
@@ -1349,11 +1362,10 @@ bool OurReader::readCStyleComment(bool* containsNewLineResult) {
 
   while ((current_ + 1) < end_) {
     Char c = getNextChar();
-    if (c == '*' && *current_ == '/') {
+    if (c == '*' && *current_ == '/')
       break;
-    } else if (c == '\n') {
+    if (c == '\n')
       *containsNewLineResult = true;
-    }
   }
 
   return getNextChar() == '/';
@@ -1437,7 +1449,9 @@ bool OurReader::readObject(Token& token) {
       initialTokenOk = readToken(tokenName);
     if (!initialTokenOk)
       break;
-    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+    if (tokenName.type_ == tokenObjectEnd &&
+        (name.empty() ||
+         features_.allowTrailingCommas_)) // empty object or trailing comma
       return true;
     name.clear();
     if (tokenName.type_ == tokenString) {
@@ -1491,15 +1505,19 @@ bool OurReader::readArray(Token& token) {
   Value init(arrayValue);
   currentValue().swapPayload(init);
   currentValue().setOffsetStart(token.start_ - begin_);
-  skipSpaces();
-  if (current_ != end_ && *current_ == ']') // empty array
-  {
-    Token endArray;
-    readToken(endArray);
-    return true;
-  }
   int index = 0;
   for (;;) {
+    skipSpaces();
+    if (current_ != end_ && *current_ == ']' &&
+        (index == 0 ||
+         (features_.allowTrailingCommas_ &&
+          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
+                                                      // comma
+    {
+      Token endArray;
+      readToken(endArray);
+      return true;
+    }
     Value& value = currentValue()[index++];
     nodes_.push(&value);
     bool ok = readValue();
@@ -1571,9 +1589,9 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
   // then take the inverse. This assumes that minLargestInt is only a single
   // power of 10 different in magnitude, which we check above. For the last
   // digit, we take the modulus before negating for the same reason.
-  static constexpr Value::LargestUInt negative_threshold =
+  static constexpr auto negative_threshold =
       Value::LargestUInt(-(Value::minLargestInt / 10));
-  static constexpr Value::UInt negative_last_digit =
+  static constexpr auto negative_last_digit =
       Value::UInt(-(Value::minLargestInt % 10));
 
   const Value::LargestUInt threshold =
@@ -1587,7 +1605,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
     if (c < '0' || c > '9')
       return decodeDouble(token, decoded);
 
-    const Value::UInt digit(static_cast<Value::UInt>(c - '0'));
+    const auto digit(static_cast<Value::UInt>(c - '0'));
     if (value >= threshold) {
       // We've hit or exceeded the max value divided by 10 (rounded down). If
       // a) we've only just touched the limit, meaing value == threshold,
@@ -1604,7 +1622,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
 
   if (isNegative) {
     // We use the same magnitude assumption here, just in case.
-    const Value::UInt last_digit = static_cast<Value::UInt>(value % 10);
+    const auto last_digit = static_cast<Value::UInt>(value % 10);
     decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
   } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
     decoded = Value::LargestInt(value);
@@ -1654,9 +1672,9 @@ bool OurReader::decodeString(Token& token, String& decoded) {
   Location end = token.end_ - 1;       // do not include '"'
   while (current != end) {
     Char c = *current++;
-    if (c == '"') {
+    if (c == '"')
       break;
-    } else if (c == '\\') {
+    if (c == '\\') {
       if (current == end)
         return addError("Empty escape sequence in string", token, current);
       Char escape = *current++;
@@ -1866,6 +1884,7 @@ CharReader* CharReaderBuilder::newCharReader() const {
   bool collectComments = settings_["collectComments"].asBool();
   OurFeatures features = OurFeatures::all();
   features.allowComments_ = settings_["allowComments"].asBool();
+  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
   features.strictRoot_ = settings_["strictRoot"].asBool();
   features.allowDroppedNullPlaceholders_ =
       settings_["allowDroppedNullPlaceholders"].asBool();
@@ -1878,38 +1897,37 @@ CharReader* CharReaderBuilder::newCharReader() const {
   features.failIfExtra_ = settings_["failIfExtra"].asBool();
   features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
   features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+  features.skipBom_ = settings_["skipBom"].asBool();
   return new OurCharReader(collectComments, features);
 }
-static void getValidReaderKeys(std::set<String>* valid_keys) {
-  valid_keys->clear();
-  valid_keys->insert("collectComments");
-  valid_keys->insert("allowComments");
-  valid_keys->insert("strictRoot");
-  valid_keys->insert("allowDroppedNullPlaceholders");
-  valid_keys->insert("allowNumericKeys");
-  valid_keys->insert("allowSingleQuotes");
-  valid_keys->insert("stackLimit");
-  valid_keys->insert("failIfExtra");
-  valid_keys->insert("rejectDupKeys");
-  valid_keys->insert("allowSpecialFloats");
-}
+
 bool CharReaderBuilder::validate(Json::Value* invalid) const {
-  Json::Value my_invalid;
-  if (!invalid)
-    invalid = &my_invalid; // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<String> valid_keys;
-  getValidReaderKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    String const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
+  static const auto& valid_keys = *new std::set<String>{
+      "collectComments",
+      "allowComments",
+      "allowTrailingCommas",
+      "strictRoot",
+      "allowDroppedNullPlaceholders",
+      "allowNumericKeys",
+      "allowSingleQuotes",
+      "stackLimit",
+      "failIfExtra",
+      "rejectDupKeys",
+      "allowSpecialFloats",
+      "skipBom",
+  };
+  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+    auto key = si.name();
+    if (valid_keys.count(key))
+      continue;
+    if (invalid)
+      (*invalid)[std::move(key)] = *si;
+    else
+      return false;
   }
-  return inv.empty();
+  return invalid ? invalid->empty() : true;
 }
+
 Value& CharReaderBuilder::operator[](const String& key) {
   return settings_[key];
 }
@@ -1917,6 +1935,7 @@ Value& CharReaderBuilder::operator[](const String& key) {
 void CharReaderBuilder::strictMode(Json::Value* settings) {
   //! [CharReaderBuilderStrictMode]
   (*settings)["allowComments"] = false;
+  (*settings)["allowTrailingCommas"] = false;
   (*settings)["strictRoot"] = true;
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
@@ -1925,6 +1944,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) {
   (*settings)["failIfExtra"] = true;
   (*settings)["rejectDupKeys"] = true;
   (*settings)["allowSpecialFloats"] = false;
+  (*settings)["skipBom"] = true;
   //! [CharReaderBuilderStrictMode]
 }
 // static
@@ -1932,6 +1952,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
   //! [CharReaderBuilderDefaults]
   (*settings)["collectComments"] = true;
   (*settings)["allowComments"] = true;
+  (*settings)["allowTrailingCommas"] = true;
   (*settings)["strictRoot"] = false;
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
@@ -1940,6 +1961,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
   (*settings)["failIfExtra"] = false;
   (*settings)["rejectDupKeys"] = false;
   (*settings)["allowSpecialFloats"] = false;
+  (*settings)["skipBom"] = true;
   //! [CharReaderBuilderDefaults]
 }
 
index 5c13f1f..2d7b7d9 100644 (file)
@@ -71,7 +71,7 @@ enum {
 };
 
 // Defines a char buffer for use with uintToString().
-typedef char UIntToStringBuffer[uintToStringBufferSize];
+using UIntToStringBuffer = char[uintToStringBufferSize];
 
 /** Converts an unsigned integer to string.
  * @param value Unsigned integer to convert to string
index 00d28f7..0872ff5 100644 (file)
@@ -8,16 +8,14 @@
 #include <json/value.h>
 #include <json/writer.h>
 #endif // if !defined(JSON_IS_AMALGAMATION)
+#include <algorithm>
 #include <cassert>
 #include <cmath>
+#include <cstddef>
 #include <cstring>
+#include <iostream>
 #include <sstream>
 #include <utility>
-#ifdef JSON_USE_CPPTL
-#include <cpptl/conststring.h>
-#endif
-#include <algorithm> // min()
-#include <cstddef>   // size_t
 
 // Provide implementation equivalent of std::snprintf for older _MSC compilers
 #if defined(_MSC_VER) && _MSC_VER < 1900
@@ -120,7 +118,7 @@ static inline char* duplicateStringValue(const char* value, size_t length) {
   if (length >= static_cast<size_t>(Value::maxInt))
     length = Value::maxInt - 1;
 
-  char* newString = static_cast<char*>(malloc(length + 1));
+  auto newString = static_cast<char*>(malloc(length + 1));
   if (newString == nullptr) {
     throwRuntimeError("in Json::Value::duplicateStringValue(): "
                       "Failed to allocate string value buffer");
@@ -140,8 +138,8 @@ static inline char* duplicateAndPrefixStringValue(const char* value,
                                     sizeof(unsigned) - 1U,
                       "in Json::Value::duplicateAndPrefixStringValue(): "
                       "length too big for prefixing");
-  unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
-  char* newString = static_cast<char*>(malloc(actualLength));
+  size_t actualLength = sizeof(length) + length + 1;
+  auto newString = static_cast<char*>(malloc(actualLength));
   if (newString == nullptr) {
     throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
                       "Failed to allocate string value buffer");
@@ -203,8 +201,8 @@ namespace Json {
 
 #if JSON_USE_EXCEPTION
 Exception::Exception(String msg) : msg_(std::move(msg)) {}
-Exception::~Exception() JSONCPP_NOEXCEPT = default;
-char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
+Exception::~Exception() noexcept = default;
+char const* Exception::what() const noexcept { return msg_.c_str(); }
 RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
 LogicError::LogicError(String const& msg) : Exception(msg) {}
 JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
@@ -214,8 +212,14 @@ JSONCPP_NORETURN void throwLogicError(String const& msg) {
   throw LogicError(msg);
 }
 #else // !JSON_USE_EXCEPTION
-JSONCPP_NORETURN void throwRuntimeError(String const& msg) { abort(); }
-JSONCPP_NORETURN void throwLogicError(String const& msg) { abort(); }
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
+  std::cerr << msg << std::endl;
+  abort();
+}
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
+  std::cerr << msg << std::endl;
+  abort();
+}
 #endif
 
 // //////////////////////////////////////////////////////////////////
@@ -419,14 +423,6 @@ Value::Value(const StaticString& value) {
   value_.string_ = const_cast<char*>(value.c_str());
 }
 
-#ifdef JSON_USE_CPPTL
-Value::Value(const CppTL::ConstString& value) {
-  initBasic(stringValue, true);
-  value_.string_ = duplicateAndPrefixStringValue(
-      value, static_cast<unsigned>(value.length()));
-}
-#endif
-
 Value::Value(bool value) {
   initBasic(booleanValue);
   value_.bool_ = value;
@@ -529,9 +525,10 @@ bool Value::operator<(const Value& other) const {
   }
   case arrayValue:
   case objectValue: {
-    int delta = int(value_.map_->size() - other.value_.map_->size());
-    if (delta)
-      return delta < 0;
+    auto thisSize = value_.map_->size();
+    auto otherSize = other.value_.map_->size();
+    if (thisSize != otherSize)
+      return thisSize < otherSize;
     return (*value_.map_) < (*other.value_.map_);
   }
   default:
@@ -654,15 +651,6 @@ String Value::asString() const {
   }
 }
 
-#ifdef JSON_USE_CPPTL
-CppTL::ConstString Value::asConstString() const {
-  unsigned len;
-  char const* str;
-  decodePrefixedString(isAllocated(), value_.string_, &len, &str);
-  return CppTL::ConstString(str, len);
-}
-#endif
-
 Value::Int Value::asInt() const {
   switch (type()) {
   case intValue:
@@ -894,8 +882,7 @@ ArrayIndex Value::size() const {
 bool Value::empty() const {
   if (isNull() || isArray() || isObject())
     return size() == 0U;
-  else
-    return false;
+  return false;
 }
 
 Value::operator bool() const { return !isNull(); }
@@ -1135,18 +1122,6 @@ Value& Value::operator[](const StaticString& key) {
   return resolveReference(key.c_str());
 }
 
-#ifdef JSON_USE_CPPTL
-Value& Value::operator[](const CppTL::ConstString& key) {
-  return resolveReference(key.c_str(), key.end_c_str());
-}
-Value const& Value::operator[](CppTL::ConstString const& key) const {
-  Value const* found = find(key.c_str(), key.end_c_str());
-  if (!found)
-    return nullSingleton();
-  return *found;
-}
-#endif
-
 Value& Value::append(const Value& value) { return append(Value(value)); }
 
 Value& Value::append(Value&& value) {
@@ -1158,19 +1133,22 @@ Value& Value::append(Value&& value) {
   return this->value_.map_->emplace(size(), std::move(value)).first->second;
 }
 
-bool Value::insert(ArrayIndex index, Value newValue) {
+bool Value::insert(ArrayIndex index, const Value& newValue) {
+  return insert(index, Value(newValue));
+}
+
+bool Value::insert(ArrayIndex index, Value&& newValue) {
   JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                       "in Json::Value::insert: requires arrayValue");
   ArrayIndex length = size();
   if (index > length) {
     return false;
-  } else {
-    for (ArrayIndex i = length; i > index; i--) {
-      (*this)[i] = std::move((*this)[i - 1]);
-    }
-    (*this)[index] = std::move(newValue);
-    return true;
   }
+  for (ArrayIndex i = length; i > index; i--) {
+    (*this)[i] = std::move((*this)[i - 1]);
+  }
+  (*this)[index] = std::move(newValue);
+  return true;
 }
 
 Value Value::get(char const* begin, char const* end,
@@ -1240,13 +1218,6 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) {
   return true;
 }
 
-#ifdef JSON_USE_CPPTL
-Value Value::get(const CppTL::ConstString& key,
-                 const Value& defaultValue) const {
-  return get(key.c_str(), key.end_c_str(), defaultValue);
-}
-#endif
-
 bool Value::isMember(char const* begin, char const* end) const {
   Value const* value = find(begin, end);
   return nullptr != value;
@@ -1258,12 +1229,6 @@ bool Value::isMember(String const& key) const {
   return isMember(key.data(), key.data() + key.length());
 }
 
-#ifdef JSON_USE_CPPTL
-bool Value::isMember(const CppTL::ConstString& key) const {
-  return isMember(key.c_str(), key.end_c_str());
-}
-#endif
-
 Value::Members Value::getMemberNames() const {
   JSON_ASSERT_MESSAGE(
       type() == nullValue || type() == objectValue,
@@ -1279,31 +1244,6 @@ Value::Members Value::getMemberNames() const {
   }
   return members;
 }
-//
-//# ifdef JSON_USE_CPPTL
-// EnumMemberNames
-// Value::enumMemberNames() const
-//{
-//   if ( type() == objectValue )
-//   {
-//      return CppTL::Enum::any(  CppTL::Enum::transform(
-//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-//         MemberNamesTransform() ) );
-//   }
-//   return EnumMemberNames();
-//}
-//
-//
-// EnumValues
-// Value::enumValues() const
-//{
-//   if ( type() == objectValue  ||  type() == arrayValue )
-//      return CppTL::Enum::anyValues( *(value_.map_),
-//                                     CppTL::Type<const Value &>() );
-//   return EnumValues();
-//}
-//
-//# endif
 
 static bool IsIntegral(double d) {
   double integral_part;
index 9bd9315..d6128b8 100644 (file)
@@ -30,9 +30,6 @@ void ValueIteratorBase::decrement() { --current_; }
 
 ValueIteratorBase::difference_type
 ValueIteratorBase::computeDistance(const SelfType& other) const {
-#ifdef JSON_USE_CPPTL_SMALLMAP
-  return other.current_ - current_;
-#else
   // Iterator for null value are initialized using the default
   // constructor, which initialize current_ to the default
   // std::map::iterator. As begin() and end() are two instance
@@ -53,7 +50,6 @@ ValueIteratorBase::computeDistance(const SelfType& other) const {
     ++myDistance;
   }
   return myDistance;
-#endif
 }
 
 bool ValueIteratorBase::isEqual(const SelfType& other) const {
index 519ce23..8bf02db 100644 (file)
@@ -7,7 +7,9 @@
 #include "json_tool.h"
 #include <json/writer.h>
 #endif // if !defined(JSON_IS_AMALGAMATION)
+#include <algorithm>
 #include <cassert>
+#include <cctype>
 #include <cstring>
 #include <iomanip>
 #include <memory>
@@ -86,7 +88,7 @@ namespace Json {
 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
 using StreamWriterPtr = std::unique_ptr<StreamWriter>;
 #else
-typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
+using StreamWriterPtr = std::auto_ptr<StreamWriter>;
 #endif
 
 String valueToString(LargestInt value) {
@@ -173,16 +175,12 @@ String valueToString(double value, unsigned int precision,
 
 String valueToString(bool value) { return value ? "true" : "false"; }
 
-static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
+static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
   assert(s || !n);
 
-  char const* const end = s + n;
-  for (char const* cur = s; cur < end; ++cur) {
-    if (*cur == '\\' || *cur == '\"' || *cur < ' ' ||
-        static_cast<unsigned char>(*cur) < 0x80)
-      return true;
-  }
-  return false;
+  return std::any_of(s, s + n, [](unsigned char c) {
+    return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
+  });
 }
 
 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
@@ -264,12 +262,20 @@ static String toHex16Bit(unsigned int x) {
   return result;
 }
 
+static void appendRaw(String& result, unsigned ch) {
+  result += static_cast<char>(ch);
+}
+
+static void appendHex(String& result, unsigned ch) {
+  result.append("\\u").append(toHex16Bit(ch));
+}
+
 static String valueToQuotedStringN(const char* value, unsigned length,
                                    bool emitUTF8 = false) {
   if (value == nullptr)
     return "";
 
-  if (!isAnyCharRequiredQuoting(value, length))
+  if (!doesAnyCharRequireEscaping(value, length))
     return String("\"") + value + "\"";
   // We have to walk value and escape any special characters.
   // Appending to String is not efficient, but this should be rare.
@@ -312,29 +318,26 @@ static String valueToQuotedStringN(const char* value, unsigned length,
     // sequence from occurring.
     default: {
       if (emitUTF8) {
-        result += *c;
+        unsigned codepoint = static_cast<unsigned char>(*c);
+        if (codepoint < 0x20) {
+          appendHex(result, codepoint);
+        } else {
+          appendRaw(result, codepoint);
+        }
       } else {
-        unsigned int codepoint = utf8ToCodepoint(c, end);
-        const unsigned int FIRST_NON_CONTROL_CODEPOINT = 0x20;
-        const unsigned int LAST_NON_CONTROL_CODEPOINT = 0x7F;
-        const unsigned int FIRST_SURROGATE_PAIR_CODEPOINT = 0x10000;
-        // don't escape non-control characters
-        // (short escape sequence are applied above)
-        if (FIRST_NON_CONTROL_CODEPOINT <= codepoint &&
-            codepoint <= LAST_NON_CONTROL_CODEPOINT) {
-          result += static_cast<char>(codepoint);
-        } else if (codepoint <
-                   FIRST_SURROGATE_PAIR_CODEPOINT) { // codepoint is in Basic
-                                                     // Multilingual Plane
-          result += "\\u";
-          result += toHex16Bit(codepoint);
-        } else { // codepoint is not in Basic Multilingual Plane
-                 // convert to surrogate pair first
-          codepoint -= FIRST_SURROGATE_PAIR_CODEPOINT;
-          result += "\\u";
-          result += toHex16Bit((codepoint >> 10) + 0xD800);
-          result += "\\u";
-          result += toHex16Bit((codepoint & 0x3FF) + 0xDC00);
+        unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
+        if (codepoint < 0x20) {
+          appendHex(result, codepoint);
+        } else if (codepoint < 0x80) {
+          appendRaw(result, codepoint);
+        } else if (codepoint < 0x10000) {
+          // Basic Multilingual Plane
+          appendHex(result, codepoint);
+        } else {
+          // Extended Unicode. Encode 20 bits as a surrogate pair.
+          codepoint -= 0x10000;
+          appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
+          appendHex(result, 0xdc00 + (codepoint & 0x3ff));
         }
       }
     } break;
@@ -1197,34 +1200,30 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const {
                                      endingLineFeedSymbol, usf, emitUTF8, pre,
                                      precisionType);
 }
-static void getValidWriterKeys(std::set<String>* valid_keys) {
-  valid_keys->clear();
-  valid_keys->insert("indentation");
-  valid_keys->insert("commentStyle");
-  valid_keys->insert("enableYAMLCompatibility");
-  valid_keys->insert("dropNullPlaceholders");
-  valid_keys->insert("useSpecialFloats");
-  valid_keys->insert("emitUTF8");
-  valid_keys->insert("precision");
-  valid_keys->insert("precisionType");
-}
+
 bool StreamWriterBuilder::validate(Json::Value* invalid) const {
-  Json::Value my_invalid;
-  if (!invalid)
-    invalid = &my_invalid; // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<String> valid_keys;
-  getValidWriterKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    String const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
+  static const auto& valid_keys = *new std::set<String>{
+      "indentation",
+      "commentStyle",
+      "enableYAMLCompatibility",
+      "dropNullPlaceholders",
+      "useSpecialFloats",
+      "emitUTF8",
+      "precision",
+      "precisionType",
+  };
+  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+    auto key = si.name();
+    if (valid_keys.count(key))
+      continue;
+    if (invalid)
+      (*invalid)[std::move(key)] = *si;
+    else
+      return false;
   }
-  return inv.empty();
+  return invalid ? invalid->empty() : true;
 }
+
 Value& StreamWriterBuilder::operator[](const String& key) {
   return settings_[key];
 }
index 6e301ec..1c3fce9 100644 (file)
@@ -1,46 +1,39 @@
 # vim: et ts=4 sts=4 sw=4 tw=0
 
-add_executable( jsoncpp_test
-                jsontest.cpp
-                jsontest.h
-                fuzz.cpp
-                fuzz.h
-                main.cpp
-                )
+add_executable(jsoncpp_test
+    jsontest.cpp
+    jsontest.h
+    fuzz.cpp
+    fuzz.h
+    main.cpp
+)
 
 
 if(BUILD_SHARED_LIBS)
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
-    add_compile_definitions( JSON_DLL )
-  else()
-    add_definitions( -DJSON_DLL )
-  endif()
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
+        add_compile_definitions( JSON_DLL )
+    else()
+        add_definitions( -DJSON_DLL )
+    endif()
+    target_link_libraries(jsoncpp_test jsoncpp_lib)
+else()
+    target_link_libraries(jsoncpp_test jsoncpp_static)
 endif()
-target_link_libraries(jsoncpp_test jsoncpp_lib)
 
 # another way to solve issue #90
 #set_target_properties(jsoncpp_test PROPERTIES COMPILE_FLAGS -ffloat-store)
 
+## Create tests for dashboard submission, allows easy review of CI results https://my.cdash.org/index.php?project=jsoncpp
+add_test(NAME jsoncpp_test
+    COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test>
+)
+set_target_properties(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test)
+
 # Run unit tests in post-build
 # (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
 if(JSONCPP_WITH_POST_BUILD_UNITTEST)
-    if(BUILD_SHARED_LIBS)
-        # First, copy the shared lib, for Microsoft.
-        # Then, run the test executable.
-        add_custom_command( TARGET jsoncpp_test
-                            POST_BUILD
-                            COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:jsoncpp_lib> $<TARGET_FILE_DIR:jsoncpp_test>
-                            COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test>)
-    else(BUILD_SHARED_LIBS)
-        # Just run the test executable.
-        add_custom_command( TARGET jsoncpp_test
-                            POST_BUILD
-                            COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test>)
-    endif()
-    ## Create tests for dashboard submission, allows easy review of CI results https://my.cdash.org/index.php?project=jsoncpp
-    add_test(NAME jsoncpp_test
-       COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}  $<TARGET_FILE:jsoncpp_test>
+    add_custom_command(TARGET jsoncpp_test
+        POST_BUILD
+        COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test>
     )
 endif()
-
-set_target_properties(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test)
index b31c597..5b75c22 100644 (file)
@@ -9,7 +9,6 @@
 #include <json/config.h>
 #include <json/json.h>
 #include <memory>
-#include <stdint.h>
 #include <string>
 
 namespace Json {
@@ -40,11 +39,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7);
   builder.settings_["allowSpecialFloats_"] = hash_settings & (1 << 8);
   builder.settings_["collectComments"] = hash_settings & (1 << 9);
+  builder.settings_["allowTrailingCommas_"] = hash_settings & (1 << 10);
 
   std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
 
   Json::Value root;
-  const char* data_str = reinterpret_cast<const char*>(data);
+  const auto data_str = reinterpret_cast<const char*>(data);
   try {
     reader->parse(data_str, data_str + size, &root, nullptr);
   } catch (Json::Exception const&) {
index 0cc500a..0b7d12b 100644 (file)
@@ -267,19 +267,18 @@ bool Runner::runAllTest(bool printSummary) const {
       printf("All %zu tests passed\n", count);
     }
     return true;
-  } else {
-    for (auto& result : failures) {
-      result.printFailure(count > 1);
-    }
+  }
+  for (auto& result : failures) {
+    result.printFailure(count > 1);
+  }
 
-    if (printSummary) {
-      size_t const failedCount = failures.size();
-      size_t const passedCount = count - failedCount;
-      printf("%zu/%zu tests passed (%zu failure(s))\n", passedCount, count,
-             failedCount);
-    }
-    return false;
+  if (printSummary) {
+    size_t const failedCount = failures.size();
+    size_t const passedCount = count - failedCount;
+    printf("%zu/%zu tests passed (%zu failure(s))\n", passedCount, count,
+           failedCount);
   }
+  return false;
 }
 
 bool Runner::testIndex(const Json::String& testName, size_t& indexOut) const {
@@ -308,7 +307,8 @@ int Runner::runCommandLine(int argc, const char* argv[]) const {
     if (opt == "--list-tests") {
       listTests();
       return 0;
-    } else if (opt == "--test-auto") {
+    }
+    if (opt == "--test-auto") {
       preventDialogOnCrash();
     } else if (opt == "--test") {
       ++index;
index e076f7c..4e8af0f 100644 (file)
@@ -42,7 +42,7 @@ public:
 /// Must be a POD to allow inline initialisation without stepping
 /// into the debugger.
 struct PredicateContext {
-  typedef unsigned int Id;
+  using Id = unsigned int;
   Id id_;
   const char* file_;
   unsigned int line_;
@@ -102,7 +102,7 @@ private:
   static Json::String indentText(const Json::String& text,
                                  const Json::String& indent);
 
-  typedef std::deque<Failure> Failures;
+  using Failures = std::deque<Failure>;
   Failures failures_;
   Json::String name_;
   PredicateContext rootPredicateNode_;
@@ -129,7 +129,7 @@ private:
 };
 
 /// Function pointer type for TestCase factory
-typedef TestCase* (*TestCaseFactory)();
+using TestCaseFactory = TestCase* (*)();
 
 class Runner {
 public:
@@ -168,7 +168,7 @@ private:
   static void preventDialogOnCrash();
 
 private:
-  typedef std::deque<TestCaseFactory> Factories;
+  using Factories = std::deque<TestCaseFactory>;
   Factories tests_;
 };
 
@@ -207,7 +207,7 @@ TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
 /// The predicate may do other assertions and be a member function of the
 /// fixture.
 #define JSONTEST_ASSERT_PRED(expr)                                             \
-  {                                                                            \
+  do {                                                                         \
     JsonTest::PredicateContext _minitest_Context = {                           \
         result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL};         \
     result_->predicateStackTail_->next_ = &_minitest_Context;                  \
@@ -215,7 +215,7 @@ TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
     result_->predicateStackTail_ = &_minitest_Context;                         \
     (expr);                                                                    \
     result_->popPredicateContext();                                            \
-  }
+  } while (0)
 
 /// \brief Asserts that two values are equals.
 #define JSONTEST_ASSERT_EQUAL(expected, actual)                                \
@@ -230,7 +230,7 @@ TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
 
 /// \brief Asserts that a given expression throws an exception
 #define JSONTEST_ASSERT_THROWS(expr)                                           \
-  {                                                                            \
+  do {                                                                         \
     bool _threw = false;                                                       \
     try {                                                                      \
       expr;                                                                    \
@@ -240,7 +240,7 @@ TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
     if (!_threw)                                                               \
       result_->addFailure(__FILE__, __LINE__,                                  \
                           "expected exception thrown: " #expr);                \
-  }
+  } while (0)
 
 /// \brief Begin a fixture test case.
 #define JSONTEST_FIXTURE(FixtureType, name)                                    \
index f2e4d4c..991c247 100644 (file)
 #include "jsontest.h"
 #include <cmath>
 #include <cstring>
+#include <functional>
 #include <iomanip>
 #include <iostream>
 #include <iterator>
 #include <json/config.h>
 #include <json/json.h>
 #include <limits>
+#include <memory>
 #include <sstream>
 #include <string>
 
+using CharReaderPtr = std::unique_ptr<Json::CharReader>;
+
 // Make numeric limits more convenient to talk about.
 // Assumes int type in 32 bits.
 #define kint32max Json::Value::maxInt
@@ -61,27 +65,22 @@ static std::deque<JsonTest::TestCaseFactory> local_;
 
 struct ValueTest : JsonTest::TestCase {
   Json::Value null_;
-  Json::Value emptyArray_;
-  Json::Value emptyObject_;
-  Json::Value integer_;
-  Json::Value unsignedInteger_;
-  Json::Value smallUnsignedInteger_;
-  Json::Value real_;
-  Json::Value float_;
+  Json::Value emptyArray_{Json::arrayValue};
+  Json::Value emptyObject_{Json::objectValue};
+  Json::Value integer_{123456789};
+  Json::Value unsignedInteger_{34567890};
+  Json::Value smallUnsignedInteger_{Json::Value::UInt(Json::Value::maxInt)};
+  Json::Value real_{1234.56789};
+  Json::Value float_{0.00390625f};
   Json::Value array1_;
   Json::Value object1_;
-  Json::Value emptyString_;
-  Json::Value string1_;
-  Json::Value string_;
-  Json::Value true_;
-  Json::Value false_;
-
-  ValueTest()
-      : emptyArray_(Json::arrayValue), emptyObject_(Json::objectValue),
-        integer_(123456789), unsignedInteger_(34567890u),
-        smallUnsignedInteger_(Json::Value::UInt(Json::Value::maxInt)),
-        real_(1234.56789), float_(0.00390625f), emptyString_(""), string1_("a"),
-        string_("sometext with space"), true_(true), false_(false) {
+  Json::Value emptyString_{""};
+  Json::Value string1_{"a"};
+  Json::Value string_{"sometext with space"};
+  Json::Value true_{true};
+  Json::Value false_{false};
+
+  ValueTest() {
     array1_.append(1234);
     object1_["id"] = 1234;
   }
@@ -122,53 +121,45 @@ struct ValueTest : JsonTest::TestCase {
 };
 
 Json::String ValueTest::normalizeFloatingPointStr(const Json::String& s) {
-  Json::String::size_type index = s.find_last_of("eE");
-  if (index != Json::String::npos) {
-    Json::String::size_type hasSign =
-        (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
-    Json::String::size_type exponentStartIndex = index + 1 + hasSign;
-    Json::String normalized = s.substr(0, exponentStartIndex);
-    Json::String::size_type indexDigit =
-        s.find_first_not_of('0', exponentStartIndex);
-    Json::String exponent = "0";
-    if (indexDigit != Json::String::npos) // There is an exponent different
-                                          // from 0
-    {
-      exponent = s.substr(indexDigit);
-    }
-    return normalized + exponent;
+  auto index = s.find_last_of("eE");
+  if (index == s.npos)
+    return s;
+  std::size_t signWidth = (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
+  auto exponentStartIndex = index + 1 + signWidth;
+  Json::String normalized = s.substr(0, exponentStartIndex);
+  auto indexDigit = s.find_first_not_of('0', exponentStartIndex);
+  Json::String exponent = "0";
+  if (indexDigit != s.npos) { // nonzero exponent
+    exponent = s.substr(indexDigit);
   }
-  return s;
+  return normalized + exponent;
 }
 
 JSONTEST_FIXTURE_LOCAL(ValueTest, checkNormalizeFloatingPointStr) {
-  JSONTEST_ASSERT_STRING_EQUAL("0.0", normalizeFloatingPointStr("0.0"));
-  JSONTEST_ASSERT_STRING_EQUAL("0e0", normalizeFloatingPointStr("0e0"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234.0", normalizeFloatingPointStr("1234.0"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234.0e0",
-                               normalizeFloatingPointStr("1234.0e0"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234.0e-1",
-                               normalizeFloatingPointStr("1234.0e-1"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234.0e+0",
-                               normalizeFloatingPointStr("1234.0e+0"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234.0e+1",
-                               normalizeFloatingPointStr("1234.0e+001"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e-1", normalizeFloatingPointStr("1234e-1"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e+0",
-                               normalizeFloatingPointStr("1234e+000"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e+1",
-                               normalizeFloatingPointStr("1234e+001"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e10", normalizeFloatingPointStr("1234e10"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e10",
-                               normalizeFloatingPointStr("1234e010"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e+10",
-                               normalizeFloatingPointStr("1234e+010"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e-10",
-                               normalizeFloatingPointStr("1234e-010"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e+100",
-                               normalizeFloatingPointStr("1234e+100"));
-  JSONTEST_ASSERT_STRING_EQUAL("1234e-100",
-                               normalizeFloatingPointStr("1234e-100"));
+  struct TestData {
+    std::string in;
+    std::string out;
+  } const testData[] = {
+      {"0.0", "0.0"},
+      {"0e0", "0e0"},
+      {"1234.0", "1234.0"},
+      {"1234.0e0", "1234.0e0"},
+      {"1234.0e-1", "1234.0e-1"},
+      {"1234.0e+0", "1234.0e+0"},
+      {"1234.0e+001", "1234.0e+1"},
+      {"1234e-1", "1234e-1"},
+      {"1234e+000", "1234e+0"},
+      {"1234e+001", "1234e+1"},
+      {"1234e10", "1234e10"},
+      {"1234e010", "1234e10"},
+      {"1234e+010", "1234e+10"},
+      {"1234e-010", "1234e-10"},
+      {"1234e+100", "1234e+100"},
+      {"1234e-100", "1234e-100"},
+  };
+  for (const auto& td : testData) {
+    JSONTEST_ASSERT_STRING_EQUAL(normalizeFloatingPointStr(td.in), td.out);
+  }
 }
 
 JSONTEST_FIXTURE_LOCAL(ValueTest, memberCount) {
@@ -427,8 +418,7 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, arrayInsertAtRandomIndex) {
   }
   vec.push_back(&array[4]);
   // insert rvalue at the tail
-  Json::Value index5("index5");
-  JSONTEST_ASSERT(array.insert(5, std::move(index5)));
+  JSONTEST_ASSERT(array.insert(5, "index5"));
   JSONTEST_ASSERT_EQUAL(Json::Value("index3"), array[0]);
   JSONTEST_ASSERT_EQUAL(Json::Value("index0"), array[1]);
   JSONTEST_ASSERT_EQUAL(Json::Value("index4"), array[2]);
@@ -1486,9 +1476,7 @@ void ValueTest::checkMemberCount(Json::Value& value,
   JSONTEST_ASSERT_PRED(checkConstMemberCount(value, expectedCount));
 }
 
-ValueTest::IsCheck::IsCheck()
-
-    = default;
+ValueTest::IsCheck::IsCheck() = default;
 
 void ValueTest::checkIs(const Json::Value& value, const IsCheck& check) {
   JSONTEST_ASSERT_EQUAL(check.isObject_, value.isObject());
@@ -1829,7 +1817,7 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, StaticString) {
 
 JSONTEST_FIXTURE_LOCAL(ValueTest, WideString) {
   // https://github.com/open-source-parsers/jsoncpp/issues/756
-  const std::string uni = u8"式,进"; // "\u5f0f\uff0c\u8fdb"
+  const std::string uni = u8"\u5f0f\uff0c\u8fdb"; // "式,进"
   std::string styled;
   {
     Json::Value v;
@@ -1886,7 +1874,7 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, CommentBefore) {
     Json::String result = Json::writeString(wbuilder, val);
     JSONTEST_ASSERT_STRING_EQUAL(expected, result);
     Json::String res2 = val.toStyledString();
-    Json::String exp2 = "";
+    Json::String exp2;
     exp2 += expected;
     exp2 += "\n";
     JSONTEST_ASSERT_STRING_EQUAL(exp2, res2);
@@ -2604,7 +2592,7 @@ JSONTEST_FIXTURE_LOCAL(StreamWriterTest, indentation) {
 JSONTEST_FIXTURE_LOCAL(StreamWriterTest, writeZeroes) {
   Json::String binary("hi", 3); // include trailing 0
   JSONTEST_ASSERT_EQUAL(3, binary.length());
-  Json::String expected("\"hi\\u0000\""); // unicoded zero
+  Json::String expected(R"("hi\u0000")"); // unicoded zero
   Json::StreamWriterBuilder b;
   {
     Json::Value root;
@@ -2652,214 +2640,519 @@ JSONTEST_FIXTURE_LOCAL(StreamWriterTest, unicode) {
                   "\"\\t\\n\\ud806\\udca1=\\u0133\\ud82c\\udd1b\\uff67\"\n}");
 }
 
-struct ReaderTest : JsonTest::TestCase {};
+// Control chars should be escaped regardless of UTF-8 input encoding.
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, escapeControlCharacters) {
+  auto uEscape = [](unsigned ch) {
+    static const char h[] = "0123456789abcdef";
+    std::string r = "\\u";
+    r += h[(ch >> (3 * 4)) & 0xf];
+    r += h[(ch >> (2 * 4)) & 0xf];
+    r += h[(ch >> (1 * 4)) & 0xf];
+    r += h[(ch >> (0 * 4)) & 0xf];
+    return r;
+  };
+  auto shortEscape = [](unsigned ch) -> const char* {
+    switch (ch) {
+    case '\"':
+      return "\\\"";
+    case '\\':
+      return "\\\\";
+    case '\b':
+      return "\\b";
+    case '\f':
+      return "\\f";
+    case '\n':
+      return "\\n";
+    case '\r':
+      return "\\r";
+    case '\t':
+      return "\\t";
+    default:
+      return nullptr;
+    }
+  };
+
+  Json::StreamWriterBuilder b;
+
+  for (bool emitUTF8 : {true, false}) {
+    b.settings_["emitUTF8"] = emitUTF8;
+
+    for (unsigned i = 0; i != 0x100; ++i) {
+      if (!emitUTF8 && i >= 0x80)
+        break; // The algorithm would try to parse UTF-8, so stop here.
+
+      std::string raw({static_cast<char>(i)});
+      std::string esc = raw;
+      if (i < 0x20)
+        esc = uEscape(i);
+      if (const char* shEsc = shortEscape(i))
+        esc = shEsc;
+
+      // std::cout << "emit=" << emitUTF8 << ", i=" << std::hex << i << std::dec
+      //          << std::endl;
+
+      Json::Value root;
+      root["test"] = raw;
+      JSONTEST_ASSERT_STRING_EQUAL(
+          std::string("{\n\t\"test\" : \"").append(esc).append("\"\n}"),
+          Json::writeString(b, root))
+          << ", emit=" << emitUTF8 << ", i=" << i << ", raw=\"" << raw << "\""
+          << ", esc=\"" << esc << "\"";
+    }
+  }
+}
+
+#ifdef _WIN32
+JSONTEST_FIXTURE_LOCAL(StreamWriterTest, escapeTabCharacterWindows) {
+  // Get the current locale before changing it
+  std::string currentLocale = setlocale(LC_ALL, NULL);
+  setlocale(LC_ALL, "English_United States.1252");
 
-JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithNoErrors) {
-  Json::Reader reader;
   Json::Value root;
-  bool ok = reader.parse("{ \"property\" : \"value\" }", root);
-  JSONTEST_ASSERT(ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
-  JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+  root["test"] = "\tTabTesting\t";
+
+  Json::StreamWriterBuilder b;
+
+  JSONTEST_ASSERT(Json::writeString(b, root) == "{\n\t\"test\" : "
+                                                "\"\\tTabTesting\\t\"\n}");
+
+  b.settings_["emitUTF8"] = true;
+  JSONTEST_ASSERT(Json::writeString(b, root) == "{\n\t\"test\" : "
+                                                "\"\\tTabTesting\\t\"\n}");
+
+  b.settings_["emitUTF8"] = false;
+  JSONTEST_ASSERT(Json::writeString(b, root) == "{\n\t\"test\" : "
+                                                "\"\\tTabTesting\\t\"\n}");
+
+  // Restore the locale
+  if (!currentLocale.empty())
+    setlocale(LC_ALL, currentLocale.c_str());
 }
+#endif
 
-JSONTEST_FIXTURE_LOCAL(ReaderTest, parseComment) {
-  Json::Reader reader;
+struct ReaderTest : JsonTest::TestCase {
+  void setStrictMode() {
+    reader = std::unique_ptr<Json::Reader>(
+        new Json::Reader(Json::Features{}.strictMode()));
+  }
+
+  void setFeatures(Json::Features& features) {
+    reader = std::unique_ptr<Json::Reader>(new Json::Reader(features));
+  }
+
+  void checkStructuredErrors(
+      const std::vector<Json::Reader::StructuredError>& actual,
+      const std::vector<Json::Reader::StructuredError>& expected) {
+    JSONTEST_ASSERT_EQUAL(expected.size(), actual.size());
+    for (size_t i = 0; i < actual.size(); ++i) {
+      const auto& a = actual[i];
+      const auto& e = expected[i];
+      JSONTEST_ASSERT_EQUAL(e.offset_start, a.offset_start) << i;
+      JSONTEST_ASSERT_EQUAL(e.offset_limit, a.offset_limit) << i;
+      JSONTEST_ASSERT_EQUAL(e.message, a.message) << i;
+    }
+  }
+
+  template <typename Input> void checkParse(Input&& input) {
+    JSONTEST_ASSERT(reader->parse(input, root));
+  }
+
+  template <typename Input>
+  void
+  checkParse(Input&& input,
+             const std::vector<Json::Reader::StructuredError>& structured) {
+    JSONTEST_ASSERT(!reader->parse(input, root));
+    checkStructuredErrors(reader->getStructuredErrors(), structured);
+  }
+
+  template <typename Input>
+  void checkParse(Input&& input,
+                  const std::vector<Json::Reader::StructuredError>& structured,
+                  const std::string& formatted) {
+    checkParse(input, structured);
+    JSONTEST_ASSERT_EQUAL(formatted, reader->getFormattedErrorMessages());
+  }
+
+  std::unique_ptr<Json::Reader> reader{new Json::Reader()};
   Json::Value root;
-  bool ok = reader.parse("{ /*commentBeforeValue*/"
-                         " \"property\" : \"value\" }"
-                         "//commentAfterValue\n",
-                         root);
-  JSONTEST_ASSERT(ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
-  JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+};
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithNoErrors) {
+  checkParse(R"({ "property" : "value" })");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseObject) {
+  checkParse(R"({"property"})",
+             {{11, 12, "Missing ':' after object member name"}},
+             "* Line 1, Column 12\n  Missing ':' after object member name\n");
+  checkParse(
+      R"({"property" : "value" )",
+      {{22, 22, "Missing ',' or '}' in object declaration"}},
+      "* Line 1, Column 23\n  Missing ',' or '}' in object declaration\n");
+  checkParse(R"({"property" : "value", )",
+             {{23, 23, "Missing '}' or object member name"}},
+             "* Line 1, Column 24\n  Missing '}' or object member name\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseArray) {
+  checkParse(
+      R"([ "value" )", {{10, 10, "Missing ',' or ']' in array declaration"}},
+      "* Line 1, Column 11\n  Missing ',' or ']' in array declaration\n");
+  checkParse(
+      R"([ "value1" "value2" ] )",
+      {{11, 19, "Missing ',' or ']' in array declaration"}},
+      "* Line 1, Column 12\n  Missing ',' or ']' in array declaration\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseString) {
+  checkParse(R"([ "\u8a2a" ])");
+  checkParse(
+      R"([ "\ud801" ])",
+      {{2, 10,
+        "additional six characters expected to parse unicode surrogate "
+        "pair."}},
+      "* Line 1, Column 3\n"
+      "  additional six characters expected to parse unicode surrogate pair.\n"
+      "See Line 1, Column 10 for detail.\n");
+  checkParse(R"([ "\ud801\d1234" ])",
+             {{2, 16,
+               "expecting another \\u token to begin the "
+               "second half of a unicode surrogate pair"}},
+             "* Line 1, Column 3\n"
+             "  expecting another \\u token to begin the "
+             "second half of a unicode surrogate pair\n"
+             "See Line 1, Column 12 for detail.\n");
+  checkParse(R"([ "\ua3t@" ])",
+             {{2, 10,
+               "Bad unicode escape sequence in string: "
+               "hexadecimal digit expected."}},
+             "* Line 1, Column 3\n"
+             "  Bad unicode escape sequence in string: "
+             "hexadecimal digit expected.\n"
+             "See Line 1, Column 9 for detail.\n");
+  checkParse(
+      R"([ "\ua3t" ])",
+      {{2, 9, "Bad unicode escape sequence in string: four digits expected."}},
+      "* Line 1, Column 3\n"
+      "  Bad unicode escape sequence in string: four digits expected.\n"
+      "See Line 1, Column 6 for detail.\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseComment) {
+  checkParse(
+      R"({ /*commentBeforeValue*/ "property" : "value" }//commentAfterValue)"
+      "\n");
+  checkParse(" true //comment1\n//comment2\r//comment3\r\n");
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, streamParseWithNoErrors) {
-  Json::Reader reader;
-  std::string styled = "{ \"property\" : \"value\" }";
+  std::string styled = R"({ "property" : "value" })";
   std::istringstream iss(styled);
-  Json::Value root;
-  bool ok = reader.parse(iss, root);
-  JSONTEST_ASSERT(ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
-  JSONTEST_ASSERT(reader.getStructuredErrors().empty());
+  checkParse(iss);
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithNoErrorsTestingOffsets) {
-  Json::Reader reader;
-  Json::Value root;
-  bool ok = reader.parse("{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
-                         "{ \"nested\" : -6.2e+15, \"bool\" : true}, \"null\" :"
-                         " null, \"false\" : false }",
-                         root);
-  JSONTEST_ASSERT(ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages().empty());
-  JSONTEST_ASSERT(reader.getStructuredErrors().empty());
-  JSONTEST_ASSERT(root["property"].getOffsetStart() == 15);
-  JSONTEST_ASSERT(root["property"].getOffsetLimit() == 34);
-  JSONTEST_ASSERT(root["property"][0].getOffsetStart() == 16);
-  JSONTEST_ASSERT(root["property"][0].getOffsetLimit() == 23);
-  JSONTEST_ASSERT(root["property"][1].getOffsetStart() == 25);
-  JSONTEST_ASSERT(root["property"][1].getOffsetLimit() == 33);
-  JSONTEST_ASSERT(root["obj"].getOffsetStart() == 44);
-  JSONTEST_ASSERT(root["obj"].getOffsetLimit() == 81);
-  JSONTEST_ASSERT(root["obj"]["nested"].getOffsetStart() == 57);
-  JSONTEST_ASSERT(root["obj"]["nested"].getOffsetLimit() == 65);
-  JSONTEST_ASSERT(root["obj"]["bool"].getOffsetStart() == 76);
-  JSONTEST_ASSERT(root["obj"]["bool"].getOffsetLimit() == 80);
-  JSONTEST_ASSERT(root["null"].getOffsetStart() == 92);
-  JSONTEST_ASSERT(root["null"].getOffsetLimit() == 96);
-  JSONTEST_ASSERT(root["false"].getOffsetStart() == 108);
-  JSONTEST_ASSERT(root["false"].getOffsetLimit() == 113);
-  JSONTEST_ASSERT(root.getOffsetStart() == 0);
-  JSONTEST_ASSERT(root.getOffsetLimit() == 115);
+  checkParse(R"({)"
+             R"( "property" : ["value", "value2"],)"
+             R"( "obj" : { "nested" : -6.2e+15, "bool" : true},)"
+             R"( "null" : null,)"
+             R"( "false" : false)"
+             R"( })");
+  auto checkOffsets = [&](const Json::Value& v, int start, int limit) {
+    JSONTEST_ASSERT_EQUAL(start, v.getOffsetStart());
+    JSONTEST_ASSERT_EQUAL(limit, v.getOffsetLimit());
+  };
+  checkOffsets(root, 0, 115);
+  checkOffsets(root["property"], 15, 34);
+  checkOffsets(root["property"][0], 16, 23);
+  checkOffsets(root["property"][1], 25, 33);
+  checkOffsets(root["obj"], 44, 81);
+  checkOffsets(root["obj"]["nested"], 57, 65);
+  checkOffsets(root["obj"]["bool"], 76, 80);
+  checkOffsets(root["null"], 92, 96);
+  checkOffsets(root["false"], 108, 113);
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithOneError) {
-  Json::Reader reader;
-  Json::Value root;
-  bool ok = reader.parse("{ \"property\" :: \"value\" }", root);
-  JSONTEST_ASSERT(!ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                  "* Line 1, Column 15\n  Syntax error: value, object or array "
-                  "expected.\n");
-  std::vector<Json::Reader::StructuredError> errors =
-      reader.getStructuredErrors();
-  JSONTEST_ASSERT(errors.size() == 1);
-  JSONTEST_ASSERT(errors.at(0).offset_start == 14);
-  JSONTEST_ASSERT(errors.at(0).offset_limit == 15);
-  JSONTEST_ASSERT(errors.at(0).message ==
-                  "Syntax error: value, object or array expected.");
+  checkParse(R"({ "property" :: "value" })",
+             {{14, 15, "Syntax error: value, object or array expected."}},
+             "* Line 1, Column 15\n  Syntax error: value, object or array "
+             "expected.\n");
+  checkParse("s", {{0, 1, "Syntax error: value, object or array expected."}},
+             "* Line 1, Column 1\n  Syntax error: value, object or array "
+             "expected.\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, parseSpecialFloat) {
+  checkParse(R"({ "a" : Infi })",
+             {{8, 9, "Syntax error: value, object or array expected."}},
+             "* Line 1, Column 9\n  Syntax error: value, object or array "
+             "expected.\n");
+  checkParse(R"({ "a" : Infiniaa })",
+             {{8, 9, "Syntax error: value, object or array expected."}},
+             "* Line 1, Column 9\n  Syntax error: value, object or array "
+             "expected.\n");
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, strictModeParseNumber) {
-  Json::Features feature;
-  Json::Reader reader(feature.strictMode());
-  Json::Value root;
-  bool ok = reader.parse("123", root);
-  JSONTEST_ASSERT(!ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                  "* Line 1, Column 1\n"
-                  "  A valid JSON document must be either an array or"
-                  " an object value.\n");
-  std::vector<Json::Reader::StructuredError> errors =
-      reader.getStructuredErrors();
-  JSONTEST_ASSERT(errors.size() == 1);
-  JSONTEST_ASSERT(errors.at(0).offset_start == 0);
-  JSONTEST_ASSERT(errors.at(0).offset_limit == 3);
-  JSONTEST_ASSERT(errors.at(0).message ==
-                  "A valid JSON document must be either an array or"
-                  " an object value.");
+  setStrictMode();
+  checkParse(
+      "123",
+      {{0, 3,
+        "A valid JSON document must be either an array or an object value."}},
+      "* Line 1, Column 1\n"
+      "  A valid JSON document must be either an array or an object value.\n");
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, parseChineseWithOneError) {
-  Json::Reader reader;
-  Json::Value root;
-  bool ok = reader.parse("{ \"pr佐藤erty\" :: \"value\" }", root);
-  JSONTEST_ASSERT(!ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                  "* Line 1, Column 19\n  Syntax error: value, object or array "
-                  "expected.\n");
-  std::vector<Json::Reader::StructuredError> errors =
-      reader.getStructuredErrors();
-  JSONTEST_ASSERT(errors.size() == 1);
-  JSONTEST_ASSERT(errors.at(0).offset_start == 18);
-  JSONTEST_ASSERT(errors.at(0).offset_limit == 19);
-  JSONTEST_ASSERT(errors.at(0).message ==
-                  "Syntax error: value, object or array expected.");
+  checkParse(R"({ "pr)"
+             u8"\u4f50\u85e4" // 佐藤
+             R"(erty" :: "value" })",
+             {{18, 19, "Syntax error: value, object or array expected."}},
+             "* Line 1, Column 19\n  Syntax error: value, object or array "
+             "expected.\n");
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, parseWithDetailError) {
-  Json::Reader reader;
-  Json::Value root;
-  bool ok = reader.parse("{ \"property\" : \"v\\alue\" }", root);
-  JSONTEST_ASSERT(!ok);
-  JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                  "* Line 1, Column 16\n  Bad escape sequence in string\nSee "
-                  "Line 1, Column 20 for detail.\n");
-  std::vector<Json::Reader::StructuredError> errors =
-      reader.getStructuredErrors();
-  JSONTEST_ASSERT(errors.size() == 1);
-  JSONTEST_ASSERT(errors.at(0).offset_start == 15);
-  JSONTEST_ASSERT(errors.at(0).offset_limit == 23);
-  JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
+  checkParse(R"({ "property" : "v\alue" })",
+             {{15, 23, "Bad escape sequence in string"}},
+             "* Line 1, Column 16\n"
+             "  Bad escape sequence in string\n"
+             "See Line 1, Column 20 for detail.\n");
 }
 
 JSONTEST_FIXTURE_LOCAL(ReaderTest, pushErrorTest) {
-  Json::Reader reader;
-  Json::Value root;
-  {
-    bool ok = reader.parse("{ \"AUTHOR\" : 123 }", root);
-    JSONTEST_ASSERT(ok);
-    if (!root["AUTHOR"].isString()) {
-      ok = reader.pushError(root["AUTHOR"], "AUTHOR must be a string");
-    }
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                    "* Line 1, Column 14\n"
-                    "  AUTHOR must be a string\n");
+  checkParse(R"({ "AUTHOR" : 123 })");
+  if (!root["AUTHOR"].isString()) {
+    JSONTEST_ASSERT(
+        reader->pushError(root["AUTHOR"], "AUTHOR must be a string"));
   }
-  {
-    bool ok = reader.parse("{ \"AUTHOR\" : 123 }", root);
-    JSONTEST_ASSERT(ok);
-    if (!root["AUTHOR"].isString()) {
-      ok = reader.pushError(root["AUTHOR"], "AUTHOR must be a string",
-                            root["AUTHOR"]);
-    }
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
-                    "* Line 1, Column 14\n"
-                    "  AUTHOR must be a string\n"
-                    "See Line 1, Column 14 for detail.\n");
+  JSONTEST_ASSERT_STRING_EQUAL(reader->getFormattedErrorMessages(),
+                               "* Line 1, Column 14\n"
+                               "  AUTHOR must be a string\n");
+
+  checkParse(R"({ "AUTHOR" : 123 })");
+  if (!root["AUTHOR"].isString()) {
+    JSONTEST_ASSERT(reader->pushError(root["AUTHOR"], "AUTHOR must be a string",
+                                      root["AUTHOR"]));
   }
+  JSONTEST_ASSERT_STRING_EQUAL(reader->getFormattedErrorMessages(),
+                               "* Line 1, Column 14\n"
+                               "  AUTHOR must be a string\n"
+                               "See Line 1, Column 14 for detail.\n");
+}
+
+JSONTEST_FIXTURE_LOCAL(ReaderTest, allowNumericKeysTest) {
+  Json::Features features;
+  features.allowNumericKeys_ = true;
+  setFeatures(features);
+  checkParse(R"({ 123 : "abc" })");
 }
 
 struct CharReaderTest : JsonTest::TestCase {};
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithNoErrors) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   Json::Value root;
-  char const doc[] = "{ \"property\" : \"value\" }";
+  char const doc[] = R"({ "property" : "value" })";
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
   JSONTEST_ASSERT(errs.empty());
-  delete reader;
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithNoErrorsTestingOffsets) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   Json::Value root;
   char const doc[] = "{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
-                     "{ \"nested\" : -6.2e+15, \"bool\" : true}, \"null\" : "
-                     "null, \"false\" : false }";
+                     "{ \"nested\" : -6.2e+15, \"num\" : +123, \"bool\" : "
+                     "true}, \"null\" : null, \"false\" : false }";
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
   JSONTEST_ASSERT(errs.empty());
-  delete reader;
+}
+
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseNumber) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::String errs;
+  Json::Value root;
+  {
+    // if intvalue > threshold, treat the number as a double.
+    // 21 digits
+    char const doc[] = "[111111111111111111111]";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT(errs.empty());
+    JSONTEST_ASSERT_EQUAL(1.1111111111111111e+020, root[0]);
+  }
+}
+
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseString) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::Value root;
+  Json::String errs;
+  {
+    char const doc[] = "[\"\"]";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT(errs.empty());
+    JSONTEST_ASSERT_EQUAL("", root[0]);
+  }
+  {
+    char const doc[] = R"(["\u8A2a"])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT(errs.empty());
+    JSONTEST_ASSERT_EQUAL(u8"\u8A2a", root[0].asString()); // "訪"
+  }
+  {
+    char const doc[] = R"([ "\uD801" ])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 3\n"
+                            "  additional six characters expected to "
+                            "parse unicode surrogate pair.\n"
+                            "See Line 1, Column 10 for detail.\n");
+  }
+  {
+    char const doc[] = R"([ "\uD801\d1234" ])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 3\n"
+                            "  expecting another \\u token to begin the "
+                            "second half of a unicode surrogate pair\n"
+                            "See Line 1, Column 12 for detail.\n");
+  }
+  {
+    char const doc[] = R"([ "\ua3t@" ])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 3\n"
+                            "  Bad unicode escape sequence in string: "
+                            "hexadecimal digit expected.\n"
+                            "See Line 1, Column 9 for detail.\n");
+  }
+  {
+    char const doc[] = R"([ "\ua3t" ])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(
+        errs ==
+        "* Line 1, Column 3\n"
+        "  Bad unicode escape sequence in string: four digits expected.\n"
+        "See Line 1, Column 6 for detail.\n");
+  }
+  {
+    b.settings_["allowSingleQuotes"] = true;
+    CharReaderPtr charreader(b.newCharReader());
+    char const doc[] = R"({'a': 'x\ty', "b":'x\\y'})";
+    bool ok = charreader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT_STRING_EQUAL("", errs);
+    JSONTEST_ASSERT_EQUAL(2u, root.size());
+    JSONTEST_ASSERT_STRING_EQUAL("x\ty", root["a"].asString());
+    JSONTEST_ASSERT_STRING_EQUAL("x\\y", root["b"].asString());
+  }
+}
+
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseComment) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::Value root;
+  Json::String errs;
+  {
+    char const doc[] = "//comment1\n { //comment2\n \"property\" :"
+                       " \"value\" //comment3\n } //comment4\n";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT(errs.empty());
+    JSONTEST_ASSERT_EQUAL("value", root["property"]);
+  }
+  {
+    char const doc[] = "{ \"property\" //comment\n : \"value\" }";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 14\n"
+                            "  Missing ':' after object member name\n");
+  }
+  {
+    char const doc[] = "//comment1\n [ //comment2\n \"value\" //comment3\n,"
+                       " //comment4\n true //comment5\n ] //comment6\n";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(ok);
+    JSONTEST_ASSERT(errs.empty());
+    JSONTEST_ASSERT_EQUAL("value", root[0]);
+    JSONTEST_ASSERT_EQUAL(true, root[1]);
+  }
+}
+
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseObjectWithErrors) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::Value root;
+  Json::String errs;
+  {
+    char const doc[] = R"({ "property" : "value" )";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 24\n"
+                            "  Missing ',' or '}' in object declaration\n");
+    JSONTEST_ASSERT_EQUAL("value", root["property"]);
+  }
+  {
+    char const doc[] = R"({ "property" : "value" ,)";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 25\n"
+                            "  Missing '}' or object member name\n");
+    JSONTEST_ASSERT_EQUAL("value", root["property"]);
+  }
+}
+
+JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseArrayWithErrors) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::Value root;
+  Json::String errs;
+  {
+    char const doc[] = "[ \"value\" ";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 11\n"
+                            "  Missing ',' or ']' in array declaration\n");
+    JSONTEST_ASSERT_EQUAL("value", root[0]);
+  }
+  {
+    char const doc[] = R"([ "value1" "value2" ])";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT(errs == "* Line 1, Column 12\n"
+                            "  Missing ',' or ']' in array declaration\n");
+    JSONTEST_ASSERT_EQUAL("value1", root[0]);
+  }
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithOneError) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   Json::Value root;
-  char const doc[] = "{ \"property\" :: \"value\" }";
+  char const doc[] = R"({ "property" :: "value" })";
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(!ok);
   JSONTEST_ASSERT(errs ==
                   "* Line 1, Column 15\n  Syntax error: value, object or array "
                   "expected.\n");
-  delete reader;
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseChineseWithOneError) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   Json::Value root;
   char const doc[] = "{ \"pr佐藤erty\" :: \"value\" }";
@@ -2868,49 +3161,45 @@ JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseChineseWithOneError) {
   JSONTEST_ASSERT(errs ==
                   "* Line 1, Column 19\n  Syntax error: value, object or array "
                   "expected.\n");
-  delete reader;
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithDetailError) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   Json::Value root;
-  char const doc[] = "{ \"property\" : \"v\\alue\" }";
+  char const doc[] = R"({ "property" : "v\alue" })";
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(!ok);
   JSONTEST_ASSERT(errs ==
                   "* Line 1, Column 16\n  Bad escape sequence in string\nSee "
                   "Line 1, Column 20 for detail.\n");
-  delete reader;
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithStackLimit) {
   Json::CharReaderBuilder b;
   Json::Value root;
-  char const doc[] = "{ \"property\" : \"value\" }";
+  char const doc[] = R"({ "property" : "value" })";
   {
     b.settings_["stackLimit"] = 2;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT(errs.empty());
     JSONTEST_ASSERT_EQUAL("value", root["property"]);
-    delete reader;
   }
   {
     b.settings_["stackLimit"] = 1;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     JSONTEST_ASSERT_THROWS(
         reader->parse(doc, doc + std::strlen(doc), &root, &errs));
-    delete reader;
   }
 }
 
 JSONTEST_FIXTURE_LOCAL(CharReaderTest, testOperator) {
-  const std::string styled = "{ \"property\" : \"value\" }";
+  const std::string styled = R"({ "property" : "value" })";
   std::istringstream iss(styled);
   Json::Value root;
   iss >> root;
@@ -2923,10 +3212,10 @@ JSONTEST_FIXTURE_LOCAL(CharReaderStrictModeTest, dupKeys) {
   Json::CharReaderBuilder b;
   Json::Value root;
   char const doc[] =
-      "{ \"property\" : \"value\", \"key\" : \"val1\", \"key\" : \"val2\" }";
+      R"({ "property" : "value", "key" : "val1", "key" : "val2" })";
   {
     b.strictMode(&b.settings_);
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(!ok);
@@ -2934,7 +3223,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderStrictModeTest, dupKeys) {
                                  "  Duplicate key: 'key'\n",
                                  errs);
     JSONTEST_ASSERT_EQUAL("val1", root["key"]); // so far
-    delete reader;
   }
 }
 struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
@@ -2943,20 +3231,19 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue164) {
   // This is interpreted as a string value followed by a colon.
   Json::CharReaderBuilder b;
   Json::Value root;
-  char const doc[] = " \"property\" : \"value\" }";
+  char const doc[] = R"( "property" : "value" })";
   {
     b.settings_["failIfExtra"] = false;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT(errs.empty());
     JSONTEST_ASSERT_EQUAL("property", root);
-    delete reader;
   }
   {
     b.settings_["failIfExtra"] = true;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(!ok);
@@ -2964,11 +3251,10 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue164) {
                                  "  Extra non-whitespace after JSON value.\n",
                                  errs);
     JSONTEST_ASSERT_EQUAL("property", root);
-    delete reader;
   }
   {
     b.strictMode(&b.settings_);
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(!ok);
@@ -2976,12 +3262,11 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue164) {
                                  "  Extra non-whitespace after JSON value.\n",
                                  errs);
     JSONTEST_ASSERT_EQUAL("property", root);
-    delete reader;
   }
   {
     b.strictMode(&b.settings_);
     b.settings_["failIfExtra"] = false;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(!ok);
@@ -2990,16 +3275,16 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue164) {
         "  A valid JSON document must be either an array or an object value.\n",
         errs);
     JSONTEST_ASSERT_EQUAL("property", root);
-    delete reader;
   }
 }
+
 JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue107) {
   // This is interpreted as an int value followed by a colon.
   Json::CharReaderBuilder b;
   Json::Value root;
   char const doc[] = "1:2:3";
   b.settings_["failIfExtra"] = true;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(!ok);
@@ -3007,7 +3292,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, issue107) {
                                "  Extra non-whitespace after JSON value.\n",
                                errs);
   JSONTEST_ASSERT_EQUAL(1, root.asInt());
-  delete reader;
 }
 JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterObject) {
   Json::CharReaderBuilder b;
@@ -3015,13 +3299,12 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterObject) {
   {
     char const doc[] = "{ \"property\" : \"value\" } //trailing\n//comment\n";
     b.settings_["failIfExtra"] = true;
-    Json::CharReader* reader(b.newCharReader());
+    CharReaderPtr reader(b.newCharReader());
     Json::String errs;
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT_STRING_EQUAL("", errs);
     JSONTEST_ASSERT_EQUAL("value", root["property"]);
-    delete reader;
   }
 }
 JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterArray) {
@@ -3029,147 +3312,119 @@ JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterArray) {
   Json::Value root;
   char const doc[] = "[ \"property\" , \"value\" ] //trailing\n//comment\n";
   b.settings_["failIfExtra"] = true;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
   JSONTEST_ASSERT_STRING_EQUAL("", errs);
   JSONTEST_ASSERT_EQUAL("value", root[1u]);
-  delete reader;
 }
 JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, commentAfterBool) {
   Json::CharReaderBuilder b;
   Json::Value root;
   char const doc[] = " true /*trailing\ncomment*/";
   b.settings_["failIfExtra"] = true;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::String errs;
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
   JSONTEST_ASSERT_STRING_EQUAL("", errs);
   JSONTEST_ASSERT_EQUAL(true, root.asBool());
-  delete reader;
 }
-struct CharReaderAllowDropNullTest : JsonTest::TestCase {};
 
-JSONTEST_FIXTURE_LOCAL(CharReaderAllowDropNullTest, issue178) {
+JSONTEST_FIXTURE_LOCAL(CharReaderFailIfExtraTest, parseComment) {
   Json::CharReaderBuilder b;
-  b.settings_["allowDroppedNullPlaceholders"] = true;
+  b.settings_["failIfExtra"] = true;
+  CharReaderPtr reader(b.newCharReader());
   Json::Value root;
   Json::String errs;
-  Json::CharReader* reader(b.newCharReader());
-  {
-    char const doc[] = "{\"a\":,\"b\":true}";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(2u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
-  }
-  {
-    char const doc[] = "{\"a\":}";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(1u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
-  }
-  {
-    char const doc[] = "[]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(errs.empty());
-    JSONTEST_ASSERT_EQUAL(0u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::arrayValue, root);
-  }
-  {
-    char const doc[] = "[null]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(errs.empty());
-    JSONTEST_ASSERT_EQUAL(1u, root.size());
-  }
-  {
-    char const doc[] = "[,]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(2u, root.size());
-  }
-  {
-    char const doc[] = "[,,,]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(4u, root.size());
-  }
-  {
-    char const doc[] = "[null,]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(2u, root.size());
-  }
-  {
-    char const doc[] = "[,null]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(errs.empty());
-    JSONTEST_ASSERT_EQUAL(2u, root.size());
-  }
   {
-    char const doc[] = "[,,]";
+    char const doc[] = " true //comment1\n//comment2\r//comment3\r\n";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(3u, root.size());
+    JSONTEST_ASSERT_EQUAL(true, root.asBool());
   }
   {
-    char const doc[] = "[null,,]";
+    char const doc[] = " true //com\rment";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(3u, root.size());
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT_STRING_EQUAL("* Line 2, Column 1\n"
+                                 "  Extra non-whitespace after JSON value.\n",
+                                 errs);
+    JSONTEST_ASSERT_EQUAL(true, root.asBool());
   }
   {
-    char const doc[] = "[,null,]";
+    char const doc[] = " true //com\nment";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(3u, root.size());
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT_STRING_EQUAL("* Line 2, Column 1\n"
+                                 "  Extra non-whitespace after JSON value.\n",
+                                 errs);
+    JSONTEST_ASSERT_EQUAL(true, root.asBool());
   }
-  {
-    char const doc[] = "[,,null]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(errs.empty());
-    JSONTEST_ASSERT_EQUAL(3u, root.size());
+}
+
+struct CharReaderAllowDropNullTest : JsonTest::TestCase {
+  using Value = Json::Value;
+  using ValueCheck = std::function<void(const Value&)>;
+
+  Value nullValue = Value{Json::nullValue};
+  Value emptyArray = Value{Json::arrayValue};
+
+  ValueCheck checkEq(const Value& v) {
+    return [=](const Value& root) { JSONTEST_ASSERT_EQUAL(root, v); };
   }
-  {
-    char const doc[] = "[[],,,]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(4u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[0u]);
+
+  static ValueCheck objGetAnd(std::string idx, ValueCheck f) {
+    return [=](const Value& root) { f(root.get(idx, true)); };
   }
-  {
-    char const doc[] = "[,[],,]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
-    JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT_STRING_EQUAL("", errs);
-    JSONTEST_ASSERT_EQUAL(4u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[1u]);
+
+  static ValueCheck arrGetAnd(int idx, ValueCheck f) {
+    return [=](const Value& root) { f(root[idx]); };
   }
-  {
-    char const doc[] = "[,,,[]]";
-    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+};
+
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowDropNullTest, issue178) {
+  struct TestSpec {
+    int line;
+    std::string doc;
+    size_t rootSize;
+    ValueCheck onRoot;
+  };
+  const TestSpec specs[] = {
+      {__LINE__, R"({"a":,"b":true})", 2, objGetAnd("a", checkEq(nullValue))},
+      {__LINE__, R"({"a":,"b":true})", 2, objGetAnd("a", checkEq(nullValue))},
+      {__LINE__, R"({"a":})", 1, objGetAnd("a", checkEq(nullValue))},
+      {__LINE__, "[]", 0, checkEq(emptyArray)},
+      {__LINE__, "[null]", 1, nullptr},
+      {__LINE__, "[,]", 2, nullptr},
+      {__LINE__, "[,,,]", 4, nullptr},
+      {__LINE__, "[null,]", 2, nullptr},
+      {__LINE__, "[,null]", 2, nullptr},
+      {__LINE__, "[,,]", 3, nullptr},
+      {__LINE__, "[null,,]", 3, nullptr},
+      {__LINE__, "[,null,]", 3, nullptr},
+      {__LINE__, "[,,null]", 3, nullptr},
+      {__LINE__, "[[],,,]", 4, arrGetAnd(0, checkEq(emptyArray))},
+      {__LINE__, "[,[],,]", 4, arrGetAnd(1, checkEq(emptyArray))},
+      {__LINE__, "[,,,[]]", 4, arrGetAnd(3, checkEq(emptyArray))},
+  };
+  for (const auto& spec : specs) {
+    Json::CharReaderBuilder b;
+    b.settings_["allowDroppedNullPlaceholders"] = true;
+    std::unique_ptr<Json::CharReader> reader(b.newCharReader());
+
+    Json::Value root;
+    Json::String errs;
+    bool ok = reader->parse(spec.doc.data(), spec.doc.data() + spec.doc.size(),
+                            &root, &errs);
     JSONTEST_ASSERT(ok);
-    JSONTEST_ASSERT(errs.empty());
-    JSONTEST_ASSERT_EQUAL(4u, root.size());
-    JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[3u]);
+    JSONTEST_ASSERT_STRING_EQUAL(errs, "");
+    if (spec.onRoot) {
+      spec.onRoot(root);
+    }
   }
-  delete reader;
 }
 
 struct CharReaderAllowNumericKeysTest : JsonTest::TestCase {};
@@ -3179,7 +3434,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowNumericKeysTest, allowNumericKeys) {
   b.settings_["allowNumericKeys"] = true;
   Json::Value root;
   Json::String errs;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   char const doc[] = "{15:true,-16:true,12.01:true}";
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
@@ -3188,7 +3443,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowNumericKeysTest, allowNumericKeys) {
   JSONTEST_ASSERT_EQUAL(true, root.get("15", false));
   JSONTEST_ASSERT_EQUAL(true, root.get("-16", false));
   JSONTEST_ASSERT_EQUAL(true, root.get("12.01", false));
-  delete reader;
 }
 
 struct CharReaderAllowSingleQuotesTest : JsonTest::TestCase {};
@@ -3198,7 +3452,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowSingleQuotesTest, issue182) {
   b.settings_["allowSingleQuotes"] = true;
   Json::Value root;
   Json::String errs;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   {
     char const doc[] = "{'a':true,\"b\":true}";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
@@ -3217,7 +3471,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowSingleQuotesTest, issue182) {
     JSONTEST_ASSERT_STRING_EQUAL("x", root["a"].asString());
     JSONTEST_ASSERT_STRING_EQUAL("y", root["b"].asString());
   }
-  delete reader;
 }
 
 struct CharReaderAllowZeroesTest : JsonTest::TestCase {};
@@ -3227,7 +3480,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowZeroesTest, issue176) {
   b.settings_["allowSingleQuotes"] = true;
   Json::Value root;
   Json::String errs;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   {
     char const doc[] = "{'a':true,\"b\":true}";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
@@ -3246,20 +3499,43 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowZeroesTest, issue176) {
     JSONTEST_ASSERT_STRING_EQUAL("x", root["a"].asString());
     JSONTEST_ASSERT_STRING_EQUAL("y", root["b"].asString());
   }
-  delete reader;
 }
 
 struct CharReaderAllowSpecialFloatsTest : JsonTest::TestCase {};
 
+JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, specialFloat) {
+  Json::CharReaderBuilder b;
+  CharReaderPtr reader(b.newCharReader());
+  Json::Value root;
+  Json::String errs;
+  {
+    char const doc[] = "{\"a\": NaN}";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT_STRING_EQUAL(
+        "* Line 1, Column 7\n"
+        "  Syntax error: value, object or array expected.\n",
+        errs);
+  }
+  {
+    char const doc[] = "{\"a\": Infinity}";
+    bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
+    JSONTEST_ASSERT(!ok);
+    JSONTEST_ASSERT_STRING_EQUAL(
+        "* Line 1, Column 7\n"
+        "  Syntax error: value, object or array expected.\n",
+        errs);
+  }
+}
+
 JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, issue209) {
   Json::CharReaderBuilder b;
   b.settings_["allowSpecialFloats"] = true;
   Json::Value root;
   Json::String errs;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   {
-    char const doc[] =
-        "{\"a\":NaN,\"b\":Infinity,\"c\":-Infinity,\"d\":+Infinity}";
+    char const doc[] = R"({"a":NaN,"b":Infinity,"c":-Infinity,"d":+Infinity})";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT_STRING_EQUAL("", errs);
@@ -3310,7 +3586,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, issue209) {
   }
 
   {
-    char const doc[] = "{\"posInf\": +Infinity, \"NegInf\": -Infinity}";
+    char const doc[] = R"({"posInf": +Infinity, "NegInf": -Infinity})";
     bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
     JSONTEST_ASSERT(ok);
     JSONTEST_ASSERT_STRING_EQUAL("", errs);
@@ -3320,7 +3596,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, issue209) {
     JSONTEST_ASSERT_EQUAL(-std::numeric_limits<double>::infinity(),
                           root["NegInf"].asDouble());
   }
-  delete reader;
 }
 
 struct EscapeSequenceTest : JsonTest::TestCase {};
@@ -3339,7 +3614,7 @@ JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, readerParseEscapeSequence) {
 
 JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, charReaderParseEscapeSequence) {
   Json::CharReaderBuilder b;
-  Json::CharReader* reader(b.newCharReader());
+  CharReaderPtr reader(b.newCharReader());
   Json::Value root;
   Json::String errs;
   char const doc[] = "[\"\\\"\",\"\\/\",\"\\\\\",\"\\b\","
@@ -3348,7 +3623,6 @@ JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, charReaderParseEscapeSequence) {
   bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
   JSONTEST_ASSERT(ok);
   JSONTEST_ASSERT(errs.empty());
-  delete reader;
 }
 
 JSONTEST_FIXTURE_LOCAL(EscapeSequenceTest, writeEscapeSequence) {
@@ -3393,6 +3667,32 @@ JSONTEST_FIXTURE_LOCAL(BuilderTest, settings) {
   }
 }
 
+struct BomTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(BomTest, skipBom) {
+  const std::string with_bom = "\xEF\xBB\xBF{\"key\" : \"value\"}";
+  Json::Value root;
+  JSONCPP_STRING errs;
+  std::istringstream iss(with_bom);
+  bool ok = parseFromStream(Json::CharReaderBuilder(), iss, &root, &errs);
+  // The default behavior is to skip the BOM, so we can parse it normally.
+  JSONTEST_ASSERT(ok);
+  JSONTEST_ASSERT(errs.empty());
+  JSONTEST_ASSERT_STRING_EQUAL(root["key"].asString(), "value");
+}
+JSONTEST_FIXTURE_LOCAL(BomTest, notSkipBom) {
+  const std::string with_bom = "\xEF\xBB\xBF{\"key\" : \"value\"}";
+  Json::Value root;
+  JSONCPP_STRING errs;
+  std::istringstream iss(with_bom);
+  Json::CharReaderBuilder b;
+  b.settings_["skipBom"] = false;
+  bool ok = parseFromStream(b, iss, &root, &errs);
+  // Detect the BOM, and failed on it.
+  JSONTEST_ASSERT(!ok);
+  JSONTEST_ASSERT(!errs.empty());
+}
+
 struct IteratorTest : JsonTest::TestCase {};
 
 JSONTEST_FIXTURE_LOCAL(IteratorTest, convert) {
@@ -3445,8 +3745,8 @@ JSONTEST_FIXTURE_LOCAL(IteratorTest, distance) {
   }
   {
     Json::Value empty;
-    JSONTEST_ASSERT_EQUAL(empty.end() - empty.end(), 0);
-    JSONTEST_ASSERT_EQUAL(empty.end() - empty.begin(), 0);
+    JSONTEST_ASSERT_EQUAL(0, empty.end() - empty.end());
+    JSONTEST_ASSERT_EQUAL(0, empty.end() - empty.begin());
   }
 }
 
@@ -3534,7 +3834,7 @@ JSONTEST_FIXTURE_LOCAL(IteratorTest, constness) {
   for (; iter != value.end(); ++iter) {
     out << *iter << ',';
   }
-  Json::String expected = "\" 9\",\"10\",\"11\",";
+  Json::String expected = R"(" 9","10","11",)";
   JSONTEST_ASSERT_STRING_EQUAL(expected, out.str());
 }
 
@@ -3565,8 +3865,8 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
 int main(int argc, const char* argv[]) {
   JsonTest::Runner runner;
 
-  for (auto it = local_.begin(); it != local_.end(); it++) {
-    runner.add(*it);
+  for (auto& local : local_) {
+    runner.add(local);
   }
 
   return runner.runCommandLine(argc, argv);
@@ -3584,9 +3884,6 @@ JSONTEST_FIXTURE_LOCAL(MemberTemplateAs, BehavesSameAsNamedAs) {
   const Json::Value jstr = "hello world";
   JSONTEST_ASSERT_STRING_EQUAL(jstr.as<const char*>(), jstr.asCString());
   JSONTEST_ASSERT_STRING_EQUAL(jstr.as<Json::String>(), jstr.asString());
-#ifdef JSON_USE_CPPTL
-  JSONTEST_ASSERT_STRING_EQUAL(js.as<CppTL::ConstString>(), js.asConstString());
-#endif
   EqEval(Json::Int(64), [](const Json::Value& j) { return j.asInt(); });
   EqEval(Json::UInt(64), [](const Json::Value& j) { return j.asUInt(); });
 #if defined(JSON_HAS_INT64)
diff --git a/test/data/fail_invalid_quote.json b/test/data/fail_invalid_quote.json
new file mode 100644 (file)
index 0000000..dae27f5
--- /dev/null
@@ -0,0 +1 @@
+{'//this is bad JSON.'}
\ No newline at end of file
diff --git a/test/data/fail_test_array_02.json b/test/data/fail_test_array_02.json
new file mode 100644 (file)
index 0000000..222a1b4
--- /dev/null
@@ -0,0 +1 @@
+[1,,]
diff --git a/test/data/fail_test_object_01.json b/test/data/fail_test_object_01.json
new file mode 100644 (file)
index 0000000..46fd39a
--- /dev/null
@@ -0,0 +1 @@
+{ "count" : 1234,, }
diff --git a/test/data/test_array_08.expected b/test/data/test_array_08.expected
new file mode 100644 (file)
index 0000000..ef1f262
--- /dev/null
@@ -0,0 +1,2 @@
+.=[]
+.[0]=1
diff --git a/test/data/test_array_08.json b/test/data/test_array_08.json
new file mode 100644 (file)
index 0000000..e8b1a17
--- /dev/null
@@ -0,0 +1 @@
+[1,]
diff --git a/test/data/test_object_05.expected b/test/data/test_object_05.expected
new file mode 100644 (file)
index 0000000..79391c2
--- /dev/null
@@ -0,0 +1,2 @@
+.={}
+.count=1234
diff --git a/test/data/test_object_05.json b/test/data/test_object_05.json
new file mode 100644 (file)
index 0000000..c4344b1
--- /dev/null
@@ -0,0 +1 @@
+{ "count" : 1234, }
index dfdeca3..5496e2c 100644 (file)
@@ -62,6 +62,10 @@ def safeReadFile(path):
     except IOError as e:
         return '<File "%s" is missing: %s>' % (path,e)
 
+class FailError(Exception):
+    def __init__(self, msg):
+        super(Exception, self).__init__(msg)
+
 def runAllTests(jsontest_executable_path, input_dir = None,
                  use_valgrind=False, with_json_checker=False,
                  writerClass='StyledWriter'):
@@ -69,45 +73,26 @@ def runAllTests(jsontest_executable_path, input_dir = None,
         input_dir = os.path.join(os.getcwd(), 'data')
     tests = glob(os.path.join(input_dir, '*.json'))
     if with_json_checker:
-        all_test_jsonchecker = glob(os.path.join(input_dir, '../jsonchecker', '*.json'))
-        # These tests fail with strict json support, but pass with jsoncpp extra lieniency
-        """
-        Failure details:
-        * Test ../jsonchecker/fail25.json
-        Parsing should have failed:
-        ["     tab     character       in      string  "]
-
-        * Test ../jsonchecker/fail13.json
-        Parsing should have failed:
-        {"Numbers cannot have leading zeroes": 013}
-
-        * Test ../jsonchecker/fail18.json
-        Parsing should have failed:
-        [[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
-
-        * Test ../jsonchecker/fail8.json
-        Parsing should have failed:
-        ["Extra close"]]
-
-        * Test ../jsonchecker/fail7.json
-        Parsing should have failed:
-        ["Comma after the close"],
-
-        * Test ../jsonchecker/fail10.json
-        Parsing should have failed:
-        {"Extra value after close": true} "misplaced quoted value"
-
-        * Test ../jsonchecker/fail27.json
-        Parsing should have failed:
-        ["line
-        break"]
-        """
-        known_differences_withjsonchecker = [ "fail25.json", "fail13.json", "fail18.json", "fail8.json",
-                                              "fail7.json", "fail10.json", "fail27.json" ]
-        test_jsonchecker = [ test for test in all_test_jsonchecker if os.path.basename(test) not in known_differences_withjsonchecker ]
+        all_tests = glob(os.path.join(input_dir, '../jsonchecker', '*.json'))
+        # These tests fail with strict json support, but pass with JsonCPP's
+        # extra leniency features. When adding a new exclusion to this list,
+        # remember to add the test's number and reasoning here:
+        known = ["fail{}.json".format(n) for n in [
+            4, 9, # fail because we allow trailing commas
+            7,    # fails because we allow commas after close
+            8,    # fails because we allow extra close
+            10,   # fails because we allow extra values after close
+            13,   # fails because we allow leading zeroes in numbers
+            18,   # fails because we allow deeply nested values
+            25,   # fails because we allow tab characters in strings
+            27,   # fails because we allow string line breaks
+        ]]
+        test_jsonchecker = [ test for test in all_tests
+                             if os.path.basename(test) not in known]
 
     else:
         test_jsonchecker = []
+
     failed_tests = []
     valgrind_path = use_valgrind and VALGRIND_CMD or ''
     for input_path in tests + test_jsonchecker:
@@ -161,10 +146,9 @@ def runAllTests(jsontest_executable_path, input_dir = None,
             print()
         print('Test results: %d passed, %d failed.' % (len(tests)-len(failed_tests),
                                                        len(failed_tests)))
-        return 1
+        raise FailError(repr(failed_tests))
     else:
         print('All %d tests passed.' % len(tests))
-        return 0
 
 def main():
     from optparse import OptionParser
@@ -187,24 +171,21 @@ def main():
         input_path = os.path.normpath(os.path.abspath(args[1]))
     else:
         input_path = None
-    status = runAllTests(jsontest_executable_path, input_path,
+    runAllTests(jsontest_executable_path, input_path,
                          use_valgrind=options.valgrind,
                          with_json_checker=options.with_json_checker,
                          writerClass='StyledWriter')
-    if status:
-        sys.exit(status)
-    status = runAllTests(jsontest_executable_path, input_path,
+    runAllTests(jsontest_executable_path, input_path,
                          use_valgrind=options.valgrind,
                          with_json_checker=options.with_json_checker,
                          writerClass='StyledStreamWriter')
-    if status:
-        sys.exit(status)
-    status = runAllTests(jsontest_executable_path, input_path,
+    runAllTests(jsontest_executable_path, input_path,
                          use_valgrind=options.valgrind,
                          with_json_checker=options.with_json_checker,
                          writerClass='BuiltStyledStreamWriter')
-    if status:
-        sys.exit(status)
 
 if __name__ == '__main__':
-    main()
+    try:
+        main()
+    except FailError:
+        sys.exit(1)