Compare commits

..

118 Commits
1.9.1 ... 1.9.3

Author SHA1 Message Date
kabeer27
6aba23f4a8 Fixes Oss-Fuzz issue: 21916 (#1180)
* Fix heap-buffer-overflow in json_reader
2020-05-29 21:50:26 +08:00
Billy Donahue
c161f4ac69 Escape control chars even if emitting UTF8 (#1178)
* Escape control chars even if emitting UTF8

See #1176
Fixes #1175

* review comments

* fix test by stopping early enough to punt on utf8-input.
2020-05-21 11:30:59 -04:00
Billy Donahue
75b360af4a spot fix #1171: isprint argument must be representable as unsigned char (#1173) 2020-05-13 18:37:02 -04:00
Rosen Penev
e36cff19f0 clang-tidy + any_of usage (#1171)
* [clang-tidy] change functions to static

Found with readability-convert-member-functions-to-static

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* optimize JsonWriter::validate #1171

* do the same for json_reader

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* use std::any_of

Also simplified two loops.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

Co-authored-by: Billy Donahue <billy.donahue@gmail.com>
2020-05-12 19:19:36 -04:00
Edward Brey
b8cb8889aa Added current dir specifier for PowerShell (#1169)
The `./` is needed before `vcpkg install jsoncpp` when installing with PowerShell.
2020-05-08 09:00:12 +08:00
Chen
d2d4c74a03 Update README.md and add dota17 to AUTHORS list. (#1168)
* update README

* add dota17 to AUTHORS list
2020-04-30 18:05:17 +08:00
Chen
8b7ea09b80 Bump soversion to 24 (#1167) 2020-04-30 17:58:07 +08:00
xkszltl
a4fb5db543 Put ".exe" and ".dll" together to make test usable in build dir. (#1166) 2020-04-30 10:31:54 +08:00
Christopher Dunn
d517d598a7 Merge pull request #1165 from mrjoel/mrjoel/cmake-updates
Improve CMake correctness and handling
2020-04-29 08:27:43 -05:00
Joel Johnson
e9b0b96be6 Remove redundant cmake_minimum_required from example
This is already covered by the toplevel CMake, which also serves to
provide a consistent minimum version.
2020-04-28 15:21:16 -06:00
Joel Johnson
3f0d63b5a9 Use internal CMake compiler version directly 2020-04-28 15:21:16 -06:00
Joel Johnson
524234e479 Use non-version checked add_compile_options
Commit aebc7fa added version checks for CMake compatibility. In reality,
only the add_compile_definitions need the check - add_compile_options
itself has been supported since 3.0. Tested and confirmed built
successfully with CMake 3.8.0.
2020-04-28 15:21:16 -06:00
Joel Johnson
9abf11935c Remove unused CMake variable
EXTRA_CXX_FLAGS is never defined, making this a noop. Further,
COMPILE_OPTIONS is invalid to set as a DIRECTORY property.
2020-04-28 15:21:16 -06:00
Joel Johnson
8a5e792f20 Add Clang support, be explicit about MSVC flags 2020-04-28 15:21:16 -06:00
Joel Johnson
30eb5ce128 Check compiler using CMAKE_CXX_COMPILER_ID
Since the introduction of CMAKE_COMPILER_IS_GNUCXX CMake has
suggested using CMAKE_CXX_COMPILER_ID for more general checks.
2020-04-28 15:21:16 -06:00
Joel Johnson
12ceb01485 Always use consistent CXX_STANDARD
Since CMake has subdirectory variable scope, unilaterally set the
CMAKE_CXX_STANDARD variable to use C++11. This covers cases with the
library being included externally, both in cases of only C++98 being
specified, as well as later versions being specified (since the
CXX_STANDARD itself isn't a library dependency, only the PUBLIC
target_compile_features on jsoncpp_lib). The previous direct check for
C++98 is handled by requiring C++11 on this library; should the
compiler being used not support C++11 then CMake will issue an error.
2020-04-28 15:21:16 -06:00
Joel Johnson
edc6239f39 Not needed to specify CMAKE_MACOSX_RPATH
As of CMake 3.0 with CMP0042, MACOSX_RPATH is enabled by default.
Since the validated version used by jsoncpp is later than 3.0,
this is already covered.
2020-04-28 15:21:16 -06:00
Joel Johnson
5a0152ae1b Only set CMAKE_BUILD_TYPE for single config generators 2020-04-28 15:21:16 -06:00
Joel Johnson
c648b0378a Consolidate setting of jsoncpp target properties 2020-04-28 15:21:16 -06:00
Joel Johnson
a3afd74b80 Don't use unique variable for postfix
The more general CMake way to handle library suffixing is to set
CMAKE_<CONFIG>_POSTFIX, so setting the Debug output suffix name should
be more correctly done by the caller or CMake configurer by setting
the desired value in CMAKE_DEBUG_POSTFIX.
2020-04-28 15:21:16 -06:00
Chen
2cb16b35dc allowBom -> skipBom (#1162) 2020-04-28 17:30:08 +08:00
Chen
83946a28db Ignore byte order mark in the head of UTF-8 text. (#1149)
* Ignore bom at the beginning of the UTF-8 text
2020-04-28 15:16:05 +08:00
bcsgh
91f1553f2c Make throwRuntimeError/throwLogicError print msg when built with JSON_USE_EXCEPTION=0 2020-04-24 13:40:51 -05:00
Christopher Dunn
5813ab1bc1 Merge branch 'fixup-tests' into 'master' (#1102) 2020-04-24 13:06:44 -05:00
Christopher Dunn
a0b8c3ecb4 Do not run colliding tests at same time 2020-04-24 12:43:17 -05:00
Jordan Bayles
b349221938 Cleanup test configurations 2020-04-24 12:43:17 -05:00
Jordan Bayles
9e23f66f61 Issue 1102: Fixup test suite, fix broken tests
A recent PR broken the JsonChecker tests by adding support for trailing
commas. This didn't end up breaking the build, because those tests
aren't run, except locally and only using CMake.

This patch fixes the tests by adding exclusions for trailing comma
tests, as well as updates Meson to run these tests as part of `ninja
test`.

See issue #1102.
2020-04-24 12:43:17 -05:00
Christopher Dunn
411d88fae8 Stop checking status; raise instead 2020-04-24 12:43:17 -05:00
Christopher Dunn
1ff6bb65a0 ninja test 2020-04-24 12:43:17 -05:00
Ben Boeckel
8b20b7a317 amalgamate: add version.h and allocator.h to the forwards header
Required to get JSONCPP_USING_SECURE_MEMORY and the SecureAllocator
available for the definition of Allocator.
2020-04-23 23:22:10 -05:00
Stefano Fiorentino
2e54e8ff1c adding myself to AUTHORS as per commits (#1109)
commits:
ff923658c4
5907cef86c
2020-04-23 22:52:56 -05:00
Rosen Penev
90ca694e46 clang-tidy fixes again (#1155)
* [clang-tidy] remove redundant string initialization

Found with readability-redundant-string-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] switch to raw strings

Easier to read.

Found with modernize-raw-string-literal

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] fix performance issues

Found with performance*

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* fix extra comma warnings

Found with clang's -Wextra-semi-stmt

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* remove JSONCPP_OP_EXPLICIT

This codebase in C++11. No need for compatibility with C++98.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* remove JSONCPP_NOEXCEPT

This codebase is C++11 now. No need for this macro.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-04-12 01:26:04 -04:00
Chen
3beb37ea14 revert trailing comma in old Reader (#1126) 2020-02-13 13:25:08 -08:00
David Gobbi
dc180eb25e Remove '=delete' from template methods for Xcode 8 (#1133)
For Apple clang-800.0.42.1, which was released with Xcode 8 in
September 2016, the '=delete' on the 'is' and 'as' methods causes
the following errors for value.h:

  inline declaration of 'as<bool>' follows non-inline definition
  inline declaration of 'is<bool>' follows non-inline definition

etcetera for the other specializations of 'is' and 'as'.  The same
problem also occurs for clang-3.8 but not clang-3.9 or later.
2020-02-13 13:22:49 -08:00
Claus Klein
a6fe8e27d8 Use ccache right (#1139)
* Prevent cmakelint warnings

Use 4 spaces for indent, no tabs

* Use ccache right

fix indents too at CMakeLists.txt
2020-02-13 13:20:46 -08:00
Rosen Penev
edf528edfa clang-tidy fixes again (#1087)
* [clang-tidy] Do not use else after return

Found with readability-else-after-return

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Convert several loops to be range based

Found with modernize-loop-convert

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Replace deprecated C headers

Found with modernize-deprecated-headers

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Use auto where applicable

Found with modernize-use-auto

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* .clang-tidy: Add these checks
2020-02-02 23:03:45 -05:00
Chen
6317f9a406 fix compile warnning using cmake (#1132) 2020-01-20 17:12:35 +08:00
David Seifert
6bc55ec35d Meson updates (#1124)
* Modernize meson.build

* Make tests optional
* Use `files()` for quick sanity checks

* Bump version to 1.9.3

* Bump SOVERSION, as some functions were removed
  and structs were changed, as determined by
  libabigail.
2020-01-07 09:23:50 +08:00
Andrew Childs
f11611c878 json_writer: fix inverted sense in isAnyCharRequiredQuoting (#1120)
This bug is only affects platforms where `char` is unsigned.

When char is a signed type, values >= 0x80 are also considered < 0,
and hence require escaping due to the < ' ' condition.

When char is an unsigned type, values >= 0x80 match none of the
conditions and are considered safe to emit without escaping.

This shows up as a test failure:

* Detail of EscapeSequenceTest/writeEscapeSequence test failure:
/build/source/src/test_lib_json/main.cpp(3370): expected == result
  Expected: '["\"","\\","\b","\f","\n","\r","\t","\u0278","\ud852\udf62"]
  '
  Actual  : '["\"","\\","\b","\f","\n","\r","\t","ɸ","𤭢"]
  '
2019-12-28 15:04:24 +08:00
Chen
8f7f35c5cd Replace Raw Unicode in Testcase (#1127)
* use unicode code point

* change values' order, because JSONTEST_ASSERT_EQUAL(expected, actual)
2019-12-26 16:08:17 +08:00
theirix
7e5485ab5b Add option JSONCPP_WITH_EXAMPLE (#1099)
* Add option JSONCPP_WITH_EXAMPLE

Allows to conditionally build examples as
it has been done for tests.  Useful for packaging.

* Do not build example by default
2019-12-23 11:04:44 +08:00
Chen
92d90250f2 fix Reader bug and add testcase (#1122) 2019-12-23 10:56:54 +08:00
Billy Donahue
d6c4a8fb2d tweak to avoid implicit narrowing warning. (#1114)
* tweak to avoid implicit narrowing warning.

change an int to size_t #1113

* Update main.cpp
2019-12-11 12:12:37 -08:00
Chen
a3c8642886 Add test cases for Reader (#1108)
* update testcase for reader

* add a helper function

* refactor structured error testing
2019-12-04 10:24:52 +08:00
Chen
2983f5a89a Run Clang-tidy with modernize-use-auto (#1077)
* Run clang-tidy modify with modernize-use-auto
* Use using instead of typedef
2019-12-04 09:08:45 +08:00
Chen
a0bd9adfef Add an insert overload function (#1110) 2019-12-03 09:13:42 +08:00
Billy Donahue
9e0d70aa66 eliminate some redundancy in test_lib_json/main.cpp (#1104)
refactor test 'CharReaderAllowDropNullTest/issue178'
2019-11-15 09:18:32 -05:00
dota17
f200239d5b Add some testcases for charReader (#1095)
* update charReader test case

* remove C ++ compiler version switch for smart pointer

* remove comma test due to PR #1098
2019-11-15 14:51:43 +08:00
Christopher Dunn
cfc3e927fc Merge pull request #1101 from open-source-parsers/drop_cpptl_support
Issue 1100: Drop CPPTL support
2019-11-15 00:31:06 -06:00
Jordan Bayles
a481201af1 Readd some overzealously removed code 2019-11-14 10:21:15 -08:00
Jordan Bayles
9704cedb20 Issue 1100: Drop CPPTL support
CPPTL support is no longer relevant to JsonCpp, and can be removed from
the library. This patch removes all mentions of CPPTL, by removing all
definitions and code sections conditionally compiled only when JsonCpp
is used with CPPTL. Include guards are also renamed to not refer to
CPPTL where appropriate.
2019-11-14 09:38:11 -08:00
Christopher Dunn
781eec4da8 Merge pull request #1098 from cdunn2001/allow-trailing-commas
Allow trailing comma in objects and arrays
2019-11-14 00:21:18 -06:00
Jacob Bundgaard
1c8f7d8ae5 Run clang-format 2019-11-14 00:05:24 -06:00
Jacob Bundgaard
554d961625 Allow trailing comma in arrays if dropped null placeholders are not allowed 2019-11-14 00:05:24 -06:00
Jacob Bundgaard
01db7b7430 Allow trailing comma in objects 2019-11-14 00:05:24 -06:00
Billy Donahue
d2e6a971f4 some test coverage for Value::iterator (#1093) 2019-11-12 02:16:54 -05:00
Billy Donahue
53c8e2cb3b Issue 1066 (#1080)
Implemented `as<T>()` and `is<T>()` with accompanying tests
2019-11-11 22:43:52 -05:00
Jordan Bayles
645cd0412c Number fixes (#1053)
* cleaning up the logic for parsing numbers

* Add Testcases for new Reader in jsontestrunner
2019-11-09 11:49:16 +08:00
dota17
ff58fdcc75 Update coverage badge (#1088)
update coverage badge
change int to Json::ArrayIndex in for-loop
2019-11-07 15:25:06 +08:00
chenguoping
82b736734d add testcase for json_value.cpp to improve coverage [90+%] 2019-11-06 21:38:47 -08:00
Hans Johnson
a86e129983 ENH: Move to requiring python 3
Resolves #1081

See for more details:
https://devguide.python.org/devcycle/#end-of-life-branches
https://pythonclock.org/
2019-11-06 21:06:57 -08:00
Hans Johnson
5fb17a66b8 COMP: Remove shadow variable warning
jsoncpp/src/test_lib_json/main.cpp:2261:30: warning: declaration shadows a local variable [-Wshadow]
    Json::StyledStreamWriter writer;
                             ^
jsoncpp/src/test_lib_json/main.cpp:2237:28: note: previous declaration is here
  Json::StyledStreamWriter writer;
                           ^
2019-11-06 21:06:57 -08:00
Hans Johnson
7429bb2bfa COMP: Fix type mismatch ambiguity
jsoncpp/src/test_lib_json/main.cpp:354:31: error:
  implicit conversion changes signedness: 'int' to
  'std::__1::vector<Json::Value *, std::__1::allocator<Json::Value *> >::size_type'
  aka 'unsigned long') [-Werror,-Wsign-conversion]
    JSONTEST_ASSERT_EQUAL(vec[i], &array[i]);
                          ~~~ ^
2019-11-06 21:06:57 -08:00
Christopher Dunn
2eb20a938c Remove deprecated makerelease.py
re: #1081
2019-11-04 01:41:35 -08:00
Billy Donahue
638ad269e7 Explicitly specify hexfloat in TestResult operator<< (#1078) 2019-11-04 01:37:02 -08:00
Christopher Dunn
ec9302c4ed Avoid deprecated Meson feature
* https://mesonbuild.com/Python-3-module.html

> This module is deprecated and replaced by the python module.
2019-11-04 01:29:02 -08:00
Christopher Dunn
fb9aaf8112 Update meson/python
```
DEPRECATION: Project targetting '>= 0.41.1' but tried to use feature deprecated since '0.48.0': python3 module
Build targets in project: 3
WARNING: Deprecated features used:
 * 0.48.0: {'python3 module'}
```
2019-11-04 01:29:02 -08:00
dota17
2703c306a3 add coverage badge in readme (#1072) 2019-10-26 17:03:05 +08:00
dota17
c634b98e7d modify README.md: add some badges (#1070) 2019-10-25 17:12:11 +08:00
dota17
6c9408d128 remove pushError in CharReader (#1055) 2019-10-23 15:31:25 -07:00
dota17
54bd178bd8 update testcases to improve coverage (#1061) 2019-10-23 15:30:34 -07:00
Jacob Bundgaard
41ffff01d3 Fix link to Amalgamated wiki article (#1064) 2019-10-18 11:06:56 -07:00
Hans Johnson
b082693b9e COMP: Improve const correctness for ValueIterators (#1056)
The protected deref method had inconsistent interface
of being a const function that returned a non-const
reference.  Resolves #914.
2019-10-17 10:52:13 -07:00
nicolaswilson
a955529e47 Added emitUTF8 setting. (#1045)
* Added emitUTF8 setting to emit UTF8 format JSON.

* Added a test for emitUTF8, with it in default, on and off states.

* Review comments addressed.

* Merged master into my branch & resolved conflicts.

* Fix clang-format errors.

* Fix clang-format errors.

* Fixed clang-format errors.

* Fixed clang-format errors.
2019-10-17 10:47:51 -07:00
dota17
f59ac2a1d7 add coveralls to test coverage (#1060) 2019-10-17 10:46:41 -07:00
Jordan Bayles
a07b37e4ec Improve performance for comment parsing (#1052)
* Improve performance for comment parsing

* Fix weird main.cpp issue

* Readd newline

* remove carriage return feed char

* Remove unnecessary checks
2019-10-17 10:43:25 -07:00
Hans Johnson
aebc7faa4f BUG: New CMake features used that break backward compatibility
We desire for jsoncpp to compile and be readily available
with older  versions of cmake.  The use of newer cmake
commands requires conditional statements so that older
strategies can be used with older versions of cmake.

Resolves: #1018
2019-10-17 11:11:34 -05:00
dota17
bdacfd7bc0 add a new method to insert a new value in an array at specific index. (#949)
* add a new method to insert a new value in an array at specific index.

* update: index > length, return false;

* fix clang-format
2019-10-16 14:33:53 +08:00
dota17
c5f66ab816 Test Framework Modify : Remove JSONTEST_REGISTER_FIXTURE (#1050)
* add JSONTEST_FIXTURE_V2 to automatically register

* fix clang-format

* revert singleton
2019-10-15 15:48:50 -07:00
Rosen Penev
bcad4e4de2 clang-tidy cleanups 2 (#1048)
* [clang-tidy] Add explicit to single argument constructor

Found with hicpp-explicit-conversions

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Fix mismatching declaration

Found with readability-inconsistent-declaration-parameter-name

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Replace {} with = default

Found with modernize-use-equals-default

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Remove redundant .c_Str

Found with readability-redundant-string-cstr

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Simplify boolean expressions

Found with readability-simplify-boolean-expr

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Use std::move

Found with modernize-pass-by-value

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Uppercase literal suffixes

Found with hicpp-uppercase-literal-suffix

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-10-15 15:27:23 +08:00
dota17
7329223f58 fix clang-format (#1049)
fix clang-format for #1039
2019-10-14 09:42:47 +08:00
Jordan Bayles
2e33c218cb Fix fuzzer off by one error (#1047)
* Fix fuzzer off by one error

Currently the fuzzer has an off by one error, as it passing a bad length
to the CharReader::parse method, resulting in a heap buffer overflow.

* Rebase master, rerun clang format
2019-10-11 15:08:42 -07:00
dota17
ddc0748c4f add testcases for writerTest [improve coverage] (#1039)
* add testcases for writerTest

* update StyledWriterTest, StyledStreamWriterTest and StreamWriterTest

* run clang-format

* add FastWriter Test

* Improve Coverage to 90+%
2019-10-11 14:39:09 -07:00
es1x
2ee3b1dbb1 Re-add JSONCPP_NORETURN (#1041)
Fixes Visual Studio 2013 compatibility.
2019-10-11 11:33:36 -07:00
Jordan Bayles
f34bf24bbd Issue #958: Travis CI should enforce clang-format standards (#1026)
* Issue #958: Travis CI should enfore clang-format standards

This patch adds clang format support to the travis bots.

* Update path

* Roll back to version 8 since 9 is in test

* Cleanup clang

* Revert "Delete JSONCPP_DEPRECATED, use [[deprecated]] instead. (#978)" (#1029)

This reverts commit b27c83f691.
2019-10-11 11:19:00 -07:00
Jacob Bundgaard
c4bc6da87d Fix dead link in CONTRIBUTING.md (#1044) 2019-10-10 10:22:25 +08:00
dota17
736409f1b5 fix clang-format error for ci (#1036)
* fix clang-format error for ci

* update
2019-10-01 12:54:07 -07:00
Griffin Downs
7e97345e26 Add vcpkg installation instructions (#1037) 2019-10-01 12:53:42 -07:00
Vincent
227c7cdfa5 Supplement the testcase for comparing object (#1032)
* supplement the testcase for comparing object

* update testcase

* add a new test scenarios in compareObject
2019-09-25 14:07:34 -07:00
Vincent
00c2c9f6e4 Supplement the testcase for comparing the Array and Null (#1031)
* supplement the testcase for comparing the Array and Null

* update testcase
2019-09-25 14:07:08 -07:00
Jordan Bayles
d448610770 Fixup Json::Value append methods, run clang format. (#1022) 2019-09-25 14:05:45 -07:00
Jordan Bayles
00b979f086 Issue #970: Rename features.h to json_features.h (#1024)
This patch fixes a build issue on CMake, presumably due to the new glibc
having a features.h include file. This patch renames our features.h file
to avoid a name collision.
2019-09-25 14:04:53 -07:00
Rosen Penev
ae4dc9aa62 clang-tidy fixes (#1033)
* [clang-tidy] Replace C typedef with C++ using

Found with modernize-use-using

Added to .clang-tidy file.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Remove redundant member init

Found with readability-redundant-member-init

Added to .clang-tidy

* [clang-tidy] Replace C casts with C++ ones

Found with google-readability-casting

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* [clang-tidy] Use default member init

Found with modernize-use-default-member-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-09-25 14:03:30 -07:00
dota17
e9ccbe0145 Create an example directory and add some code examples. (#944)
* update example directory

* modify some compile error.

* update with clang-format

* update

* update

* add_definitions("../include/json")

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Wed Jul 10 21:26:16 2019 +0800
#
# On branch code_example
# Your branch is up-to-date with 'origin/code_example'.
#
# Changes to be committed:
#	modified:   example/CMakeLists.txt
#

* change CMakeLists.txt

* update streamWrite.cpp

* update

* Update readFromStream.cpp

* fix typo
2019-09-17 13:30:00 -07:00
Rosen Penev
21e3d21243 pkgconfig: Fix for cross compilation (#1027)
exec_ and prefix must be overridden  in such a case.

Makes the .pc file more consistent with other projects.
2019-09-17 12:46:55 -07:00
Vincent
c97bd59ff2 add a testcase in ValueTest:CopyObject (#1028) 2019-09-17 12:46:29 -07:00
Jordan Bayles
81ae1d55f7 Just run clang format (#1025) 2019-09-16 12:37:14 -07:00
Jordan Bayles
18f790fbe7 Issue 1021: Fix clang 10 compilation (#1023)
This patch fixes an implicit long to double conversion, fixing
compilation on the as-of-yet unreleased clang v10.
2019-09-16 12:27:59 -07:00
m-gupta
3013ed48b3 jsoncpp: Define JSON_USE_INT64_DOUBLE_CONVERSION for clang as well. (#1002)
The current check to define JSON_USE_INT64_DOUBLE_CONVERSION
works for GCC but not clang.

Clang does define __GNUC__ but with a value 4 which misses
the check for >= 6.

This avoids the -Wimplicit-int-float-conversion warning
when jsoncpp is built with a recent version of clang.

Signed-off-by: Manoj Gupta <manojgupta@google.com>
2019-09-16 12:24:13 -07:00
dota17
2cb9a5803e reinforce readToken function and add simple tests (#1012) 2019-09-16 11:25:22 -07:00
Frank Richter
c5cb313ca0 Do not allow tokenError tokens after input if failIfExtra is set. (#1014)
Currently when failIfExtra is set and strictRoot is not set,
OurReader::parse() will accept trailing non-whitespace after the JSON value
as long as the first token is not a valid JSON token. This commit changes
this to disallow any non-whitespace after the JSON value.

This commit also suppresses the "Extra non-whitespace after JSON value."
error message if parsing was aborted after another error.
2019-09-16 10:41:50 -07:00
dota17
abcd3f7b1f Modify code comments in write.h (#987)
* modify code comments in write.h

* update
2019-09-16 10:40:35 -07:00
dota17
d622250c3e Check the comments array boundry. (#993)
* check the comments array boundry

* remove empty line
2019-09-16 10:40:09 -07:00
dota17
db61dba885 Improving Code Readability (#1004) 2019-09-16 10:35:48 -07:00
dota17
7ef0f9fa5b [Language Conformance] Use constexpr restriction in jsoncpp (#1005)
* use constexpr restriction in jsoncpp

* remove TODO comment
2019-09-16 10:33:47 -07:00
dota17
3550a0a939 add some testcases: WriteTest, StreamWriterTest (#1015) 2019-09-16 10:32:46 -07:00
Google AutoFuzz Team
21ab82916b Add dictionary for fuzzing (#1020) 2019-09-16 10:30:59 -07:00
dota17
fd940255ce change the returned value (#1003) 2019-08-26 12:47:54 -07:00
aliha
472adb60ee Fix a coupe of typos (#1007) 2019-08-26 12:37:05 -07:00
dota17
c92c87b47d Add some test cases in ValueTest (#1010)
* add some test cases in ValueTest

* add some test cases in ValueTest
2019-08-26 12:36:51 -07:00
Frank Richter
b941149a37 tests: Improve CharReaderFailIfExtraTest (#1011)
* There was a nonsensical change of 'failIfExtra' before calling strictMode():
  the latter resets the former.
  Dealt with by having one test with pure strictMode and one with strictMode
  but failIfExtra=false.
* The JSONTEST_ASSERT_STRING_EQUAL tests for the error strings swapped
  the 'expected' and 'actual' values.
2019-08-26 12:36:27 -07:00
dota17
2cf939e8c3 change Value::null to Value::nullSingleton() (#1000) 2019-08-13 22:42:10 -07:00
Jordan Bayles
7b28698c5c Cleanup versioning strategy relanding (#989) (#997)
* Cleanup versioning strategy

Currently, versioning is a mess. CMake and Meson have seperate build
version number storage locations, with no way of knowing you need to
have both. Plus, due to recent revisions the amalgamate script is broken
unless you build first, and may still be broken afterwards.

This PR fixes some issues with versioning, and adds comments clarifying
what has to be done when doing a release.

* Run clang format

* Update SOVERSION....
2019-08-13 22:41:43 -07:00
Jordan Bayles
0d27381acf Revert "Cleanup versioning strategy (#989)" (#996)
This reverts commit 12325b814f.
2019-07-31 11:26:48 -07:00
Jordan Bayles
12325b814f Cleanup versioning strategy (#989)
* Cleanup versioning strategy

Currently, versioning is a mess. CMake and Meson have seperate build
version number storage locations, with no way of knowing you need to
have both. Plus, due to recent revisions the amalgamate script is broken
unless you build first, and may still be broken afterwards.

This PR fixes some issues with versioning, and adds comments clarifying
what has to be done when doing a release.

* Run clang format

* Update SOVERSION....
2019-07-22 15:25:23 -07:00
dota17
b27c83f691 Delete JSONCPP_DEPRECATED, use [[deprecated]] instead. (#978)
* delete JSONCPP_DEPRECATED, use [[deprecated]]

* add pragma warning(disable:4996)

* add error C2416

* update

* update

* update
2019-07-17 13:35:33 -07:00
Billy Donahue
483eba84a7 Improve code comment formatting (Issue #985) 2019-07-17 13:04:53 -07:00
Jordan Bayles
b3507948e2 Fix definition check for GNUC 2019-07-17 13:03:23 -07:00
172 changed files with 3602 additions and 2235 deletions

View File

@@ -1,47 +1,4 @@
--- BasedOnStyle: LLVM
# BasedOnStyle: LLVM DerivePointerAlignment: false
AccessModifierOffset: -2 PointerAlignment: Left
ConstructorInitializerIndentWidth: 4
AlignEscapedNewlinesLeft: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakBeforeMultilineStrings: false
BreakBeforeBinaryOperators: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BinPackParameters: false
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: false
DerivePointerBinding: false
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 60
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerBindsToType: true
SpacesBeforeTrailingComments: 1
Cpp11BracedListStyle: true
Standard: Cpp11
IndentWidth: 2
TabWidth: 8
UseTab: Never
BreakBeforeBraces: Attach
IndentFunctionDeclarationAfterType: false
SpacesInParentheses: false
SpacesInAngles: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
ContinuationIndentWidth: 4
...

11
.clang-tidy Normal file
View File

@@ -0,0 +1,11 @@
---
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
FormatStyle: none
CheckOptions:
- key: modernize-use-using.IgnoreMacros
value: '0'
...

2
.gitignore vendored
View File

@@ -10,8 +10,6 @@
/libs/ /libs/
/doc/doxyfile /doc/doxyfile
/dist/ /dist/
#/version
#/include/json/version.h
# MSVC project files: # MSVC project files:
*.sln *.sln

View File

@@ -9,6 +9,7 @@ sudo: false
addons: addons:
homebrew: homebrew:
packages: packages:
- clang-format
- meson - meson
- ninja - ninja
update: false # do not update homebrew by default update: false # do not update homebrew by default
@@ -17,6 +18,7 @@ addons:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-xenial-8 - llvm-toolchain-xenial-8
packages: packages:
- clang-format-8
- clang-8 - clang-8
- valgrind - valgrind
matrix: matrix:
@@ -25,7 +27,7 @@ matrix:
include: include:
- name: Mac clang meson static release testing - name: Mac clang meson static release testing
os: osx os: osx
osx_image: xcode10.2 osx_image: xcode11
compiler: clang compiler: clang
env: env:
CXX="clang++" CXX="clang++"
@@ -60,6 +62,10 @@ matrix:
BUILD_TYPE=Debug BUILD_TYPE=Debug
LIB_TYPE=shared LIB_TYPE=shared
DESTDIR=/tmp/cmake_json_cpp DESTDIR=/tmp/cmake_json_cpp
before_install:
- pip install --user cpp-coveralls
script: ./.travis_scripts/cmake_builder.sh script: ./.travis_scripts/cmake_builder.sh
after_success:
- coveralls --include src/lib_json --include include
notifications: notifications:
email: false email: false

View File

@@ -63,9 +63,11 @@ meson --version
ninja --version ninja --version
_COMPILER_NAME=`basename ${CXX}` _COMPILER_NAME=`basename ${CXX}`
_BUILD_DIR_NAME="build-${BUILD_TYPE}_${LIB_TYPE}_${_COMPILER_NAME}" _BUILD_DIR_NAME="build-${BUILD_TYPE}_${LIB_TYPE}_${_COMPILER_NAME}"
meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . "${_BUILD_DIR_NAME}"
./.travis_scripts/run-clang-format.sh
meson --fatal-meson-warnings --werror --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . "${_BUILD_DIR_NAME}"
ninja -v -j 2 -C "${_BUILD_DIR_NAME}" ninja -v -j 2 -C "${_BUILD_DIR_NAME}"
#ninja -v -j 2 -C "${_BUILD_DIR_NAME}" test
cd "${_BUILD_DIR_NAME}" cd "${_BUILD_DIR_NAME}"
meson test --no-rebuild --print-errorlogs meson test --no-rebuild --print-errorlogs

View File

@@ -0,0 +1,356 @@
#!/usr/bin/env python
"""A wrapper script around clang-format, suitable for linting multiple files
and to use for continuous integration.
This is an alternative API for the clang-format command line.
It runs over multiple files and directories in parallel.
A diff output is produced and a sensible exit code is returned.
NOTE: pulled from https://github.com/Sarcasm/run-clang-format, which is
licensed under the MIT license.
"""
from __future__ import print_function, unicode_literals
import argparse
import codecs
import difflib
import fnmatch
import io
import multiprocessing
import os
import signal
import subprocess
import sys
import traceback
from functools import partial
try:
from subprocess import DEVNULL # py3k
except ImportError:
DEVNULL = open(os.devnull, "wb")
DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx'
class ExitStatus:
SUCCESS = 0
DIFF = 1
TROUBLE = 2
def list_files(files, recursive=False, extensions=None, exclude=None):
if extensions is None:
extensions = []
if exclude is None:
exclude = []
out = []
for file in files:
if recursive and os.path.isdir(file):
for dirpath, dnames, fnames in os.walk(file):
fpaths = [os.path.join(dirpath, fname) for fname in fnames]
for pattern in exclude:
# os.walk() supports trimming down the dnames list
# by modifying it in-place,
# to avoid unnecessary directory listings.
dnames[:] = [
x for x in dnames
if
not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)
]
fpaths = [
x for x in fpaths if not fnmatch.fnmatch(x, pattern)
]
for f in fpaths:
ext = os.path.splitext(f)[1][1:]
if ext in extensions:
out.append(f)
else:
out.append(file)
return out
def make_diff(file, original, reformatted):
return list(
difflib.unified_diff(
original,
reformatted,
fromfile='{}\t(original)'.format(file),
tofile='{}\t(reformatted)'.format(file),
n=3))
class DiffError(Exception):
def __init__(self, message, errs=None):
super(DiffError, self).__init__(message)
self.errs = errs or []
class UnexpectedError(Exception):
def __init__(self, message, exc=None):
super(UnexpectedError, self).__init__(message)
self.formatted_traceback = traceback.format_exc()
self.exc = exc
def run_clang_format_diff_wrapper(args, file):
try:
ret = run_clang_format_diff(args, file)
return ret
except DiffError:
raise
except Exception as e:
raise UnexpectedError('{}: {}: {}'.format(file, e.__class__.__name__,
e), e)
def run_clang_format_diff(args, file):
try:
with io.open(file, 'r', encoding='utf-8') as f:
original = f.readlines()
except IOError as exc:
raise DiffError(str(exc))
invocation = [args.clang_format_executable, file]
# Use of utf-8 to decode the process output.
#
# Hopefully, this is the correct thing to do.
#
# It's done due to the following assumptions (which may be incorrect):
# - clang-format will returns the bytes read from the files as-is,
# without conversion, and it is already assumed that the files use utf-8.
# - if the diagnostics were internationalized, they would use utf-8:
# > Adding Translations to Clang
# >
# > Not possible yet!
# > Diagnostic strings should be written in UTF-8,
# > the client can translate to the relevant code page if needed.
# > Each translation completely replaces the format string
# > for the diagnostic.
# > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
#
# It's not pretty, due to Python 2 & 3 compatibility.
encoding_py3 = {}
if sys.version_info[0] >= 3:
encoding_py3['encoding'] = 'utf-8'
try:
proc = subprocess.Popen(
invocation,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
**encoding_py3)
except OSError as exc:
raise DiffError(
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(invocation), exc
)
)
proc_stdout = proc.stdout
proc_stderr = proc.stderr
if sys.version_info[0] < 3:
# make the pipes compatible with Python 3,
# reading lines should output unicode
encoding = 'utf-8'
proc_stdout = codecs.getreader(encoding)(proc_stdout)
proc_stderr = codecs.getreader(encoding)(proc_stderr)
# hopefully the stderr pipe won't get full and block the process
outs = list(proc_stdout.readlines())
errs = list(proc_stderr.readlines())
proc.wait()
if proc.returncode:
raise DiffError(
"Command '{}' returned non-zero exit status {}".format(
subprocess.list2cmdline(invocation), proc.returncode
),
errs,
)
return make_diff(file, original, outs), errs
def bold_red(s):
return '\x1b[1m\x1b[31m' + s + '\x1b[0m'
def colorize(diff_lines):
def bold(s):
return '\x1b[1m' + s + '\x1b[0m'
def cyan(s):
return '\x1b[36m' + s + '\x1b[0m'
def green(s):
return '\x1b[32m' + s + '\x1b[0m'
def red(s):
return '\x1b[31m' + s + '\x1b[0m'
for line in diff_lines:
if line[:4] in ['--- ', '+++ ']:
yield bold(line)
elif line.startswith('@@ '):
yield cyan(line)
elif line.startswith('+'):
yield green(line)
elif line.startswith('-'):
yield red(line)
else:
yield line
def print_diff(diff_lines, use_color):
if use_color:
diff_lines = colorize(diff_lines)
if sys.version_info[0] < 3:
sys.stdout.writelines((l.encode('utf-8') for l in diff_lines))
else:
sys.stdout.writelines(diff_lines)
def print_trouble(prog, message, use_colors):
error_text = 'error:'
if use_colors:
error_text = bold_red(error_text)
print("{}: {} {}".format(prog, error_text, message), file=sys.stderr)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--clang-format-executable',
metavar='EXECUTABLE',
help='path to the clang-format executable',
default='clang-format')
parser.add_argument(
'--extensions',
help='comma separated list of file extensions (default: {})'.format(
DEFAULT_EXTENSIONS),
default=DEFAULT_EXTENSIONS)
parser.add_argument(
'-r',
'--recursive',
action='store_true',
help='run recursively over directories')
parser.add_argument('files', metavar='file', nargs='+')
parser.add_argument(
'-q',
'--quiet',
action='store_true')
parser.add_argument(
'-j',
metavar='N',
type=int,
default=0,
help='run N clang-format jobs in parallel'
' (default number of cpus + 1)')
parser.add_argument(
'--color',
default='auto',
choices=['auto', 'always', 'never'],
help='show colored diff (default: auto)')
parser.add_argument(
'-e',
'--exclude',
metavar='PATTERN',
action='append',
default=[],
help='exclude paths matching the given glob-like pattern(s)'
' from recursive search')
args = parser.parse_args()
# use default signal handling, like diff return SIGINT value on ^C
# https://bugs.python.org/issue14229#msg156446
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
signal.SIGPIPE
except AttributeError:
# compatibility, SIGPIPE does not exist on Windows
pass
else:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
colored_stdout = False
colored_stderr = False
if args.color == 'always':
colored_stdout = True
colored_stderr = True
elif args.color == 'auto':
colored_stdout = sys.stdout.isatty()
colored_stderr = sys.stderr.isatty()
version_invocation = [args.clang_format_executable, str("--version")]
try:
subprocess.check_call(version_invocation, stdout=DEVNULL)
except subprocess.CalledProcessError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
return ExitStatus.TROUBLE
except OSError as e:
print_trouble(
parser.prog,
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(version_invocation), e
),
use_colors=colored_stderr,
)
return ExitStatus.TROUBLE
retcode = ExitStatus.SUCCESS
files = list_files(
args.files,
recursive=args.recursive,
exclude=args.exclude,
extensions=args.extensions.split(','))
if not files:
return
njobs = args.j
if njobs == 0:
njobs = multiprocessing.cpu_count() + 1
njobs = min(len(files), njobs)
if njobs == 1:
# execute directly instead of in a pool,
# less overhead, simpler stacktraces
it = (run_clang_format_diff_wrapper(args, file) for file in files)
pool = None
else:
pool = multiprocessing.Pool(njobs)
it = pool.imap_unordered(
partial(run_clang_format_diff_wrapper, args), files)
while True:
try:
outs, errs = next(it)
except StopIteration:
break
except DiffError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
retcode = ExitStatus.TROUBLE
sys.stderr.writelines(e.errs)
except UnexpectedError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
sys.stderr.write(e.formatted_traceback)
retcode = ExitStatus.TROUBLE
# stop at the first unexpected error,
# something could be very wrong,
# don't process all files unnecessarily
if pool:
pool.terminate()
break
else:
sys.stderr.writelines(errs)
if outs == []:
continue
if not args.quiet:
print_diff(outs, use_color=colored_stdout)
if retcode == ExitStatus.SUCCESS:
retcode = ExitStatus.DIFF
return retcode
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
python $DIR/run-clang-format.py -r $DIR/../src/**/ $DIR/../include/**/

View File

@@ -21,6 +21,7 @@ Braden McDorman <bmcdorman@gmail.com>
Brandon Myers <bmyers1788@gmail.com> Brandon Myers <bmyers1788@gmail.com>
Brendan Drew <brendan.drew@daqri.com> Brendan Drew <brendan.drew@daqri.com>
chason <cxchao802@gmail.com> chason <cxchao802@gmail.com>
chenguoping <chenguopingdota@163.com>
Chris Gilling <cgilling@iparadigms.com> Chris Gilling <cgilling@iparadigms.com>
Christopher Dawes <christopher.dawes.1981@googlemail.com> Christopher Dawes <christopher.dawes.1981@googlemail.com>
Christopher Dunn <cdunn2001@gmail.com> Christopher Dunn <cdunn2001@gmail.com>
@@ -97,6 +98,7 @@ selaselah <selah@outlook.com>
Sergiy80 <sil2004@gmail.com> Sergiy80 <sil2004@gmail.com>
sergzub <sergzub@gmail.com> sergzub <sergzub@gmail.com>
Stefan Schweter <stefan@schweter.it> Stefan Schweter <stefan@schweter.it>
Stefano Fiorentino <stefano.fiore84@gmail.com>
Steffen Kieß <Steffen.Kiess@ipvs.uni-stuttgart.de> Steffen Kieß <Steffen.Kiess@ipvs.uni-stuttgart.de>
Steven Hahn <hahnse@ornl.gov> Steven Hahn <hahnse@ornl.gov>
Stuart Eichert <stuart@fivemicro.com> Stuart Eichert <stuart@fivemicro.com>

View File

@@ -37,37 +37,41 @@ foreach(pold "") # Currently Empty
endif() endif()
endforeach() endforeach()
# ==== Define language standard configurations requiring at least c++11 standard # Build the library with C++11 standard support, independent from other including
if(CMAKE_CXX_STANDARD EQUAL "98" ) # software which may use a different CXX_STANDARD or CMAKE_CXX_STANDARD.
message(FATAL_ERROR "CMAKE_CXX_STANDARD:STRING=98 is not supported.") set(CMAKE_CXX_STANDARD 11)
endif() set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
##### # Ensure that CMAKE_BUILD_TYPE has a value specified for single configuration generators.
## Set the default target properties if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
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)
endif()
# ====
# Ensures that CMAKE_BUILD_TYPE has a default value
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING set(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage.") "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage.")
endif() endif()
# ---------------------------------------------------------------------------
# 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
VERSION 1.9.0 # <major>[.<minor>[.<patch>[.<tweak>]]] # 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 JSONCPP_SOVERSION!!
VERSION 1.9.3 # <major>[.<minor>[.<patch>[.<tweak>]]]
LANGUAGES CXX) LANGUAGES CXX)
message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}") message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
set( JSONCPP_SOVERSION 21 ) set(JSONCPP_SOVERSION 24)
option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON) 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) option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
@@ -75,29 +79,24 @@ 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_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_PKGCONFIG_SUPPORT "Generate and install .pc files" ON)
option(JSONCPP_WITH_CMAKE_PACKAGE "Generate and install cmake package files" ON) option(JSONCPP_WITH_CMAKE_PACKAGE "Generate and install cmake package files" ON)
option(JSONCPP_WITH_EXAMPLE "Compile JsonCpp example" OFF)
option(BUILD_SHARED_LIBS "Build jsoncpp_lib as a shared library." OFF) 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()
# Adhere to GNU filesystem layout conventions # Adhere to GNU filesystem layout conventions
include(GNUInstallDirs) 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")
# File version.h is only regenerated on CMake configure step configure_file("${PROJECT_SOURCE_DIR}/version.in"
configure_file( "${PROJECT_SOURCE_DIR}/src/lib_json/version.h.in" "${PROJECT_BINARY_DIR}/version"
"${PROJECT_BINARY_DIR}/include/json/version.h" NEWLINE_STYLE UNIX)
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) if(MSVC)
# Only enabled in debug because some old versions of VS STL generate # Only enabled in debug because some old versions of VS STL generate
# warnings when compiled in release configuration. # warnings when compiled in release configuration.
@@ -111,7 +110,7 @@ macro(UseCompilationWarningAsError)
endmacro() endmacro()
# Include our configuration header # Include our configuration header
include_directories( ${jsoncpp_SOURCE_DIR}/include ) include_directories(${jsoncpp_SOURCE_DIR}/include)
if(MSVC) if(MSVC)
# Only enabled in debug because some old versions of VS STL generate # Only enabled in debug because some old versions of VS STL generate
@@ -128,7 +127,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# not yet ready for -Wsign-conversion # not yet ready for -Wsign-conversion
if(JSONCPP_WITH_STRICT_ISO) if(JSONCPP_WITH_STRICT_ISO)
add_compile_options(-pedantic) add_compile_options(-Wpedantic)
endif() endif()
if(JSONCPP_WITH_WARNING_AS_ERROR) if(JSONCPP_WITH_WARNING_AS_ERROR)
add_compile_options(-Werror=conversion) add_compile_options(-Werror=conversion)
@@ -138,18 +137,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion) add_compile_options(-Wall -Wconversion -Wshadow -Wextra -Werror=conversion)
if(JSONCPP_WITH_STRICT_ISO AND NOT JSONCPP_WITH_WARNING_AS_ERROR) if(JSONCPP_WITH_STRICT_ISO AND NOT JSONCPP_WITH_WARNING_AS_ERROR)
add_compile_options(-pedantic) add_compile_options(-Wpedantic)
endif() endif()
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) if(JSONCPP_WITH_WARNING_AS_ERROR)
UseCompilationWarningAsError() use_compilation_warning_as_error()
endif() endif()
if(JSONCPP_WITH_PKGCONFIG_SUPPORT) if(JSONCPP_WITH_PKGCONFIG_SUPPORT)
@@ -162,25 +155,29 @@ if(JSONCPP_WITH_PKGCONFIG_SUPPORT)
endif() endif()
if(JSONCPP_WITH_CMAKE_PACKAGE) if(JSONCPP_WITH_CMAKE_PACKAGE)
include (CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
install(EXPORT jsoncpp install(EXPORT jsoncpp
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp
FILE jsoncppConfig.cmake) FILE jsoncppConfig.cmake)
write_basic_package_version_file ("${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake" write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion) COMPATIBILITY SameMajorVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jsoncppConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp) DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/jsoncpp)
endif() endif()
if(JSONCPP_WITH_TESTS) if(JSONCPP_WITH_TESTS)
enable_testing() enable_testing()
include(CTest) include(CTest)
endif() endif()
# Build the different applications # Build the different applications
add_subdirectory( src ) add_subdirectory(src)
#install the includes #install the includes
add_subdirectory( include ) add_subdirectory(include)
#install the example
if(JSONCPP_WITH_EXAMPLE)
add_subdirectory(example)
endif()

View File

@@ -11,7 +11,7 @@ An example of a common Meson/Ninja environment is described next.
Thanks to David Seifert (@SoapGentoo), we (the maintainers) now use Thanks to David Seifert (@SoapGentoo), we (the maintainers) now use
[meson](http://mesonbuild.com/) and [ninja](https://ninja-build.org/) to build [meson](http://mesonbuild.com/) and [ninja](https://ninja-build.org/) to build
for debugging, as well as for continuous integration (see for debugging, as well as for continuous integration (see
[`./travis_scripts/meson_builder.sh`](./travis_scripts/meson_builder.sh) ). Other systems may work, but minor [`./.travis_scripts/meson_builder.sh`](./.travis_scripts/meson_builder.sh) ). Other systems may work, but minor
things like version strings might break. things like version strings might break.
First, install both meson (which requires Python3) and ninja. First, install both meson (which requires Python3) and ninja.
@@ -26,10 +26,14 @@ Then,
LIB_TYPE=shared LIB_TYPE=shared
#LIB_TYPE=static #LIB_TYPE=static
meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . build-${LIB_TYPE} meson --buildtype ${BUILD_TYPE} --default-library ${LIB_TYPE} . build-${LIB_TYPE}
#ninja -v -C build-${LIB_TYPE} test # This stopped working on my Mac.
ninja -v -C 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 sudo ninja install
## Building and testing with other build systems ## Building and testing with other build systems

View File

@@ -1,6 +1,10 @@
# JsonCpp # JsonCpp
[![badge](https://img.shields.io/badge/conan.io-jsoncpp%2F1.8.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](https://bintray.com/theirix/conan-repo/jsoncpp%3Atheirix) [![badge](https://img.shields.io/badge/conan.io-jsoncpp%2F1.8.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](https://bintray.com/theirix/conan-repo/jsoncpp%3Atheirix)
[![badge](https://img.shields.io/badge/license-MIT-blue)](https://github.com/open-source-parsers/jsoncpp/blob/master/LICENSE)
[![badge](https://img.shields.io/badge/document-doxygen-brightgreen)](http://open-source-parsers.github.io/jsoncpp-docs/doxygen/index.html)
[![Coverage Status](https://coveralls.io/repos/github/open-source-parsers/jsoncpp/badge.svg?branch=master)](https://coveralls.io/github/open-source-parsers/jsoncpp?branch=master)
[JSON][json-org] is a lightweight data-interchange format. It can represent [JSON][json-org] is a lightweight data-interchange format. It can represent
numbers, strings, ordered sequences of values, and collections of name/value numbers, strings, ordered sequences of values, and collections of name/value
@@ -26,19 +30,36 @@ format to store user input files.
* `1.y.z` is built with C++11. * `1.y.z` is built with C++11.
* `0.y.z` can be used with older compilers. * `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. * 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 ## Using JsonCpp in your project
### The vcpkg dependency manager
You can download and install JsonCpp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./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.
### Amalgamated source ### Amalgamated source
https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated-(Possibly-outdated)
### The Meson Build System ### The Meson Build System
If you are using the [Meson Build System](http://mesonbuild.com), then you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/jsoncpp), or simply use `meson wrap install jsoncpp`. If you are using the [Meson Build System](http://mesonbuild.com), then you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/jsoncpp), or simply use `meson wrap install jsoncpp`.
### Other ways ### Other ways
If you have trouble, see the Wiki, or post a question as an Issue. If you have trouble, see the [Wiki](https://github.com/open-source-parsers/jsoncpp/wiki), or post a question as an Issue.
## License ## License

40
amalgamate.py Normal file → Executable file
View File

@@ -1,3 +1,5 @@
#!/usr/bin/env python
"""Amalgamate json-cpp library sources into a single source and header file. """Amalgamate json-cpp library sources into a single source and header file.
Works with python2.6+ and python3.4+. Works with python2.6+ and python3.4+.
@@ -9,6 +11,9 @@ import os
import os.path import os.path
import sys import sys
INCLUDE_PATH = "include/json"
SRC_PATH = "src/lib_json"
class AmalgamationFile: class AmalgamationFile:
def __init__(self, top_dir): def __init__(self, top_dir):
self.top_dir = top_dir self.top_dir = top_dir
@@ -66,15 +71,15 @@ def amalgamate_source(source_top_dir=None,
header.add_text("/// If defined, indicates that the source file is amalgamated") header.add_text("/// If defined, indicates that the source file is amalgamated")
header.add_text("/// to prevent private header inclusion.") header.add_text("/// to prevent private header inclusion.")
header.add_text("#define JSON_IS_AMALGAMATION") header.add_text("#define JSON_IS_AMALGAMATION")
header.add_file("include/json/version.h") header.add_file(os.path.join(INCLUDE_PATH, "version.h"))
header.add_file("include/json/allocator.h") header.add_file(os.path.join(INCLUDE_PATH, "allocator.h"))
header.add_file("include/json/config.h") header.add_file(os.path.join(INCLUDE_PATH, "config.h"))
header.add_file("include/json/forwards.h") header.add_file(os.path.join(INCLUDE_PATH, "forwards.h"))
header.add_file("include/json/features.h") header.add_file(os.path.join(INCLUDE_PATH, "json_features.h"))
header.add_file("include/json/value.h") header.add_file(os.path.join(INCLUDE_PATH, "value.h"))
header.add_file("include/json/reader.h") header.add_file(os.path.join(INCLUDE_PATH, "reader.h"))
header.add_file("include/json/writer.h") header.add_file(os.path.join(INCLUDE_PATH, "writer.h"))
header.add_file("include/json/assertions.h") header.add_file(os.path.join(INCLUDE_PATH, "assertions.h"))
header.add_text("#endif //ifndef JSON_AMALGAMATED_H_INCLUDED") header.add_text("#endif //ifndef JSON_AMALGAMATED_H_INCLUDED")
target_header_path = os.path.join(os.path.dirname(target_source_path), header_include_path) target_header_path = os.path.join(os.path.dirname(target_source_path), header_include_path)
@@ -94,8 +99,10 @@ def amalgamate_source(source_top_dir=None,
header.add_text("/// If defined, indicates that the source file is amalgamated") header.add_text("/// If defined, indicates that the source file is amalgamated")
header.add_text("/// to prevent private header inclusion.") header.add_text("/// to prevent private header inclusion.")
header.add_text("#define JSON_IS_AMALGAMATION") header.add_text("#define JSON_IS_AMALGAMATION")
header.add_file("include/json/config.h") header.add_file(os.path.join(INCLUDE_PATH, "version.h"))
header.add_file("include/json/forwards.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") header.add_text("#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED")
target_forward_header_path = os.path.join(os.path.dirname(target_source_path), target_forward_header_path = os.path.join(os.path.dirname(target_source_path),
@@ -116,12 +123,11 @@ def amalgamate_source(source_top_dir=None,
#endif #endif
""") """)
source.add_text("") source.add_text("")
lib_json = "src/lib_json" source.add_file(os.path.join(SRC_PATH, "json_tool.h"))
source.add_file(os.path.join(lib_json, "json_tool.h")) source.add_file(os.path.join(SRC_PATH, "json_reader.cpp"))
source.add_file(os.path.join(lib_json, "json_reader.cpp")) source.add_file(os.path.join(SRC_PATH, "json_valueiterator.inl"))
source.add_file(os.path.join(lib_json, "json_valueiterator.inl")) source.add_file(os.path.join(SRC_PATH, "json_value.cpp"))
source.add_file(os.path.join(lib_json, "json_value.cpp")) source.add_file(os.path.join(SRC_PATH, "json_writer.cpp"))
source.add_file(os.path.join(lib_json, "json_writer.cpp"))
print("Writing amalgamated source to %r" % target_source_path) print("Writing amalgamated source to %r" % target_source_path)
source.write_to(target_source_path) source.write_to(target_source_path)

27
example/CMakeLists.txt Normal file
View File

@@ -0,0 +1,27 @@
#vim: et ts =4 sts = 4 sw = 4 tw = 0
set(EXAMPLES
readFromString
readFromStream
stringWrite
streamWrite
)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI)
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)
endforeach()
add_custom_target(examples ALL DEPENDS ${EXAMPLES})

13
example/README.md Normal file
View File

@@ -0,0 +1,13 @@
***NOTE***
If you get linker errors about undefined references to symbols that involve types in the `std::__cxx11` namespace or the tag
`[abi:cxx11]` then it probably indicates that you are trying to link together object files that were compiled with different
values for the _GLIBCXX_USE_CXX11_ABI marco. This commonly happens when linking to a third-party library that was compiled with
an older version of GCC. If the third-party library cannot be rebuilt with the new ABI, then you need to recompile your code with
the old ABI,just like:
**g++ stringWrite.cpp -ljsoncpp -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0 -o stringWrite**
Not all of uses of the new ABI will cause changes in symbol names, for example a class with a `std::string` member variable will
have the same mangled name whether compiled with the older or new ABI. In order to detect such problems, the new types and functions
are annotated with the abi_tag attribute, allowing the compiler to warn about potential ABI incompatibilities in code using them.
Those warnings can be enabled with the `-Wabi-tag` option.

View File

@@ -0,0 +1,3 @@
{
1: "value"
}

View File

@@ -0,0 +1,30 @@
#include "json/json.h"
#include <fstream>
#include <iostream>
/** \brief Parse from stream, collect comments and capture error info.
* Example Usage:
* $g++ readFromStream.cpp -ljsoncpp -std=c++11 -o readFromStream
* $./readFromStream
* // comment head
* {
* // comment before
* "key" : "value"
* }
* // comment after
* // comment tail
*/
int main(int argc, char* argv[]) {
Json::Value root;
std::ifstream ifs;
ifs.open(argv[1]);
Json::CharReaderBuilder builder;
builder["collectComments"] = true;
JSONCPP_STRING errs;
if (!parseFromStream(builder, ifs, &root, &errs)) {
std::cout << errs << std::endl;
return EXIT_FAILURE;
}
std::cout << root << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,6 @@
// comment head
{
// comment before
"key" : "value"
// comment after
}// comment tail

View File

@@ -0,0 +1,37 @@
#include "json/json.h"
#include <iostream>
/**
* \brief Parse a raw string into Value object using the CharReaderBuilder
* class, or the legacy Reader class.
* Example Usage:
* $g++ readFromString.cpp -ljsoncpp -std=c++11 -o readFromString
* $./readFromString
* colin
* 20
*/
int main() {
const std::string rawJson = R"({"Age": 20, "Name": "colin"})";
const auto rawJsonLength = static_cast<int>(rawJson.length());
constexpr bool shouldUseOldWay = false;
JSONCPP_STRING err;
Json::Value root;
if (shouldUseOldWay) {
Json::Reader reader;
reader.parse(rawJson, root);
} else {
Json::CharReaderBuilder builder;
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
if (!reader->parse(rawJson.c_str(), rawJson.c_str() + rawJsonLength, &root,
&err)) {
std::cout << "error" << std::endl;
return EXIT_FAILURE;
}
}
const std::string name = root["Name"].asString();
const int age = root["Age"].asInt();
std::cout << name << std::endl;
std::cout << age << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,22 @@
#include "json/json.h"
#include <iostream>
/** \brief Write the Value object to a stream.
* Example Usage:
* $g++ streamWrite.cpp -ljsoncpp -std=c++11 -o streamWrite
* $./streamWrite
* {
* "Age" : 20,
* "Name" : "robin"
* }
*/
int main() {
Json::Value root;
Json::StreamWriterBuilder builder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
root["Name"] = "robin";
root["Age"] = 20;
writer->write(root, &std::cout);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,33 @@
#include "json/json.h"
#include <iostream>
/** \brief Write a Value object to a string.
* Example Usage:
* $g++ stringWrite.cpp -ljsoncpp -std=c++11 -o stringWrite
* $./stringWrite
* {
* "action" : "run",
* "data" :
* {
* "number" : 1
* }
* }
*/
int main() {
Json::Value root;
Json::Value data;
constexpr bool shouldUseOldWay = false;
root["action"] = "run";
data["number"] = 1;
root["data"] = data;
if (shouldUseOldWay) {
Json::FastWriter writer;
const std::string json_file = writer.write(root);
std::cout << json_file << std::endl;
} else {
Json::StreamWriterBuilder builder;
const std::string json_file = Json::writeString(builder, root);
std::cout << json_file << std::endl;
}
return EXIT_SUCCESS;
}

View File

@@ -1,6 +1,5 @@
file(GLOB INCLUDE_FILES "json/*.h") file(GLOB INCLUDE_FILES "json/*.h")
install(FILES install(FILES
${INCLUDE_FILES} ${INCLUDE_FILES}
${PROJECT_BINARY_DIR}/include/json/version.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/json) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/json)

View File

@@ -3,8 +3,8 @@
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED #ifndef JSON_ALLOCATOR_H_INCLUDED
#define CPPTL_JSON_ALLOCATOR_H_INCLUDED #define JSON_ALLOCATOR_H_INCLUDED
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@@ -86,4 +86,4 @@ bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
#pragma pack(pop) #pragma pack(pop)
#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED #endif // JSON_ALLOCATOR_H_INCLUDED

View File

@@ -3,8 +3,8 @@
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED #ifndef JSON_ASSERTIONS_H_INCLUDED
#define CPPTL_JSON_ASSERTIONS_H_INCLUDED #define JSON_ASSERTIONS_H_INCLUDED
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
@@ -21,19 +21,19 @@
// @todo <= add detail about condition in exception // @todo <= add detail about condition in exception
#define JSON_ASSERT(condition) \ #define JSON_ASSERT(condition) \
{ \ do { \
if (!(condition)) { \ if (!(condition)) { \
Json::throwLogicError("assert json failed"); \ Json::throwLogicError("assert json failed"); \
} \ } \
} } while (0)
#define JSON_FAIL_MESSAGE(message) \ #define JSON_FAIL_MESSAGE(message) \
{ \ do { \
OStringStream oss; \ OStringStream oss; \
oss << message; \ oss << message; \
Json::throwLogicError(oss.str()); \ Json::throwLogicError(oss.str()); \
abort(); \ abort(); \
} } while (0)
#else // JSON_USE_EXCEPTION #else // JSON_USE_EXCEPTION
@@ -52,8 +52,10 @@
#endif #endif
#define JSON_ASSERT_MESSAGE(condition, message) \ #define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) { \ do { \
JSON_FAIL_MESSAGE(message); \ if (!(condition)) { \
} JSON_FAIL_MESSAGE(message); \
} \
} while (0)
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED #endif // JSON_ASSERTIONS_H_INCLUDED

View File

@@ -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

View File

@@ -14,16 +14,6 @@
#include <string> #include <string>
#include <type_traits> #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 // If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions. // assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION #ifndef JSON_USE_EXCEPTION
@@ -40,28 +30,22 @@
/// Remarks: it is automatically defined in the generated amalgamated header. /// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION // #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL // Export macros for DLL visibility
#include <cpptl/config.h> #if defined(JSON_DLL_BUILD)
#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)
#if defined(_MSC_VER) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport) #define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__) #elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default"))) #define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER) #endif // if defined(_MSC_VER)
#elif defined(JSON_DLL) #elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport) #define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER) #endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL #endif // ifdef JSON_DLL_BUILD
#if !defined(JSON_API) #if !defined(JSON_API)
#define JSON_API #define JSON_API
#endif #endif
@@ -74,8 +58,8 @@
#if defined(_MSC_VER) && _MSC_VER < 1900 #if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at // As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 // https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...); const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf #define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else #else
#define jsoncpp_snprintf std::snprintf #define jsoncpp_snprintf std::snprintf
@@ -90,25 +74,11 @@ msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
// C++11 should be used directly in JSONCPP. // C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override #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__ #ifdef __clang__
#if __has_extension(attribute_deprecated_with_message) #if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) #define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif #endif
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) #elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) #define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
@@ -123,7 +93,7 @@ msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
#define JSONCPP_DEPRECATED(message) #define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED) #endif // if !defined(JSONCPP_DEPRECATED)
#if __GNUC__ >= 6 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1 #define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif #endif
@@ -135,37 +105,37 @@ msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json { namespace Json {
typedef int Int; using Int = int;
typedef unsigned int UInt; using UInt = unsigned int;
#if defined(JSON_NO_INT64) #if defined(JSON_NO_INT64)
typedef int LargestInt; using LargestInt = int;
typedef unsigned int LargestUInt; using LargestUInt = unsigned int;
#undef JSON_HAS_INT64 #undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64) #else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported // For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio #if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64; using Int64 = __int64;
typedef unsigned __int64 UInt64; using UInt64 = unsigned __int64;
#else // if defined(_MSC_VER) // Other platforms, use long long #else // if defined(_MSC_VER) // Other platforms, use long long
typedef int64_t Int64; using Int64 = int64_t;
typedef uint64_t UInt64; using UInt64 = uint64_t;
#endif // if defined(_MSC_VER) #endif // if defined(_MSC_VER)
typedef Int64 LargestInt; using LargestInt = Int64;
typedef UInt64 LargestUInt; using LargestUInt = UInt64;
#define JSON_HAS_INT64 #define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64) #endif // if defined(JSON_NO_INT64)
template <typename T> template <typename T>
using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY, using Allocator =
SecureAllocator<T>, typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type; std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream = std::basic_istringstream<String::value_type, using IStringStream =
String::traits_type, std::basic_istringstream<String::value_type, String::traits_type,
String::allocator_type>; String::allocator_type>;
using OStringStream = std::basic_ostringstream<String::value_type, using OStringStream =
String::traits_type, std::basic_ostringstream<String::value_type, String::traits_type,
String::allocator_type>; String::allocator_type>;
using IStream = std::istream; using IStream = std::istream;
using OStream = std::ostream; using OStream = std::ostream;
} // namespace Json } // namespace Json

View File

@@ -25,11 +25,11 @@ class Reader;
class CharReader; class CharReader;
class CharReaderBuilder; class CharReaderBuilder;
// features.h // json_features.h
class Features; class Features;
// value.h // value.h
typedef unsigned int ArrayIndex; using ArrayIndex = unsigned int;
class StaticString; class StaticString;
class Path; class Path;
class PathArgument; class PathArgument;

View File

@@ -6,8 +6,8 @@
#ifndef JSON_JSON_H_INCLUDED #ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED #define JSON_JSON_H_INCLUDED
#include "autolink.h" #include "config.h"
#include "features.h" #include "json_features.h"
#include "reader.h" #include "reader.h"
#include "value.h" #include "value.h"
#include "writer.h" #include "writer.h"

View File

@@ -3,8 +3,8 @@
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED #ifndef JSON_FEATURES_H_INCLUDED
#define CPPTL_JSON_FEATURES_H_INCLUDED #define JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION) #if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h" #include "forwards.h"
@@ -58,4 +58,4 @@ public:
#pragma pack(pop) #pragma pack(pop)
#endif // CPPTL_JSON_FEATURES_H_INCLUDED #endif // JSON_FEATURES_H_INCLUDED

View File

@@ -3,11 +3,11 @@
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_READER_H_INCLUDED #ifndef JSON_READER_H_INCLUDED
#define CPPTL_JSON_READER_H_INCLUDED #define JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION) #if !defined(JSON_IS_AMALGAMATION)
#include "features.h" #include "json_features.h"
#include "value.h" #include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque> #include <deque>
@@ -28,20 +28,21 @@
namespace Json { namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
*Value. * Value.
* *
* \deprecated Use CharReader and CharReaderBuilder. * \deprecated Use CharReader and CharReaderBuilder.
*/ */
class JSON_API Reader {
class JSONCPP_DEPRECATED(
"Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
public: public:
typedef char Char; using Char = char;
typedef const Char* Location; using Location = const Char*;
/** \brief An error tagged with where in the JSON text it was encountered. /** \brief An error tagged with where in the JSON text it was encountered.
* *
* The offsets give the [start, limit) range of bytes within the text. Note * The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints. * that this is bytes, not codepoints.
*
*/ */
struct StructuredError { struct StructuredError {
ptrdiff_t offset_start; ptrdiff_t offset_start;
@@ -49,56 +50,50 @@ public:
String message; String message;
}; };
/** \brief Constructs a Reader allowing all features /** \brief Constructs a Reader allowing all features for parsing.
* for parsing.
*/ */
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader(); Reader();
/** \brief Constructs a Reader allowing the specified feature set /** \brief Constructs a Reader allowing the specified feature set for parsing.
* for parsing.
*/ */
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader(const Features& features); Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. * document.
* \param document UTF-8 encoded string containing the document to read. *
* \param root [out] Contains the root value of the document if it was * \param document UTF-8 encoded string containing the document
* successfully parsed. * to read.
* \param collectComments \c true to collect comment and allow writing them * \param[out] root Contains the root value of the document if it
* back during * was successfully parsed.
* serialization, \c false to discard comments. * \param collectComments \c true to collect comment and allow writing
* This parameter is ignored if * them back during serialization, \c false to
* Features::allowComments_ * discard comments. This parameter is ignored
* is \c false. * if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an * \return \c true if the document was successfully parsed, \c false if an
* error occurred. * error occurred.
*/ */
bool bool parse(const std::string& document, Value& root,
parse(const std::string& document, Value& root, bool collectComments = true); bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
document. * document.
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the *
document to read. * \param beginDoc Pointer on the beginning of the UTF-8 encoded
* \param endDoc Pointer on the end of the UTF-8 encoded string of the * string of the document to read.
document to read. * \param endDoc Pointer on the end of the UTF-8 encoded string
* Must be >= beginDoc. * of the document to read. Must be >= beginDoc.
* \param root [out] Contains the root value of the document if it was * \param[out] root Contains the root value of the document if it
* successfully parsed. * was successfully parsed.
* \param collectComments \c true to collect comment and allow writing them * \param collectComments \c true to collect comment and allow writing
back during * them back during serialization, \c false to
* serialization, \c false to discard comments. * discard comments. This parameter is ignored
* This parameter is ignored if * if Features::allowComments_ is \c false.
Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an * \return \c true if the document was successfully parsed, \c false if an
error occurred. * error occurred.
*/ */
bool parse(const char* beginDoc, bool parse(const char* beginDoc, const char* endDoc, Value& root,
const char* endDoc,
Value& root,
bool collectComments = true); bool collectComments = true);
/// \brief Parse from input stream. /// \brief Parse from input stream.
@@ -107,11 +102,10 @@ public:
/** \brief Returns a user friendly string that list errors in the parsed /** \brief Returns a user friendly string that list errors in the parsed
* document. * document.
* \return Formatted error message with the list of errors with their location *
* in * \return Formatted error message with the list of errors with their
* the parsed document. An empty string is returned if no error * location in the parsed document. An empty string is returned if no error
* occurred * occurred during parsing.
* during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix). * \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/ */
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
@@ -119,43 +113,45 @@ public:
/** \brief Returns a user friendly string that list errors in the parsed /** \brief Returns a user friendly string that list errors in the parsed
* document. * document.
* \return Formatted error message with the list of errors with their location *
* in * \return Formatted error message with the list of errors with their
* the parsed document. An empty string is returned if no error * location in the parsed document. An empty string is returned if no error
* occurred * occurred during parsing.
* during parsing.
*/ */
String getFormattedErrorMessages() const; String getFormattedErrorMessages() const;
/** \brief Returns a vector of structured erros encounted while parsing. /** \brief Returns a vector of structured errors encountered while parsing.
*
* \return A (possibly empty) vector of StructuredError objects. Currently * \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate * only one error can be returned, but the caller should tolerate multiple
* multiple * errors. This can occur if the parser recovers from a non-fatal parse
* errors. This can occur if the parser recovers from a non-fatal * error and then encounters additional errors.
* parse error and then encounters additional errors.
*/ */
std::vector<StructuredError> getStructuredErrors() const; std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message. /** \brief Add a semantic error message.
* \param value JSON Value location associated with the error *
* \param value JSON Value location associated with the error
* \param message The error message. * \param message The error message.
* \return \c true if the error was successfully added, \c false if the * \return \c true if the error was successfully added, \c false if the Value
* Value offset exceeds the document size. * offset exceeds the document size.
*/ */
bool pushError(const Value& value, const String& message); bool pushError(const Value& value, const String& message);
/** \brief Add a semantic error message with extra context. /** \brief Add a semantic error message with extra context.
* \param value JSON Value location associated with the error *
* \param value JSON Value location associated with the error
* \param message The error message. * \param message The error message.
* \param extra Additional JSON Value location to contextualize the error * \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either * \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size. * Value offset exceeds the document size.
*/ */
bool pushError(const Value& value, const String& message, const Value& extra); bool pushError(const Value& value, const String& message, const Value& extra);
/** \brief Return whether there are any errors. /** \brief Return whether there are any errors.
* \return \c true if there are no errors to report \c false if *
* errors have occurred. * \return \c true if there are no errors to report \c false if errors have
* occurred.
*/ */
bool good() const; bool good() const;
@@ -191,11 +187,11 @@ private:
Location extra_; Location extra_;
}; };
typedef std::deque<ErrorInfo> Errors; using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token); bool readToken(Token& token);
void skipSpaces(); void skipSpaces();
bool match(Location pattern, int patternLength); bool match(const Char* pattern, int patternLength);
bool readComment(); bool readComment();
bool readCStyleComment(); bool readCStyleComment();
bool readCppStyleComment(); bool readCppStyleComment();
@@ -210,24 +206,19 @@ private:
bool decodeString(Token& token, String& decoded); bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token); bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded); bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token, bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
Location& current,
Location end,
unsigned int& unicode); unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token, bool decodeUnicodeEscapeSequence(Token& token, Location& current,
Location& current, Location end, unsigned int& unicode);
Location end,
unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr); bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken); bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const String& message, bool addErrorAndRecover(const String& message, Token& token,
Token& token,
TokenType skipUntilToken); TokenType skipUntilToken);
void skipUntilSpace(); void skipUntilSpace();
Value& currentValue(); Value& currentValue();
Char getNextChar(); Char getNextChar();
void void getLocationLineAndColumn(Location location, int& line,
getLocationLineAndColumn(Location location, int& line, int& column) const; int& column) const;
String getLocationLineAndColumn(Location location) const; String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement); void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token); void skipCommentTokens(Token& token);
@@ -235,7 +226,7 @@ private:
static bool containsNewLine(Location begin, Location end); static bool containsNewLine(Location begin, Location end);
static String normalizeEOL(Location begin, Location end); static String normalizeEOL(Location begin, Location end);
typedef std::stack<Value*> Nodes; using Nodes = std::stack<Value*>;
Nodes nodes_; Nodes nodes_;
Errors errors_; Errors errors_;
String document_; String document_;
@@ -255,26 +246,22 @@ class JSON_API CharReader {
public: public:
virtual ~CharReader() = default; virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
document. * document. The document must be a UTF-8 encoded string containing the
* The document must be a UTF-8 encoded string containing the document to * document to read.
read.
* *
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the * \param beginDoc Pointer on the beginning of the UTF-8 encoded string
document to read. * of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the * \param endDoc Pointer on the end of the UTF-8 encoded string of the
document to read. * document to read. Must be >= beginDoc.
* Must be >= beginDoc. * \param[out] root Contains the root value of the document if it was
* \param root [out] Contains the root value of the document if it was * successfully parsed.
* successfully parsed. * \param[out] errs Formatted error messages (if not NULL) a user
* \param errs [out] Formatted error messages (if not NULL) * friendly string that lists errors in the parsed
* a user friendly string that lists errors in the parsed * document.
* document.
* \return \c true if the document was successfully parsed, \c false if an * \return \c true if the document was successfully parsed, \c false if an
error occurred. * error occurred.
*/ */
virtual bool parse(char const* beginDoc, virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
char const* endDoc,
Value* root,
String* errs) = 0; String* errs) = 0;
class JSON_API Factory { class JSON_API Factory {
@@ -288,59 +275,60 @@ public:
}; // CharReader }; // CharReader
/** \brief Build a CharReader implementation. /** \brief Build a CharReader implementation.
*
Usage: * Usage:
\code * \code
using namespace Json; * using namespace Json;
CharReaderBuilder builder; * CharReaderBuilder builder;
builder["collectComments"] = false; * builder["collectComments"] = false;
Value value; * Value value;
String errs; * String errs;
bool ok = parseFromStream(builder, std::cin, &value, &errs); * bool ok = parseFromStream(builder, std::cin, &value, &errs);
\endcode * \endcode
*/ */
class JSON_API CharReaderBuilder : public CharReader::Factory { class JSON_API CharReaderBuilder : public CharReader::Factory {
public: public:
// Note: We use a Json::Value so that we can add data-members to this class // Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump. // without a major version bump.
/** Configuration of this builder. /** Configuration of this builder.
These are case-sensitive. * These are case-sensitive.
Available settings (case-sensitive): * Available settings (case-sensitive):
- `"collectComments": false or true` * - `"collectComments": false or true`
- true to collect comment and allow writing them * - true to collect comment and allow writing them back during
back during serialization, false to discard comments. * serialization, false to discard comments. This parameter is ignored
This parameter is ignored if allowComments is false. * if allowComments is false.
- `"allowComments": false or true` * - `"allowComments": false or true`
- true if comments are allowed. * - true if comments are allowed.
- `"strictRoot": false or true` * - `"allowTrailingCommas": false or true`
- true if root must be either an array or an object value * - true if trailing commas in objects and arrays are allowed.
- `"allowDroppedNullPlaceholders": false or true` * - `"strictRoot": false or true`
- true if dropped null placeholders are allowed. (See * - true if root must be either an array or an object value
StreamWriterBuilder.) * - `"allowDroppedNullPlaceholders": false or true`
- `"allowNumericKeys": false or true` * - true if dropped null placeholders are allowed. (See
- true if numeric object keys are allowed. * StreamWriterBuilder.)
- `"allowSingleQuotes": false or true` * - `"allowNumericKeys": false or true`
- true if '' are allowed for strings (both keys and values) * - true if numeric object keys are allowed.
- `"stackLimit": integer` * - `"allowSingleQuotes": false or true`
- Exceeding stackLimit (recursive depth of `readValue()`) will * - true if '' are allowed for strings (both keys and values)
cause an exception. * - `"stackLimit": integer`
- This is a security issue (seg-faults caused by deeply nested JSON), * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
so the default is low. * exception.
- `"failIfExtra": false or true` * - This is a security issue (seg-faults caused by deeply nested JSON), so
- If true, `parse()` returns false when extra non-whitespace trails * the default is low.
the JSON value in the input string. * - `"failIfExtra": false or true`
- `"rejectDupKeys": false or true` * - If true, `parse()` returns false when extra non-whitespace trails the
- If true, `parse()` returns false when a key is duplicated within an * JSON value in the input string.
object. * - `"rejectDupKeys": false or true`
- `"allowSpecialFloats": false or true` * - If true, `parse()` returns false when a key is duplicated within an
- If true, special float values (NaNs and infinities) are allowed * object.
and their values are lossfree restorable. * - `"allowSpecialFloats": false or true`
* - If true, special float values (NaNs and infinities) are allowed and
You can examine 'settings_` yourself * their values are lossfree restorable.
to see the defaults. You can also write and read them just like any *
JSON Value. * You can examine 'settings_` yourself to see the defaults. You can also
\sa setDefaults() * write and read them just like any JSON Value.
*/ * \sa setDefaults()
*/
Json::Value settings_; Json::Value settings_;
CharReaderBuilder(); CharReaderBuilder();
@@ -375,35 +363,33 @@ public:
* Someday we might have a real StreamReader, but for now this * Someday we might have a real StreamReader, but for now this
* is convenient. * is convenient.
*/ */
bool JSON_API parseFromStream(CharReader::Factory const&, bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
IStream&,
Value* root,
String* errs); String* errs);
/** \brief Read from 'sin' into 'root'. /** \brief Read from 'sin' into 'root'.
*
Always keep comments from the input JSON. * Always keep comments from the input JSON.
*
This can be used to read a file into a particular sub-object. * This can be used to read a file into a particular sub-object.
For example: * For example:
\code * \code
Json::Value root; * Json::Value root;
cin >> root["dir"]["file"]; * cin >> root["dir"]["file"];
cout << root; * cout << root;
\endcode * \endcode
Result: * Result:
\verbatim * \verbatim
{ * {
"dir": { * "dir": {
"file": { * "file": {
// The input stream JSON would be nested here. * // The input stream JSON would be nested here.
} * }
} * }
} * }
\endverbatim * \endverbatim
\throw std::exception on parse error. * \throw std::exception on parse error.
\see Json::operator<<() * \see Json::operator<<()
*/ */
JSON_API IStream& operator>>(IStream&, Value&); JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json } // namespace Json
@@ -414,4 +400,4 @@ JSON_API IStream& operator>>(IStream&, Value&);
#pragma warning(pop) #pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_READER_H_INCLUDED #endif // JSON_READER_H_INCLUDED

View File

@@ -3,27 +3,49 @@
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_H_INCLUDED #ifndef JSON_H_INCLUDED
#define CPPTL_JSON_H_INCLUDED #define JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION) #if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h" #include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
// Conditional NORETURN attribute on the throw functions would:
// a) suppress false positives from static code analysis
// b) possibly improve optimization opportunities.
#if !defined(JSONCPP_NORETURN)
#if defined(_MSC_VER) && _MSC_VER == 1800
#define JSONCPP_NORETURN __declspec(noreturn)
#else
#define JSONCPP_NORETURN [[noreturn]]
#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 <array>
#include <exception> #include <exception>
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #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 // Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by... // be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
@@ -45,8 +67,8 @@ namespace Json {
class JSON_API Exception : public std::exception { class JSON_API Exception : public std::exception {
public: public:
Exception(String msg); Exception(String msg);
~Exception() JSONCPP_NOEXCEPT override; ~Exception() noexcept override;
char const* what() const JSONCPP_NOEXCEPT override; char const* what() const noexcept override;
protected: protected:
String msg_; String msg_;
@@ -76,9 +98,9 @@ public:
#endif #endif
/// used internally /// used internally
[[noreturn]] void throwRuntimeError(String const& msg); JSONCPP_NORETURN void throwRuntimeError(String const& msg);
/// used internally /// used internally
[[noreturn]] void throwLogicError(String const& msg); JSONCPP_NORETURN void throwLogicError(String const& msg);
/** \brief Type of the value held by a Value object. /** \brief Type of the value held by a Value object.
*/ */
@@ -108,11 +130,6 @@ enum PrecisionType {
decimalPlaces ///< we set max number of digits after "." in string 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. /** \brief Lightweight wrapper to tag static string.
* *
* Value constructor and objectValue member assignment takes advantage of the * Value constructor and objectValue member assignment takes advantage of the
@@ -177,21 +194,21 @@ class JSON_API Value {
friend class ValueIteratorBase; friend class ValueIteratorBase;
public: public:
typedef std::vector<String> Members; using Members = std::vector<String>;
typedef ValueIterator iterator; using iterator = ValueIterator;
typedef ValueConstIterator const_iterator; using const_iterator = ValueConstIterator;
typedef Json::UInt UInt; using UInt = Json::UInt;
typedef Json::Int Int; using Int = Json::Int;
#if defined(JSON_HAS_INT64) #if defined(JSON_HAS_INT64)
typedef Json::UInt64 UInt64; using UInt64 = Json::UInt64;
typedef Json::Int64 Int64; using Int64 = Json::Int64;
#endif // defined(JSON_HAS_INT64) #endif // defined(JSON_HAS_INT64)
typedef Json::LargestInt LargestInt; using LargestInt = Json::LargestInt;
typedef Json::LargestUInt LargestUInt; using LargestUInt = Json::LargestUInt;
typedef Json::ArrayIndex ArrayIndex; using ArrayIndex = Json::ArrayIndex;
// Required for boost integration, e. g. BOOST_TEST // Required for boost integration, e. g. BOOST_TEST
typedef std::string value_type; using value_type = std::string;
#if JSON_USE_NULLREF #if JSON_USE_NULLREF
// Binary compatibility kludges, do not use. // Binary compatibility kludges, do not use.
@@ -203,31 +220,34 @@ public:
static Value const& nullSingleton(); static Value const& nullSingleton();
/// Minimum signed integer value that can be stored in a Json::Value. /// Minimum signed integer value that can be stored in a Json::Value.
static const LargestInt minLargestInt; static constexpr LargestInt minLargestInt =
LargestInt(~(LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value. /// Maximum signed integer value that can be stored in a Json::Value.
static const LargestInt maxLargestInt; static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value. /// Maximum unsigned integer value that can be stored in a Json::Value.
static const LargestUInt maxLargestUInt; static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
/// Minimum signed int value that can be stored in a Json::Value. /// Minimum signed int value that can be stored in a Json::Value.
static const Int minInt; static constexpr Int minInt = Int(~(UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value. /// Maximum signed int value that can be stored in a Json::Value.
static const Int maxInt; static constexpr Int maxInt = Int(UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value. /// Maximum unsigned int value that can be stored in a Json::Value.
static const UInt maxUInt; static constexpr UInt maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64) #if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value. /// Minimum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 minInt64; static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value. /// Maximum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 maxInt64; static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value. /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static const UInt64 maxUInt64; static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64) #endif // defined(JSON_HAS_INT64)
/// Default precision for real value for string representation. /// Default precision for real value for string representation.
static const UInt defaultRealPrecision; static constexpr UInt defaultRealPrecision = 17;
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler // Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString // when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486 // cannot be defined as private. See issue #486
@@ -272,29 +292,26 @@ private:
}; };
public: public:
#ifndef JSON_USE_CPPTL_SMALLMAP
typedef std::map<CZString, Value> ObjectValues; 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 #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public: public:
/** \brief Create a default Value of the given type. /**
* \brief Create a default Value of the given type.
This is a very useful constructor. *
To create an empty array, pass arrayValue. * This is a very useful constructor.
To create an empty object, pass objectValue. * To create an empty array, pass arrayValue.
Another Value can then be set to this one by assignment. * To create an empty object, pass objectValue.
This is useful since clear() and resize() will not alter types. * Another Value can then be set to this one by assignment.
* This is useful since clear() and resize() will not alter types.
Examples: *
\code * Examples:
Json::Value null_value; // null * \code
Json::Value arr_value(Json::arrayValue); // [] * Json::Value null_value; // null
Json::Value obj_value(Json::objectValue); // {} * Json::Value arr_value(Json::arrayValue); // []
\endcode * Json::Value obj_value(Json::objectValue); // {}
*/ * \endcode
*/
Value(ValueType type = nullValue); Value(ValueType type = nullValue);
Value(Int value); Value(Int value);
Value(UInt value); Value(UInt value);
@@ -305,27 +322,25 @@ Json::Value obj_value(Json::objectValue); // {}
Value(double value); Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes. Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
/** \brief Constructs a value from a static string. /**
* \brief Constructs a value from a static string.
*
* Like other value string constructor but do not duplicate the string for * Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to this * internal storage. The given string must remain alive after the call to
* constructor. * this constructor.
*
* \note This works only for null-terminated strings. (We cannot change the * \note This works only for null-terminated strings. (We cannot change the
* size of this class, so we have nowhere to store the length, * size of this class, so we have nowhere to store the length, which might be
* which might be computed later for various operations.) * computed later for various operations.)
* *
* Example of usage: * Example of usage:
* \code * \code
* static StaticString foo("some text"); * static StaticString foo("some text");
* Json::Value aValue(foo); * Json::Value aValue(foo);
* \endcode * \endcode
*/ */
Value(const StaticString& value); Value(const StaticString& value);
Value(const String& value); ///< Copy data() til size(). Embedded Value(const String& value);
///< zeroes too.
#ifdef JSON_USE_CPPTL
Value(const CppTL::ConstString& value);
#endif
Value(bool value); Value(bool value);
Value(const Value& other); Value(const Value& other);
Value(Value&& other); Value(Value&& other);
@@ -367,9 +382,6 @@ Json::Value obj_value(Json::objectValue); // {}
* \return false if !string. (Seg-fault if str or end are NULL.) * \return false if !string. (Seg-fault if str or end are NULL.)
*/ */
bool getString(char const** begin, char const** end) const; bool getString(char const** begin, char const** end) const;
#ifdef JSON_USE_CPPTL
CppTL::ConstString asConstString() const;
#endif
Int asInt() const; Int asInt() const;
UInt asUInt() const; UInt asUInt() const;
#if defined(JSON_HAS_INT64) #if defined(JSON_HAS_INT64)
@@ -395,6 +407,10 @@ Json::Value obj_value(Json::objectValue); // {}
bool isArray() const; bool isArray() const;
bool isObject() const; bool isObject() const;
/// The `as<T>` and `is<T>` member function templates and specializations.
template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
bool isConvertibleTo(ValueType other) const; bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object /// Number of values in array or object
@@ -405,7 +421,7 @@ Json::Value obj_value(Json::objectValue); // {}
bool empty() const; bool empty() const;
/// Return !isNull() /// Return !isNull()
JSONCPP_OP_EXPLICIT operator bool() const; explicit operator bool() const;
/// Remove all object members and array elements. /// Remove all object members and array elements.
/// \pre type() is arrayValue, objectValue, or nullValue /// \pre type() is arrayValue, objectValue, or nullValue
@@ -419,35 +435,26 @@ Json::Value obj_value(Json::objectValue); // {}
/// \post type() is arrayValue /// \post type() is arrayValue
void resize(ArrayIndex newSize); void resize(ArrayIndex newSize);
/// Access an array element (zero based index ). //@{
/// If the array contains less than index element, then null value are /// Access an array element (zero based index). If the array contains less
/// inserted /// than index element, then null value are inserted in the array so that
/// in the array so that its size is index+1. /// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish /// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index); Value& operator[](ArrayIndex index);
/// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are
/// inserted
/// in the array so that its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](int index); Value& operator[](int index);
//@}
/// Access an array element (zero based index ) //@{
/// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish /// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const; const Value& operator[](ArrayIndex index) const;
/// Access an array element (zero based index )
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](int index) const; const Value& operator[](int index) const;
//@}
/// If the array contains at least index+1 elements, returns the element /// If the array contains at least index+1 elements, returns the element
/// value, /// value, otherwise returns defaultValue.
/// otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const; Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size(). /// Return true if index < size().
bool isValidIndex(ArrayIndex index) const; bool isValidIndex(ArrayIndex index) const;
@@ -457,9 +464,13 @@ Json::Value obj_value(Json::objectValue); // {}
Value& append(const Value& value); Value& append(const Value& value);
Value& append(Value&& value); Value& append(Value&& value);
/// \brief Insert value in array at specific index
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. /// 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. /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception. /// Exceeding that will cause an exception.
Value& operator[](const char* key); Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with /// Access an object value by name, returns null if there is no member with
/// that name. /// that name.
@@ -472,43 +483,30 @@ Json::Value obj_value(Json::objectValue); // {}
/// \param key may contain embedded nulls. /// \param key may contain embedded nulls.
const Value& operator[](const String& key) const; const Value& operator[](const String& key) const;
/** \brief Access an object value by name, create a null member if it does not /** \brief Access an object value by name, create a null member if it does not
exist. * exist.
*
* If the object has no entry for that name, then the member name used to * If the object has no entry for that name, then the member name used to
store * store the new entry is not duplicated.
* the new entry is not duplicated.
* Example of use: * Example of use:
* \code * \code
* Json::Value object; * Json::Value object;
* static const StaticString code("code"); * static const StaticString code("code");
* object[code] = 1234; * object[code] = 1234;
* \endcode * \endcode
*/ */
Value& operator[](const StaticString& key); 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. /// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy /// \note deep copy
Value get(const char* key, const Value& defaultValue) const; Value get(const char* key, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise. /// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy /// \note deep copy
/// \note key may contain embedded nulls. /// \note key may contain embedded nulls.
Value Value get(const char* begin, const char* end,
get(const char* begin, const char* end, const Value& defaultValue) const; const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise. /// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy /// \note deep copy
/// \param key may contain embedded nulls. /// \param key may contain embedded nulls.
Value get(const String& key, const Value& defaultValue) const; 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, /// Most general and efficient version of isMember()const, get()const,
/// and operator[]const /// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
@@ -530,20 +528,20 @@ Json::Value obj_value(Json::objectValue); // {}
/// but 'key' is null-terminated. /// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed); bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member. /** \brief Remove the named map member.
*
Update 'removed' iff removed. * Update 'removed' iff removed.
\param key may contain embedded nulls. * \param key may contain embedded nulls.
\return true iff removed (no exceptions) * \return true iff removed (no exceptions)
*/ */
bool removeMember(String const& key, Value* removed); bool removeMember(String const& key, Value* removed);
/// Same as removeMember(String const& key, Value* removed) /// Same as removeMember(String const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed); bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element. /** \brief Remove the indexed array element.
*
O(n) expensive operations. * O(n) expensive operations.
Update 'removed' iff removed. * Update 'removed' iff removed.
\return true if removed (no exceptions) * \return true if removed (no exceptions)
*/ */
bool removeIndex(ArrayIndex index, Value* removed); bool removeIndex(ArrayIndex index, Value* removed);
/// Return true if the object has a member named key. /// Return true if the object has a member named key.
@@ -554,10 +552,6 @@ Json::Value obj_value(Json::objectValue); // {}
bool isMember(const String& key) const; bool isMember(const String& key) const;
/// Same as isMember(String const& key)const /// Same as isMember(String const& key)const
bool isMember(const char* begin, const char* end) 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. /// \brief Return a list of the member names.
/// ///
@@ -566,11 +560,6 @@ Json::Value obj_value(Json::objectValue); // {}
/// \post if type() was nullValue, it remains nullValue /// \post if type() was nullValue, it remains nullValue
Members getMemberNames() const; Members getMemberNames() const;
//# ifdef JSON_USE_CPPTL
// EnumMemberNames enumMemberNames() const;
// EnumValues enumValues() const;
//# endif
/// \deprecated Always pass len. /// \deprecated Always pass len.
JSONCPP_DEPRECATED("Use setComment(String const&) instead.") JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
void setComment(const char* comment, CommentPlacement placement) { void setComment(const char* comment, CommentPlacement placement) {
@@ -650,7 +639,7 @@ private:
Comments& operator=(Comments&& that); Comments& operator=(Comments&& that);
bool has(CommentPlacement slot) const; bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const; String get(CommentPlacement slot) const;
void set(CommentPlacement slot, String s); void set(CommentPlacement slot, String comment);
private: private:
using Array = std::array<String, numberOfCommentPlacement>; using Array = std::array<String, numberOfCommentPlacement>;
@@ -664,6 +653,36 @@ private:
ptrdiff_t limit_; ptrdiff_t limit_;
}; };
template <> inline bool Value::as<bool>() const { return asBool(); }
template <> inline bool Value::is<bool>() const { return isBool(); }
template <> inline Int Value::as<Int>() const { return asInt(); }
template <> inline bool Value::is<Int>() const { return isInt(); }
template <> inline UInt Value::as<UInt>() const { return asUInt(); }
template <> inline bool Value::is<UInt>() const { return isUInt(); }
#if defined(JSON_HAS_INT64)
template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
template <> inline bool Value::is<Int64>() const { return isInt64(); }
template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
#endif
template <> inline double Value::as<double>() const { return asDouble(); }
template <> inline bool Value::is<double>() const { return isDouble(); }
template <> inline String Value::as<String>() const { return asString(); }
template <> inline bool Value::is<String>() const { return isString(); }
/// These `as` specializations are type conversions, and do not have a
/// corresponding `is`.
template <> inline float Value::as<float>() const { return asFloat(); }
template <> inline const char* Value::as<const char*>() const {
return asCString();
}
/** \brief Experimental and untested: represents an element of the "path" to /** \brief Experimental and untested: represents an element of the "path" to
* access a node. * access a node.
*/ */
@@ -674,7 +693,7 @@ public:
PathArgument(); PathArgument();
PathArgument(ArrayIndex index); PathArgument(ArrayIndex index);
PathArgument(const char* key); PathArgument(const char* key);
PathArgument(const String& key); PathArgument(String key);
private: private:
enum Kind { kindNone = 0, kindIndex, kindKey }; enum Kind { kindNone = 0, kindIndex, kindKey };
@@ -692,12 +711,11 @@ private:
* - ".name1.name2.name3" * - ".name1.name2.name3"
* - ".[0][1][2].name1[3]" * - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter * - ".%" => member name is provided as parameter
* - ".[%]" => index is provied as parameter * - ".[%]" => index is provided as parameter
*/ */
class JSON_API Path { class JSON_API Path {
public: public:
Path(const String& path, Path(const String& path, const PathArgument& a1 = PathArgument(),
const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(), const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(), const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(), const PathArgument& a4 = PathArgument(),
@@ -710,14 +728,12 @@ public:
Value& make(Value& root) const; Value& make(Value& root) const;
private: private:
typedef std::vector<const PathArgument*> InArgs; using InArgs = std::vector<const PathArgument*>;
typedef std::vector<PathArgument> Args; using Args = std::vector<PathArgument>;
void makePath(const String& path, const InArgs& in); void makePath(const String& path, const InArgs& in);
void addPathInArg(const String& path, void addPathInArg(const String& path, const InArgs& in,
const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind);
InArgs::const_iterator& itInArg,
PathArgument::Kind kind);
static void invalidPath(const String& path, int location); static void invalidPath(const String& path, int location);
Args args_; Args args_;
@@ -728,10 +744,10 @@ private:
*/ */
class JSON_API ValueIteratorBase { class JSON_API ValueIteratorBase {
public: public:
typedef std::bidirectional_iterator_tag iterator_category; using iterator_category = std::bidirectional_iterator_tag;
typedef unsigned int size_t; using size_t = unsigned int;
typedef int difference_type; using difference_type = int;
typedef ValueIteratorBase SelfType; using SelfType = ValueIteratorBase;
bool operator==(const SelfType& other) const { return isEqual(other); } bool operator==(const SelfType& other) const { return isEqual(other); }
@@ -766,7 +782,14 @@ public:
char const* memberName(char const** end) const; char const* memberName(char const** end) const;
protected: protected:
Value& deref() const; /*! Internal utility functions to assist with implementing
* other iterator functions. The const and non-const versions
* of the "deref" protected methods expose the protected
* current_ member variable in a way that can often be
* optimized away by the compiler.
*/
const Value& deref() const;
Value& deref();
void increment(); void increment();
@@ -797,12 +820,12 @@ class JSON_API ValueConstIterator : public ValueIteratorBase {
friend class Value; friend class Value;
public: public:
typedef const Value value_type; using value_type = const Value;
// typedef unsigned int size_t; // typedef unsigned int size_t;
// typedef int difference_type; // typedef int difference_type;
typedef const Value& reference; using reference = const Value&;
typedef const Value* pointer; using pointer = const Value*;
typedef ValueConstIterator SelfType; using SelfType = ValueConstIterator;
ValueConstIterator(); ValueConstIterator();
ValueConstIterator(ValueIterator const& other); ValueConstIterator(ValueIterator const& other);
@@ -848,12 +871,12 @@ class JSON_API ValueIterator : public ValueIteratorBase {
friend class Value; friend class Value;
public: public:
typedef Value value_type; using value_type = Value;
typedef unsigned int size_t; using size_t = unsigned int;
typedef int difference_type; using difference_type = int;
typedef Value& reference; using reference = Value&;
typedef Value* pointer; using pointer = Value*;
typedef ValueIterator SelfType; using SelfType = ValueIterator;
ValueIterator(); ValueIterator();
explicit ValueIterator(const ValueConstIterator& other); explicit ValueIterator(const ValueConstIterator& other);
@@ -889,9 +912,13 @@ public:
return *this; return *this;
} }
reference operator*() const { return deref(); } /*! The return value of non-const iterators can be
* changed, so the these functions are not const
pointer operator->() const { return &deref(); } * because the returned references/pointers can be used
* to change state of the base class.
*/
reference operator*() { return deref(); }
pointer operator->() { return &deref(); }
}; };
inline void swap(Value& a, Value& b) { a.swap(b); } inline void swap(Value& a, Value& b) { a.swap(b); }
@@ -904,4 +931,4 @@ inline void swap(Value& a, Value& b) { a.swap(b); }
#pragma warning(pop) #pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_H_INCLUDED #endif // JSON_H_INCLUDED

28
include/json/version.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
// 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!!
#define JSONCPP_VERSION_STRING "1.9.3"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 3
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED

View File

@@ -27,18 +27,17 @@ namespace Json {
class Value; class Value;
/** /**
*
Usage: * Usage:
\code * \code
using namespace Json; * using namespace Json;
void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { * void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
std::unique_ptr<StreamWriter> const writer( * { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
factory.newStreamWriter()); * writer->write(value, &std::cout);
writer->write(value, &std::cout); * std::cout << std::endl; // add lf and flush
std::cout << std::endl; // add lf and flush * }
} * \endcode
\endcode */
*/
class JSON_API StreamWriter { class JSON_API StreamWriter {
protected: protected:
OStream* sout_; // not owned; will not delete OStream* sout_; // not owned; will not delete
@@ -46,10 +45,11 @@ public:
StreamWriter(); StreamWriter();
virtual ~StreamWriter(); virtual ~StreamWriter();
/** Write Value into document as configured in sub-class. /** Write Value into document as configured in sub-class.
Do not take ownership of sout, but maintain a reference during function. * Do not take ownership of sout, but maintain a reference during function.
\pre sout != NULL * \pre sout != NULL
\return zero on success (For now, we always return zero, so check the * \return zero on success (For now, we always return zero, so check the
stream instead.) \throw std::exception possibly, depending on configuration * stream instead.) \throw std::exception possibly, depending on
* configuration
*/ */
virtual int write(Value const& root, OStream* sout) = 0; virtual int write(Value const& root, OStream* sout) = 0;
@@ -73,49 +73,49 @@ String JSON_API writeString(StreamWriter::Factory const& factory,
/** \brief Build a StreamWriter implementation. /** \brief Build a StreamWriter implementation.
Usage: * Usage:
\code * \code
using namespace Json; * using namespace Json;
Value value = ...; * Value value = ...;
StreamWriterBuilder builder; * StreamWriterBuilder builder;
builder["commentStyle"] = "None"; * builder["commentStyle"] = "None";
builder["indentation"] = " "; // or whatever you like * builder["indentation"] = " "; // or whatever you like
std::unique_ptr<Json::StreamWriter> writer( * std::unique_ptr<Json::StreamWriter> writer(
builder.newStreamWriter()); * builder.newStreamWriter());
writer->write(value, &std::cout); * writer->write(value, &std::cout);
std::cout << std::endl; // add lf and flush * std::cout << std::endl; // add lf and flush
\endcode * \endcode
*/ */
class JSON_API StreamWriterBuilder : public StreamWriter::Factory { class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public: public:
// Note: We use a Json::Value so that we can add data-members to this class // Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump. // without a major version bump.
/** Configuration of this builder. /** Configuration of this builder.
Available settings (case-sensitive): * Available settings (case-sensitive):
- "commentStyle": "None" or "All" * - "commentStyle": "None" or "All"
- "indentation": "<anything>". * - "indentation": "<anything>".
- Setting this to an empty string also omits newline characters. * - Setting this to an empty string also omits newline characters.
- "enableYAMLCompatibility": false or true * - "enableYAMLCompatibility": false or true
- slightly change the whitespace around colons * - slightly change the whitespace around colons
- "dropNullPlaceholders": false or true * - "dropNullPlaceholders": false or true
- Drop the "null" string from the writer's output for nullValues. * - Drop the "null" string from the writer's output for nullValues.
Strictly speaking, this is not valid JSON. But when the output is being * Strictly speaking, this is not valid JSON. But when the output is being
fed to a browser's JavaScript, it makes for smaller output and the * fed to a browser's JavaScript, it makes for smaller output and the
browser can handle the output just fine. * browser can handle the output just fine.
- "useSpecialFloats": false or true * - "useSpecialFloats": false or true
- If true, outputs non-finite floating point values in the following way: * - If true, outputs non-finite floating point values in the following way:
NaN values as "NaN", positive infinity as "Infinity", and negative * NaN values as "NaN", positive infinity as "Infinity", and negative
infinity as "-Infinity". * infinity as "-Infinity".
- "precision": int * - "precision": int
- Number of precision digits for formatting of real values. * - Number of precision digits for formatting of real values.
- "precisionType": "significant"(default) or "decimal" * - "precisionType": "significant"(default) or "decimal"
- Type of precision for formatting of real values. * - Type of precision for formatting of real values.
You can examine 'settings_` yourself * You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any * to see the defaults. You can also write and read them just like any
JSON Value. * JSON Value.
\sa setDefaults() * \sa setDefaults()
*/ */
Json::Value settings_; Json::Value settings_;
StreamWriterBuilder(); StreamWriterBuilder();
@@ -252,7 +252,7 @@ private:
static bool hasCommentForValue(const Value& value); static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text); static String normalizeEOL(const String& text);
typedef std::vector<String> ChildValues; using ChildValues = std::vector<String>;
ChildValues childValues_; ChildValues childValues_;
String document_; String document_;
@@ -326,7 +326,7 @@ private:
static bool hasCommentForValue(const Value& value); static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text); static String normalizeEOL(const String& text);
typedef std::vector<String> ChildValues; using ChildValues = std::vector<String>;
ChildValues childValues_; ChildValues childValues_;
OStream* document_; OStream* document_;
@@ -346,10 +346,9 @@ String JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64) #endif // if defined(JSON_HAS_INT64)
String JSON_API valueToString(LargestInt value); String JSON_API valueToString(LargestInt value);
String JSON_API valueToString(LargestUInt value); String JSON_API valueToString(LargestUInt value);
String JSON_API String JSON_API valueToString(
valueToString(double value, double value, unsigned int precision = Value::defaultRealPrecision,
unsigned int precision = Value::defaultRealPrecision, PrecisionType precisionType = PrecisionType::significantDigits);
PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value); String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value); String JSON_API valueToQuotedString(const char* value);

View File

@@ -1,390 +0,0 @@
# Copyright 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
"""Tag the sandbox for release, make source and doc tarballs.
Requires Python 2.6
Example of invocation (use to test the script):
python makerelease.py --platform=msvc6,msvc71,msvc80,msvc90,mingw -ublep 0.6.0 0.7.0-dev
When testing this script:
python makerelease.py --force --retag --platform=msvc6,msvc71,msvc80,mingw -ublep test-0.6.0 test-0.6.1-dev
Example of invocation when doing a release:
python makerelease.py 0.5.0 0.6.0-dev
Note: This was for Subversion. Now that we are in GitHub, we do not
need to build versioned tarballs anymore, so makerelease.py is defunct.
"""
from __future__ import print_function
import os.path
import subprocess
import sys
import doxybuild
import subprocess
import xml.etree.ElementTree as ElementTree
import shutil
import urllib2
import tempfile
import os
import time
from devtools import antglob, fixeol, tarball
import amalgamate
SVN_ROOT = 'https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/'
SVN_TAG_ROOT = SVN_ROOT + 'tags/jsoncpp'
SCONS_LOCAL_URL = 'http://sourceforge.net/projects/scons/files/scons-local/1.2.0/scons-local-1.2.0.tar.gz/download'
SOURCEFORGE_PROJECT = 'jsoncpp'
def set_version(version):
with open('version','wb') as f:
f.write(version.strip())
def rmdir_if_exist(dir_path):
if os.path.isdir(dir_path):
shutil.rmtree(dir_path)
class SVNError(Exception):
pass
def svn_command(command, *args):
cmd = ['svn', '--non-interactive', command] + list(args)
print('Running:', ' '.join(cmd))
process = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = process.communicate()[0]
if process.returncode:
error = SVNError('SVN command failed:\n' + stdout)
error.returncode = process.returncode
raise error
return stdout
def check_no_pending_commit():
"""Checks that there is no pending commit in the sandbox."""
stdout = svn_command('status', '--xml')
etree = ElementTree.fromstring(stdout)
msg = []
for entry in etree.getiterator('entry'):
path = entry.get('path')
status = entry.find('wc-status').get('item')
if status != 'unversioned' and path != 'version':
msg.append('File "%s" has pending change (status="%s")' % (path, status))
if msg:
msg.insert(0, 'Pending change to commit found in sandbox. Commit them first!')
return '\n'.join(msg)
def svn_join_url(base_url, suffix):
if not base_url.endswith('/'):
base_url += '/'
if suffix.startswith('/'):
suffix = suffix[1:]
return base_url + suffix
def svn_check_if_tag_exist(tag_url):
"""Checks if a tag exist.
Returns: True if the tag exist, False otherwise.
"""
try:
list_stdout = svn_command('list', tag_url)
except SVNError as e:
if e.returncode != 1 or not str(e).find('tag_url'):
raise e
# otherwise ignore error, meaning tag does not exist
return False
return True
def svn_commit(message):
"""Commit the sandbox, providing the specified comment.
"""
svn_command('ci', '-m', message)
def svn_tag_sandbox(tag_url, message):
"""Makes a tag based on the sandbox revisions.
"""
svn_command('copy', '-m', message, '.', tag_url)
def svn_remove_tag(tag_url, message):
"""Removes an existing tag.
"""
svn_command('delete', '-m', message, tag_url)
def svn_export(tag_url, export_dir):
"""Exports the tag_url revision to export_dir.
Target directory, including its parent is created if it does not exist.
If the directory export_dir exist, it is deleted before export proceed.
"""
rmdir_if_exist(export_dir)
svn_command('export', tag_url, export_dir)
def fix_sources_eol(dist_dir):
"""Set file EOL for tarball distribution.
"""
print('Preparing exported source file EOL for distribution...')
prune_dirs = antglob.prune_dirs + 'scons-local* ./build* ./libs ./dist'
win_sources = antglob.glob(dist_dir,
includes = '**/*.sln **/*.vcproj',
prune_dirs = prune_dirs)
unix_sources = antglob.glob(dist_dir,
includes = '''**/*.h **/*.cpp **/*.inl **/*.txt **/*.dox **/*.py **/*.html **/*.in
sconscript *.json *.expected AUTHORS LICENSE''',
excludes = antglob.default_excludes + 'scons.py sconsign.py scons-*',
prune_dirs = prune_dirs)
for path in win_sources:
fixeol.fix_source_eol(path, is_dry_run = False, verbose = True, eol = '\r\n')
for path in unix_sources:
fixeol.fix_source_eol(path, is_dry_run = False, verbose = True, eol = '\n')
def download(url, target_path):
"""Download file represented by url to target_path.
"""
f = urllib2.urlopen(url)
try:
data = f.read()
finally:
f.close()
fout = open(target_path, 'wb')
try:
fout.write(data)
finally:
fout.close()
def check_compile(distcheck_top_dir, platform):
cmd = [sys.executable, 'scons.py', 'platform=%s' % platform, 'check']
print('Running:', ' '.join(cmd))
log_path = os.path.join(distcheck_top_dir, 'build-%s.log' % platform)
flog = open(log_path, 'wb')
try:
process = subprocess.Popen(cmd,
stdout=flog,
stderr=subprocess.STDOUT,
cwd=distcheck_top_dir)
stdout = process.communicate()[0]
status = (process.returncode == 0)
finally:
flog.close()
return (status, log_path)
def write_tempfile(content, **kwargs):
fd, path = tempfile.mkstemp(**kwargs)
f = os.fdopen(fd, 'wt')
try:
f.write(content)
finally:
f.close()
return path
class SFTPError(Exception):
pass
def run_sftp_batch(userhost, sftp, batch, retry=0):
path = write_tempfile(batch, suffix='.sftp', text=True)
# psftp -agent -C blep,jsoncpp@web.sourceforge.net -batch -b batch.sftp -bc
cmd = [sftp, '-agent', '-C', '-batch', '-b', path, '-bc', userhost]
error = None
for retry_index in range(0, max(1,retry)):
heading = retry_index == 0 and 'Running:' or 'Retrying:'
print(heading, ' '.join(cmd))
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = process.communicate()[0]
if process.returncode != 0:
error = SFTPError('SFTP batch failed:\n' + stdout)
else:
break
if error:
raise error
return stdout
def sourceforge_web_synchro(sourceforge_project, doc_dir,
user=None, sftp='sftp'):
"""Notes: does not synchronize sub-directory of doc-dir.
"""
userhost = '%s,%s@web.sourceforge.net' % (user, sourceforge_project)
stdout = run_sftp_batch(userhost, sftp, """
cd htdocs
dir
exit
""")
existing_paths = set()
collect = 0
for line in stdout.split('\n'):
line = line.strip()
if not collect and line.endswith('> dir'):
collect = True
elif collect and line.endswith('> exit'):
break
elif collect == 1:
collect = 2
elif collect == 2:
path = line.strip().split()[-1:]
if path and path[0] not in ('.', '..'):
existing_paths.add(path[0])
upload_paths = set([os.path.basename(p) for p in antglob.glob(doc_dir)])
paths_to_remove = existing_paths - upload_paths
if paths_to_remove:
print('Removing the following file from web:')
print('\n'.join(paths_to_remove))
stdout = run_sftp_batch(userhost, sftp, """cd htdocs
rm %s
exit""" % ' '.join(paths_to_remove))
print('Uploading %d files:' % len(upload_paths))
batch_size = 10
upload_paths = list(upload_paths)
start_time = time.time()
for index in range(0,len(upload_paths),batch_size):
paths = upload_paths[index:index+batch_size]
file_per_sec = (time.time() - start_time) / (index+1)
remaining_files = len(upload_paths) - index
remaining_sec = file_per_sec * remaining_files
print('%d/%d, ETA=%.1fs' % (index+1, len(upload_paths), remaining_sec))
run_sftp_batch(userhost, sftp, """cd htdocs
lcd %s
mput %s
exit""" % (doc_dir, ' '.join(paths)), retry=3)
def sourceforge_release_tarball(sourceforge_project, paths, user=None, sftp='sftp'):
userhost = '%s,%s@frs.sourceforge.net' % (user, sourceforge_project)
run_sftp_batch(userhost, sftp, """
mput %s
exit
""" % (' '.join(paths),))
def main():
usage = """%prog release_version next_dev_version
Update 'version' file to release_version and commit.
Generates the document tarball.
Tags the sandbox revision with release_version.
Update 'version' file to next_dev_version and commit.
Performs an svn export of tag release version, and build a source tarball.
Must be started in the project top directory.
Warning: --force should only be used when developing/testing the release script.
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option('--dot', dest="dot_path", action='store', default=doxybuild.find_program('dot'),
help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
parser.add_option('--doxygen', dest="doxygen_path", action='store', default=doxybuild.find_program('doxygen'),
help="""Path to Doxygen tool. [Default: %default]""")
parser.add_option('--force', dest="ignore_pending_commit", action='store_true', default=False,
help="""Ignore pending commit. [Default: %default]""")
parser.add_option('--retag', dest="retag_release", action='store_true', default=False,
help="""Overwrite release existing tag if it exist. [Default: %default]""")
parser.add_option('-p', '--platforms', dest="platforms", action='store', default='',
help="""Comma separated list of platform passed to scons for build check.""")
parser.add_option('--no-test', dest="no_test", action='store_true', default=False,
help="""Skips build check.""")
parser.add_option('--no-web', dest="no_web", action='store_true', default=False,
help="""Do not update web site.""")
parser.add_option('-u', '--upload-user', dest="user", action='store',
help="""Sourceforge user for SFTP documentation upload.""")
parser.add_option('--sftp', dest='sftp', action='store', default=doxybuild.find_program('psftp', 'sftp'),
help="""Path of the SFTP compatible binary used to upload the documentation.""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
if len(args) != 2:
parser.error('release_version missing on command-line.')
release_version = args[0]
next_version = args[1]
if not options.platforms and not options.no_test:
parser.error('You must specify either --platform or --no-test option.')
if options.ignore_pending_commit:
msg = ''
else:
msg = check_no_pending_commit()
if not msg:
print('Setting version to', release_version)
set_version(release_version)
svn_commit('Release ' + release_version)
tag_url = svn_join_url(SVN_TAG_ROOT, release_version)
if svn_check_if_tag_exist(tag_url):
if options.retag_release:
svn_remove_tag(tag_url, 'Overwriting previous tag')
else:
print('Aborting, tag %s already exist. Use --retag to overwrite it!' % tag_url)
sys.exit(1)
svn_tag_sandbox(tag_url, 'Release ' + release_version)
print('Generated doxygen document...')
## doc_dirname = r'jsoncpp-api-html-0.5.0'
## doc_tarball_path = r'e:\prg\vc\Lib\jsoncpp-trunk\dist\jsoncpp-api-html-0.5.0.tar.gz'
doc_tarball_path, doc_dirname = doxybuild.build_doc(options, make_release=True)
doc_distcheck_dir = 'dist/doccheck'
tarball.decompress(doc_tarball_path, doc_distcheck_dir)
doc_distcheck_top_dir = os.path.join(doc_distcheck_dir, doc_dirname)
export_dir = 'dist/export'
svn_export(tag_url, export_dir)
fix_sources_eol(export_dir)
source_dir = 'jsoncpp-src-' + release_version
source_tarball_path = 'dist/%s.tar.gz' % source_dir
print('Generating source tarball to', source_tarball_path)
tarball.make_tarball(source_tarball_path, [export_dir], export_dir, prefix_dir=source_dir)
amalgamation_tarball_path = 'dist/%s-amalgamation.tar.gz' % source_dir
print('Generating amalgamation source tarball to', amalgamation_tarball_path)
amalgamation_dir = 'dist/amalgamation'
amalgamate.amalgamate_source(export_dir, '%s/jsoncpp.cpp' % amalgamation_dir, 'json/json.h')
amalgamation_source_dir = 'jsoncpp-src-amalgamation' + release_version
tarball.make_tarball(amalgamation_tarball_path, [amalgamation_dir],
amalgamation_dir, prefix_dir=amalgamation_source_dir)
# Decompress source tarball, download and install scons-local
distcheck_dir = 'dist/distcheck'
distcheck_top_dir = distcheck_dir + '/' + source_dir
print('Decompressing source tarball to', distcheck_dir)
rmdir_if_exist(distcheck_dir)
tarball.decompress(source_tarball_path, distcheck_dir)
scons_local_path = 'dist/scons-local.tar.gz'
print('Downloading scons-local to', scons_local_path)
download(SCONS_LOCAL_URL, scons_local_path)
print('Decompressing scons-local to', distcheck_top_dir)
tarball.decompress(scons_local_path, distcheck_top_dir)
# Run compilation
print('Compiling decompressed tarball')
all_build_status = True
for platform in options.platforms.split(','):
print('Testing platform:', platform)
build_status, log_path = check_compile(distcheck_top_dir, platform)
print('see build log:', log_path)
print(build_status and '=> ok' or '=> FAILED')
all_build_status = all_build_status and build_status
if not build_status:
print('Testing failed on at least one platform, aborting...')
svn_remove_tag(tag_url, 'Removing tag due to failed testing')
sys.exit(1)
if options.user:
if not options.no_web:
print('Uploading documentation using user', options.user)
sourceforge_web_synchro(SOURCEFORGE_PROJECT, doc_distcheck_top_dir, user=options.user, sftp=options.sftp)
print('Completed documentation upload')
print('Uploading source and documentation tarballs for release using user', options.user)
sourceforge_release_tarball(SOURCEFORGE_PROJECT,
[source_tarball_path, doc_tarball_path],
user=options.user, sftp=options.sftp)
print('Source and doc release tarballs uploaded')
else:
print('No upload user specified. Web site and download tarball were not uploaded.')
print('Tarball can be found at:', doc_tarball_path)
# Set next version number and commit
set_version(next_version)
svn_commit('Released ' + release_version)
else:
sys.stderr.write(msg + '\n')
if __name__ == '__main__':
main()

View File

@@ -1,45 +1,35 @@
project( project(
'jsoncpp', 'jsoncpp',
'cpp', 'cpp',
version : '1.9.0',
# 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.3',
default_options : [ default_options : [
'buildtype=release', 'buildtype=release',
'cpp_std=c++11', 'cpp_std=c++11',
'warning_level=1'], 'warning_level=1'],
license : 'Public Domain', license : 'Public Domain',
meson_version : '>= 0.50.0') meson_version : '>= 0.49.0')
jsoncpp_ver_arr = meson.project_version().split('.')
jsoncpp_major_version = jsoncpp_ver_arr[0]
jsoncpp_minor_version = jsoncpp_ver_arr[1]
jsoncpp_patch_version = jsoncpp_ver_arr[2]
jsoncpp_cdata = configuration_data() jsoncpp_headers = files([
jsoncpp_cdata.set('JSONCPP_VERSION', meson.project_version())
jsoncpp_cdata.set('JSONCPP_VERSION_MAJOR', jsoncpp_major_version)
jsoncpp_cdata.set('JSONCPP_VERSION_MINOR', jsoncpp_minor_version)
jsoncpp_cdata.set('JSONCPP_VERSION_PATCH', jsoncpp_patch_version)
jsoncpp_cdata.set('JSONCPP_USE_SECURE_MEMORY',0)
jsoncpp_gen_sources = configure_file(
input : 'src/lib_json/version.h.in',
output : 'version.h',
configuration : jsoncpp_cdata,
install : true,
install_dir : join_paths(get_option('prefix'), get_option('includedir'), 'json')
)
jsoncpp_headers = [
'include/json/allocator.h', 'include/json/allocator.h',
'include/json/assertions.h', 'include/json/assertions.h',
'include/json/autolink.h',
'include/json/config.h', 'include/json/config.h',
'include/json/features.h', 'include/json/json_features.h',
'include/json/forwards.h', 'include/json/forwards.h',
'include/json/json.h', 'include/json/json.h',
'include/json/reader.h', 'include/json/reader.h',
'include/json/value.h', 'include/json/value.h',
'include/json/writer.h'] 'include/json/version.h',
'include/json/writer.h',
])
jsoncpp_include_directories = include_directories('include') jsoncpp_include_directories = include_directories('include')
install_headers( install_headers(
@@ -55,14 +45,12 @@ else
endif endif
jsoncpp_lib = library( jsoncpp_lib = library(
'jsoncpp', 'jsoncpp', files([
[ jsoncpp_gen_sources,
jsoncpp_headers,
'src/lib_json/json_tool.h',
'src/lib_json/json_reader.cpp', 'src/lib_json/json_reader.cpp',
'src/lib_json/json_value.cpp', 'src/lib_json/json_value.cpp',
'src/lib_json/json_writer.cpp'], 'src/lib_json/json_writer.cpp',
soversion : 21, ]),
soversion : 24,
install : true, install : true,
include_directories : jsoncpp_include_directories, include_directories : jsoncpp_include_directories,
cpp_args: dll_export_flag) cpp_args: dll_export_flag)
@@ -78,18 +66,21 @@ import('pkgconfig').generate(
jsoncpp_dep = declare_dependency( jsoncpp_dep = declare_dependency(
include_directories : jsoncpp_include_directories, include_directories : jsoncpp_include_directories,
link_with : jsoncpp_lib, link_with : jsoncpp_lib,
version : meson.project_version(), version : meson.project_version())
sources : jsoncpp_gen_sources)
# tests # tests
python = import('python3').find_python() if meson.is_subproject() or not get_option('tests')
subdir_done()
endif
python = import('python').find_installation('python3')
jsoncpp_test = executable( jsoncpp_test = executable(
'jsoncpp_test', 'jsoncpp_test', files([
[ 'src/test_lib_json/jsontest.cpp', 'src/test_lib_json/jsontest.cpp',
'src/test_lib_json/jsontest.h',
'src/test_lib_json/main.cpp', 'src/test_lib_json/main.cpp',
'src/test_lib_json/fuzz.cpp'], 'src/test_lib_json/fuzz.cpp',
]),
include_directories : jsoncpp_include_directories, include_directories : jsoncpp_include_directories,
link_with : jsoncpp_lib, link_with : jsoncpp_lib,
install : false, install : false,
@@ -112,5 +103,17 @@ test(
'-B', '-B',
join_paths(meson.current_source_dir(), 'test/runjsontests.py'), join_paths(meson.current_source_dir(), 'test/runjsontests.py'),
jsontestrunner, 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'),
) )

5
meson_options.txt Normal file
View File

@@ -0,0 +1,5 @@
option(
'tests',
type : 'boolean',
value : true,
description : 'Enable building tests')

View File

@@ -1,5 +1,7 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@ prefix=@CMAKE_INSTALL_PREFIX@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: jsoncpp Name: jsoncpp
Description: A C++ library for interacting with JSON Description: A C++ library for interacting with JSON

View File

@@ -1,11 +1,24 @@
find_package(PythonInterp 2.6) 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})
else()
set(Python_ADDITIONAL_VERSIONS 3.8)
find_package(PythonInterp 3)
endif()
add_executable(jsontestrunner_exe add_executable(jsontestrunner_exe
main.cpp main.cpp
) )
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
add_compile_definitions( JSON_DLL ) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions( JSON_DLL )
else()
add_definitions(-DJSON_DLL)
endif()
endif() endif()
target_link_libraries(jsontestrunner_exe jsoncpp_lib) target_link_libraries(jsontestrunner_exe jsoncpp_lib)
@@ -19,18 +32,18 @@ if(PYTHONINTERP_FOUND)
# Run unit tests in post-build # Run unit tests in post-build
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?) # (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
add_custom_target(jsoncpp_readerwriter_tests add_custom_target(jsoncpp_readerwriter_tests
"${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data" "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
DEPENDS jsontestrunner_exe jsoncpp_test DEPENDS jsontestrunner_exe jsoncpp_test
) )
add_custom_target(jsoncpp_check DEPENDS jsoncpp_readerwriter_tests) 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 ## Create tests for dashboard submission, allows easy review of CI results https://my.cdash.org/index.php?project=jsoncpp
add_test(NAME jsoncpp_readerwriter add_test(NAME jsoncpp_readerwriter
COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data" COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
WORKING_DIRECTORY "${TEST_DIR}/data" WORKING_DIRECTORY "${TEST_DIR}/data"
) )
add_test(NAME jsoncpp_readerwriter_json_checker add_test(NAME jsoncpp_readerwriter_json_checker
COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" --with-json-checker $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data" COMMAND "${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" --with-json-checker $<TARGET_FILE:jsontestrunner_exe> "${TEST_DIR}/data"
WORKING_DIRECTORY "${TEST_DIR}/data" WORKING_DIRECTORY "${TEST_DIR}/data"
) )
endif() endif()

View File

@@ -15,7 +15,9 @@
#include <algorithm> // sort #include <algorithm> // sort
#include <cstdio> #include <cstdio>
#include <iostream>
#include <json/json.h> #include <json/json.h>
#include <memory>
#include <sstream> #include <sstream>
struct Options { struct Options {
@@ -55,10 +57,10 @@ static Json::String readInputTestFile(const char* path) {
if (!file) if (!file)
return ""; return "";
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
long const size = ftell(file); auto const size = ftell(file);
size_t const usize = static_cast<unsigned long>(size); auto const usize = static_cast<size_t>(size);
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
char* buffer = new char[size + 1]; auto buffer = new char[size + 1];
buffer[size] = 0; buffer[size] = 0;
Json::String text; Json::String text;
if (fread(buffer, 1, usize, file) == usize) if (fread(buffer, 1, usize, file) == usize)
@@ -68,8 +70,8 @@ static Json::String readInputTestFile(const char* path) {
return text; return text;
} }
static void static void printValueTree(FILE* fout, Json::Value& value,
printValueTree(FILE* fout, Json::Value& value, const Json::String& path = ".") { const Json::String& path = ".") {
if (value.hasComment(Json::commentBefore)) { if (value.hasComment(Json::commentBefore)) {
fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str()); fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
} }
@@ -109,7 +111,7 @@ printValueTree(FILE* fout, Json::Value& value, const Json::String& path = ".") {
Json::Value::Members members(value.getMemberNames()); Json::Value::Members members(value.getMemberNames());
std::sort(members.begin(), members.end()); std::sort(members.begin(), members.end());
Json::String suffix = *(path.end() - 1) == '.' ? "" : "."; Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
for (auto name : members) { for (const auto& name : members) {
printValueTree(fout, value[name], path + suffix + name); printValueTree(fout, value[name], path + suffix + name);
} }
} break; } break;
@@ -125,21 +127,46 @@ printValueTree(FILE* fout, Json::Value& value, const Json::String& path = ".") {
static int parseAndSaveValueTree(const Json::String& input, static int parseAndSaveValueTree(const Json::String& input,
const Json::String& actual, const Json::String& actual,
const Json::String& kind, const Json::String& kind,
const Json::Features& features, const Json::Features& features, bool parseOnly,
bool parseOnly, Json::Value* root, bool use_legacy) {
Json::Value* root) { if (!use_legacy) {
Json::Reader reader(features); Json::CharReaderBuilder builder;
bool parsingSuccessful =
reader.parse(input.data(), input.data() + input.size(), *root); builder.settings_["allowComments"] = features.allowComments_;
if (!parsingSuccessful) { builder.settings_["strictRoot"] = features.strictRoot_;
printf("Failed to parse %s file: \n%s\n", kind.c_str(), builder.settings_["allowDroppedNullPlaceholders"] =
reader.getFormattedErrorMessages().c_str()); features.allowDroppedNullPlaceholders_;
return 1; builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::String errors;
const bool parsingSuccessful =
reader->parse(input.data(), input.data() + input.size(), root, &errors);
if (!parsingSuccessful) {
std::cerr << "Failed to parse " << kind << " file: " << std::endl
<< errors << std::endl;
return 1;
}
// We may instead check the legacy implementation (to ensure it doesn't
// randomly get broken).
} else {
Json::Reader reader(features);
const bool parsingSuccessful =
reader.parse(input.data(), input.data() + input.size(), *root);
if (!parsingSuccessful) {
std::cerr << "Failed to parse " << kind << " file: " << std::endl
<< reader.getFormatedErrorMessages() << std::endl;
return 1;
}
} }
if (!parseOnly) { if (!parseOnly) {
FILE* factual = fopen(actual.c_str(), "wt"); FILE* factual = fopen(actual.c_str(), "wt");
if (!factual) { if (!factual) {
printf("Failed to create %s actual file.\n", kind.c_str()); std::cerr << "Failed to create '" << kind << "' actual file."
<< std::endl;
return 2; return 2;
} }
printValueTree(factual, *root); printValueTree(factual, *root);
@@ -173,7 +200,7 @@ static int rewriteValueTree(const Json::String& rewritePath,
*rewrite = write(root); *rewrite = write(root);
FILE* fout = fopen(rewritePath.c_str(), "wt"); FILE* fout = fopen(rewritePath.c_str(), "wt");
if (!fout) { if (!fout) {
printf("Failed to create rewrite file: %s\n", rewritePath.c_str()); std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
return 2; return 2;
} }
fprintf(fout, "%s\n", rewrite->c_str()); fprintf(fout, "%s\n", rewrite->c_str());
@@ -194,14 +221,15 @@ static Json::String removeSuffix(const Json::String& path,
static void printConfig() { static void printConfig() {
// Print the configuration used to compile JsonCpp // Print the configuration used to compile JsonCpp
#if defined(JSON_NO_INT64) #if defined(JSON_NO_INT64)
printf("JSON_NO_INT64=1\n"); std::cout << "JSON_NO_INT64=1" << std::endl;
#else #else
printf("JSON_NO_INT64=0\n"); std::cout << "JSON_NO_INT64=0" << std::endl;
#endif #endif
} }
static int printUsage(const char* argv[]) { static int printUsage(const char* argv[]) {
printf("Usage: %s [--strict] input-json-file", argv[0]); std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
<< std::endl;
return 3; return 3;
} }
@@ -231,7 +259,7 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) {
} else if (writerName == "BuiltStyledStreamWriter") { } else if (writerName == "BuiltStyledStreamWriter") {
opts->write = &useBuiltStyledStreamWriter; opts->write = &useBuiltStyledStreamWriter;
} else { } else {
printf("Unknown '--json-writer %s'\n", writerName.c_str()); std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
return 4; return 4;
} }
} }
@@ -241,19 +269,20 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) {
opts->path = argv[index]; opts->path = argv[index];
return 0; return 0;
} }
static int runTest(Options const& opts) {
static int runTest(Options const& opts, bool use_legacy) {
int exitCode = 0; int exitCode = 0;
Json::String input = readInputTestFile(opts.path.c_str()); Json::String input = readInputTestFile(opts.path.c_str());
if (input.empty()) { if (input.empty()) {
printf("Failed to read input or empty input: %s\n", opts.path.c_str()); std::cerr << "Invalid input file: " << opts.path << std::endl;
return 3; return 3;
} }
Json::String basePath = removeSuffix(opts.path, ".json"); Json::String basePath = removeSuffix(opts.path, ".json");
if (!opts.parseOnly && basePath.empty()) { if (!opts.parseOnly && basePath.empty()) {
printf("Bad input path. Path does not end with '.expected':\n%s\n", std::cerr << "Bad input path '" << opts.path
opts.path.c_str()); << "'. Must end with '.expected'" << std::endl;
return 3; return 3;
} }
@@ -263,34 +292,47 @@ static int runTest(Options const& opts) {
Json::Value root; Json::Value root;
exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features, exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
opts.parseOnly, &root); opts.parseOnly, &root, use_legacy);
if (exitCode || opts.parseOnly) { if (exitCode || opts.parseOnly) {
return exitCode; return exitCode;
} }
Json::String rewrite; Json::String rewrite;
exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite); exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
if (exitCode) { if (exitCode) {
return exitCode; return exitCode;
} }
Json::Value rewriteRoot; Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite", exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
opts.features, opts.parseOnly, &rewriteRoot); opts.features, opts.parseOnly, &rewriteRoot,
if (exitCode) { use_legacy);
return exitCode;
} return exitCode;
return 0;
} }
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
Options opts; Options opts;
try { try {
int exitCode = parseCommandLine(argc, argv, &opts); int exitCode = parseCommandLine(argc, argv, &opts);
if (exitCode != 0) { if (exitCode != 0) {
printf("Failed to parse command-line."); std::cerr << "Failed to parse command-line." << std::endl;
return exitCode; return exitCode;
} }
return runTest(opts);
const int modern_return_code = runTest(opts, false);
if (modern_return_code) {
return modern_return_code;
}
const std::string filename =
opts.path.substr(opts.path.find_last_of("\\/") + 1);
const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0);
if (should_run_legacy) {
return runTest(opts, true);
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
printf("Unhandled exception:\n%s\n", e.what()); std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
return 1; return 1;
} }
} }

View File

@@ -1,13 +1,7 @@
if( CMAKE_COMPILER_IS_GNUCXX ) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.1.2)
#Get compiler version.
execute_process( COMMAND ${CMAKE_CXX_COMPILER} -dumpversion
OUTPUT_VARIABLE GNUCXX_VERSION )
#-Werror=* was introduced -after- GCC 4.1.2 #-Werror=* was introduced -after- GCC 4.1.2
if( GNUCXX_VERSION VERSION_GREATER 4.1.2 ) add_compile_options("-Werror=strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=strict-aliasing") endif()
endif()
endif( CMAKE_COMPILER_IS_GNUCXX )
include(CheckIncludeFileCXX) include(CheckIncludeFileCXX)
include(CheckTypeSize) include(CheckTypeSize)
@@ -34,53 +28,63 @@ endif()
if(NOT (HAVE_CLOCALE AND HAVE_LCONV_SIZE AND HAVE_DECIMAL_POINT AND HAVE_LOCALECONV)) if(NOT (HAVE_CLOCALE AND HAVE_LCONV_SIZE AND HAVE_DECIMAL_POINT AND HAVE_LOCALECONV))
message(WARNING "Locale functionality is not supported") message(WARNING "Locale functionality is not supported")
add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions(JSONCPP_NO_LOCALE_SUPPORT)
else()
add_definitions(-DJSONCPP_NO_LOCALE_SUPPORT)
endif()
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/config.h
${JSONCPP_INCLUDE_DIR}/json/forwards.h ${JSONCPP_INCLUDE_DIR}/json/forwards.h
${JSONCPP_INCLUDE_DIR}/json/features.h ${JSONCPP_INCLUDE_DIR}/json/json_features.h
${JSONCPP_INCLUDE_DIR}/json/value.h ${JSONCPP_INCLUDE_DIR}/json/value.h
${JSONCPP_INCLUDE_DIR}/json/reader.h ${JSONCPP_INCLUDE_DIR}/json/reader.h
${JSONCPP_INCLUDE_DIR}/json/version.h
${JSONCPP_INCLUDE_DIR}/json/writer.h ${JSONCPP_INCLUDE_DIR}/json/writer.h
${JSONCPP_INCLUDE_DIR}/json/assertions.h ${JSONCPP_INCLUDE_DIR}/json/assertions.h
${PROJECT_BINARY_DIR}/include/json/version.h )
)
source_group( "Public API" FILES ${PUBLIC_HEADERS} ) source_group("Public API" FILES ${PUBLIC_HEADERS})
set(jsoncpp_sources set(jsoncpp_sources
json_tool.h json_tool.h
json_reader.cpp json_reader.cpp
json_valueiterator.inl json_valueiterator.inl
json_value.cpp json_value.cpp
json_writer.cpp json_writer.cpp
version.h.in) )
# Install instructions for this target # Install instructions for this target
if(JSONCPP_WITH_CMAKE_PACKAGE) if(JSONCPP_WITH_CMAKE_PACKAGE)
set(INSTALL_EXPORT EXPORT jsoncpp) set(INSTALL_EXPORT EXPORT jsoncpp)
else(JSONCPP_WITH_CMAKE_PACKAGE) else()
set(INSTALL_EXPORT) set(INSTALL_EXPORT)
endif() endif()
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
add_compile_definitions( JSON_DLL_BUILD ) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions(JSON_DLL_BUILD)
else()
add_definitions(-DJSON_DLL_BUILD)
endif()
endif() endif()
add_library(jsoncpp_lib ${PUBLIC_HEADERS} ${jsoncpp_sources}) 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
set_target_properties( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp OUTPUT_NAME jsoncpp
DEBUG_OUTPUT_NAME jsoncpp${DEBUG_LIBNAME_SUFFIX} ) VERSION ${JSONCPP_VERSION}
set_target_properties( jsoncpp_lib PROPERTIES POSITION_INDEPENDENT_CODE ON) SOVERSION ${JSONCPP_SOVERSION}
POSITION_INDEPENDENT_CODE ON
)
# Set library's runtime search path on OSX # Set library's runtime search path on OSX
if(APPLE) if(APPLE)
set_target_properties( jsoncpp_lib PROPERTIES INSTALL_RPATH "@loader_path/." ) set_target_properties(jsoncpp_lib PROPERTIES INSTALL_RPATH "@loader_path/.")
endif() endif()
# Specify compiler features required when compiling a given target. # Specify compiler features required when compiling a given target.
@@ -133,14 +137,16 @@ target_compile_features(jsoncpp_lib PUBLIC
cxx_variadic_templates # Variadic templates, as defined in N2242. cxx_variadic_templates # Variadic templates, as defined in N2242.
) )
install( TARGETS jsoncpp_lib ${INSTALL_EXPORT} install(TARGETS jsoncpp_lib ${INSTALL_EXPORT}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
if(NOT CMAKE_VERSION VERSION_LESS 2.8.11) if(NOT CMAKE_VERSION VERSION_LESS 2.8.11)
target_include_directories( jsoncpp_lib PUBLIC target_include_directories(jsoncpp_lib PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${JSONCPP_INCLUDE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>) $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/json>
)
endif() endif()

View File

@@ -10,8 +10,10 @@
#include <json/reader.h> #include <json/reader.h>
#include <json/value.h> #include <json/value.h>
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <iostream>
#include <istream> #include <istream>
#include <limits> #include <limits>
#include <memory> #include <memory>
@@ -51,9 +53,9 @@ static size_t const stackLimit_g =
namespace Json { namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
typedef std::unique_ptr<CharReader> CharReaderPtr; using CharReaderPtr = std::unique_ptr<CharReader>;
#else #else
typedef std::auto_ptr<CharReader> CharReaderPtr; using CharReaderPtr = std::auto_ptr<CharReader>;
#endif #endif
// Implementation of class Features // Implementation of class Features
@@ -76,25 +78,17 @@ Features Features::strictMode() {
// //////////////////////////////// // ////////////////////////////////
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
for (; begin < end; ++begin) return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
if (*begin == '\n' || *begin == '\r')
return true;
return false;
} }
// Class Reader // Class Reader
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Reader::Reader() Reader::Reader() : features_(Features::all()) {}
: errors_(), document_(), commentsBefore_(), features_(Features::all()) {}
Reader::Reader(const Features& features) Reader::Reader(const Features& features) : features_(features) {}
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
lastValue_(), commentsBefore_(), features_(features), collectComments_() {
}
bool Reader::parse(const std::string& document, bool Reader::parse(const std::string& document, Value& root,
Value& root,
bool collectComments) { bool collectComments) {
document_.assign(document.begin(), document.end()); document_.assign(document.begin(), document.end());
const char* begin = document_.c_str(); const char* begin = document_.c_str();
@@ -111,13 +105,11 @@ bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
// Since String is reference-counted, this at least does not // Since String is reference-counted, this at least does not
// create an extra copy. // create an extra copy.
String doc; String doc;
std::getline(is, doc, (char)EOF); std::getline(is, doc, static_cast<char> EOF);
return parse(doc.data(), doc.data() + doc.size(), root, collectComments); return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
} }
bool Reader::parse(const char* beginDoc, bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
const char* endDoc,
Value& root,
bool collectComments) { bool collectComments) {
if (!features_.allowComments_) { if (!features_.allowComments_) {
collectComments = false; collectComments = false;
@@ -311,7 +303,7 @@ bool Reader::readToken(Token& token) {
if (!ok) if (!ok)
token.type_ = tokenError; token.type_ = tokenError;
token.end_ = current_; token.end_ = current_;
return true; return ok;
} }
void Reader::skipSpaces() { void Reader::skipSpaces() {
@@ -324,7 +316,7 @@ void Reader::skipSpaces() {
} }
} }
bool Reader::match(Location pattern, int patternLength) { bool Reader::match(const Char* pattern, int patternLength) {
if (end_ - current_ < patternLength) if (end_ - current_ < patternLength)
return false; return false;
int index = patternLength; int index = patternLength;
@@ -377,8 +369,7 @@ String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
return normalized; return normalized;
} }
void Reader::addComment(Location begin, void Reader::addComment(Location begin, Location end,
Location end,
CommentPlacement placement) { CommentPlacement placement) {
assert(collectComments_); assert(collectComments_);
const String& normalized = normalizeEOL(begin, end); const String& normalized = normalizeEOL(begin, end);
@@ -416,7 +407,7 @@ bool Reader::readCppStyleComment() {
} }
void Reader::readNumber() { void Reader::readNumber() {
const char* p = current_; Location p = current_;
char c = '0'; // stopgap for already consumed character char c = '0'; // stopgap for already consumed character
// integral part // integral part
while (c >= '0' && c <= '9') while (c >= '0' && c <= '9')
@@ -471,7 +462,7 @@ bool Reader::readObject(Token& token) {
Value numberName; Value numberName;
if (!decodeNumber(tokenName, numberName)) if (!decodeNumber(tokenName, numberName))
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
name = String(numberName.asCString()); name = numberName.asString();
} else { } else {
break; break;
} }
@@ -636,7 +627,7 @@ bool Reader::decodeString(Token& token, String& decoded) {
Char c = *current++; Char c = *current++;
if (c == '"') if (c == '"')
break; break;
else if (c == '\\') { if (c == '\\') {
if (current == end) if (current == end)
return addError("Empty escape sequence in string", token, current); return addError("Empty escape sequence in string", token, current);
Char escape = *current++; Char escape = *current++;
@@ -681,10 +672,8 @@ bool Reader::decodeString(Token& token, String& decoded) {
return true; return true;
} }
bool Reader::decodeUnicodeCodePoint(Token& token, bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
Location& current, Location end, unsigned int& unicode) {
Location end,
unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false; return false;
@@ -708,8 +697,7 @@ bool Reader::decodeUnicodeCodePoint(Token& token,
return true; return true;
} }
bool Reader::decodeUnicodeEscapeSequence(Token& token, bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
Location& current,
Location end, Location end,
unsigned int& ret_unicode) { unsigned int& ret_unicode) {
if (end - current < 4) if (end - current < 4)
@@ -757,8 +745,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) {
return false; return false;
} }
bool Reader::addErrorAndRecover(const String& message, bool Reader::addErrorAndRecover(const String& message, Token& token,
Token& token,
TokenType skipUntilToken) { TokenType skipUntilToken) {
addError(message, token); addError(message, token);
return recoverFromError(skipUntilToken); return recoverFromError(skipUntilToken);
@@ -772,8 +759,7 @@ Reader::Char Reader::getNextChar() {
return *current_++; return *current_++;
} }
void Reader::getLocationLineAndColumn(Location location, void Reader::getLocationLineAndColumn(Location location, int& line,
int& line,
int& column) const { int& column) const {
Location current = begin_; Location current = begin_;
Location lastLineStart = current; Location lastLineStart = current;
@@ -849,8 +835,7 @@ bool Reader::pushError(const Value& value, const String& message) {
return true; return true;
} }
bool Reader::pushError(const Value& value, bool Reader::pushError(const Value& value, const String& message,
const String& message,
const Value& extra) { const Value& extra) {
ptrdiff_t const length = end_ - begin_; ptrdiff_t const length = end_ - begin_;
if (value.getOffsetStart() > length || value.getOffsetLimit() > length || if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
@@ -876,6 +861,7 @@ class OurFeatures {
public: public:
static OurFeatures all(); static OurFeatures all();
bool allowComments_; bool allowComments_;
bool allowTrailingCommas_;
bool strictRoot_; bool strictRoot_;
bool allowDroppedNullPlaceholders_; bool allowDroppedNullPlaceholders_;
bool allowNumericKeys_; bool allowNumericKeys_;
@@ -883,6 +869,7 @@ public:
bool failIfExtra_; bool failIfExtra_;
bool rejectDupKeys_; bool rejectDupKeys_;
bool allowSpecialFloats_; bool allowSpecialFloats_;
bool skipBom_;
size_t stackLimit_; size_t stackLimit_;
}; // OurFeatures }; // OurFeatures
@@ -895,24 +882,19 @@ OurFeatures OurFeatures::all() { return {}; }
// for implementing JSON reading. // for implementing JSON reading.
class OurReader { class OurReader {
public: public:
typedef char Char; using Char = char;
typedef const Char* Location; using Location = const Char*;
struct StructuredError { struct StructuredError {
ptrdiff_t offset_start; ptrdiff_t offset_start;
ptrdiff_t offset_limit; ptrdiff_t offset_limit;
String message; String message;
}; };
OurReader(OurFeatures const& features); explicit OurReader(OurFeatures const& features);
bool parse(const char* beginDoc, bool parse(const char* beginDoc, const char* endDoc, Value& root,
const char* endDoc,
Value& root,
bool collectComments = true); bool collectComments = true);
String getFormattedErrorMessages() const; String getFormattedErrorMessages() const;
std::vector<StructuredError> getStructuredErrors() const; std::vector<StructuredError> getStructuredErrors() const;
bool pushError(const Value& value, const String& message);
bool pushError(const Value& value, const String& message, const Value& extra);
bool good() const;
private: private:
OurReader(OurReader const&); // no impl OurReader(OurReader const&); // no impl
@@ -952,13 +934,14 @@ private:
Location extra_; Location extra_;
}; };
typedef std::deque<ErrorInfo> Errors; using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token); bool readToken(Token& token);
void skipSpaces(); void skipSpaces();
bool match(Location pattern, int patternLength); void skipBom(bool skipBom);
bool match(const Char* pattern, int patternLength);
bool readComment(); bool readComment();
bool readCStyleComment(); bool readCStyleComment(bool* containsNewLineResult);
bool readCppStyleComment(); bool readCppStyleComment();
bool readString(); bool readString();
bool readStringSingleQuote(); bool readStringSingleQuote();
@@ -972,24 +955,19 @@ private:
bool decodeString(Token& token, String& decoded); bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token); bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded); bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token, bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
Location& current,
Location end,
unsigned int& unicode); unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token, bool decodeUnicodeEscapeSequence(Token& token, Location& current,
Location& current, Location end, unsigned int& unicode);
Location end,
unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr); bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken); bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const String& message, bool addErrorAndRecover(const String& message, Token& token,
Token& token,
TokenType skipUntilToken); TokenType skipUntilToken);
void skipUntilSpace(); void skipUntilSpace();
Value& currentValue(); Value& currentValue();
Char getNextChar(); Char getNextChar();
void void getLocationLineAndColumn(Location location, int& line,
getLocationLineAndColumn(Location location, int& line, int& column) const; int& column) const;
String getLocationLineAndColumn(Location location) const; String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement); void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token); void skipCommentTokens(Token& token);
@@ -997,39 +975,33 @@ private:
static String normalizeEOL(Location begin, Location end); static String normalizeEOL(Location begin, Location end);
static bool containsNewLine(Location begin, Location end); static bool containsNewLine(Location begin, Location end);
typedef std::stack<Value*> Nodes; using Nodes = std::stack<Value*>;
Nodes nodes_;
Errors errors_; Nodes nodes_{};
String document_; Errors errors_{};
Location begin_; String document_{};
Location end_; Location begin_ = nullptr;
Location current_; Location end_ = nullptr;
Location lastValueEnd_; Location current_ = nullptr;
Value* lastValue_; Location lastValueEnd_ = nullptr;
String commentsBefore_; Value* lastValue_ = nullptr;
bool lastValueHasAComment_ = false;
String commentsBefore_{};
OurFeatures const features_; OurFeatures const features_;
bool collectComments_; bool collectComments_ = false;
}; // OurReader }; // OurReader
// complete copy of Read impl, for OurReader // complete copy of Read impl, for OurReader
bool OurReader::containsNewLine(OurReader::Location begin, bool OurReader::containsNewLine(OurReader::Location begin,
OurReader::Location end) { OurReader::Location end) {
for (; begin < end; ++begin) return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
if (*begin == '\n' || *begin == '\r')
return true;
return false;
} }
OurReader::OurReader(OurFeatures const& features) OurReader::OurReader(OurFeatures const& features) : features_(features) {}
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
lastValue_(), commentsBefore_(), features_(features), collectComments_() {
}
bool OurReader::parse(const char* beginDoc, bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
const char* endDoc,
Value& root,
bool collectComments) { bool collectComments) {
if (!features_.allowComments_) { if (!features_.allowComments_) {
collectComments = false; collectComments = false;
@@ -1047,16 +1019,15 @@ bool OurReader::parse(const char* beginDoc,
nodes_.pop(); nodes_.pop();
nodes_.push(&root); nodes_.push(&root);
// skip byte order mark if it exists at the beginning of the UTF-8 text.
skipBom(features_.skipBom_);
bool successful = readValue(); bool successful = readValue();
nodes_.pop(); nodes_.pop();
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
if (features_.failIfExtra_) { if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
if ((features_.strictRoot_ || token.type_ != tokenError) && addError("Extra non-whitespace after JSON value.", token);
token.type_ != tokenEndOfStream) { return false;
addError("Extra non-whitespace after JSON value.", token);
return false;
}
} }
if (collectComments_ && !commentsBefore_.empty()) if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter); root.setComment(commentsBefore_, commentAfter);
@@ -1161,6 +1132,7 @@ bool OurReader::readValue() {
if (collectComments_) { if (collectComments_) {
lastValueEnd_ = current_; lastValueEnd_ = current_;
lastValueHasAComment_ = false;
lastValue_ = &currentValue(); lastValue_ = &currentValue();
} }
@@ -1230,6 +1202,14 @@ bool OurReader::readToken(Token& token) {
ok = features_.allowSpecialFloats_ && match("nfinity", 7); ok = features_.allowSpecialFloats_ && match("nfinity", 7);
} }
break; break;
case '+':
if (readNumber(true)) {
token.type_ = tokenNumber;
} else {
token.type_ = tokenPosInf;
ok = features_.allowSpecialFloats_ && match("nfinity", 7);
}
break;
case 't': case 't':
token.type_ = tokenTrue; token.type_ = tokenTrue;
ok = match("rue", 3); ok = match("rue", 3);
@@ -1274,7 +1254,7 @@ bool OurReader::readToken(Token& token) {
if (!ok) if (!ok)
token.type_ = tokenError; token.type_ = tokenError;
token.end_ = current_; token.end_ = current_;
return true; return ok;
} }
void OurReader::skipSpaces() { void OurReader::skipSpaces() {
@@ -1287,7 +1267,17 @@ void OurReader::skipSpaces() {
} }
} }
bool OurReader::match(Location pattern, int patternLength) { 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) if (end_ - current_ < patternLength)
return false; return false;
int index = patternLength; int index = patternLength;
@@ -1299,21 +1289,32 @@ bool OurReader::match(Location pattern, int patternLength) {
} }
bool OurReader::readComment() { bool OurReader::readComment() {
Location commentBegin = current_ - 1; const Location commentBegin = current_ - 1;
Char c = getNextChar(); const Char c = getNextChar();
bool successful = false; bool successful = false;
if (c == '*') bool cStyleWithEmbeddedNewline = false;
successful = readCStyleComment();
else if (c == '/') const bool isCStyleComment = (c == '*');
const bool isCppStyleComment = (c == '/');
if (isCStyleComment) {
successful = readCStyleComment(&cStyleWithEmbeddedNewline);
} else if (isCppStyleComment) {
successful = readCppStyleComment(); successful = readCppStyleComment();
}
if (!successful) if (!successful)
return false; return false;
if (collectComments_) { if (collectComments_) {
CommentPlacement placement = commentBefore; CommentPlacement placement = commentBefore;
if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
if (c != '*' || !containsNewLine(commentBegin, current_)) if (!lastValueHasAComment_) {
placement = commentAfterOnSameLine; if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
placement = commentAfterOnSameLine;
lastValueHasAComment_ = true;
}
}
} }
addComment(commentBegin, current_, placement); addComment(commentBegin, current_, placement);
@@ -1341,8 +1342,7 @@ String OurReader::normalizeEOL(OurReader::Location begin,
return normalized; return normalized;
} }
void OurReader::addComment(Location begin, void OurReader::addComment(Location begin, Location end,
Location end,
CommentPlacement placement) { CommentPlacement placement) {
assert(collectComments_); assert(collectComments_);
const String& normalized = normalizeEOL(begin, end); const String& normalized = normalizeEOL(begin, end);
@@ -1354,12 +1354,17 @@ void OurReader::addComment(Location begin,
} }
} }
bool OurReader::readCStyleComment() { bool OurReader::readCStyleComment(bool* containsNewLineResult) {
*containsNewLineResult = false;
while ((current_ + 1) < end_) { while ((current_ + 1) < end_) {
Char c = getNextChar(); Char c = getNextChar();
if (c == '*' && *current_ == '/') if (c == '*' && *current_ == '/')
break; break;
if (c == '\n')
*containsNewLineResult = true;
} }
return getNextChar() == '/'; return getNextChar() == '/';
} }
@@ -1380,7 +1385,7 @@ bool OurReader::readCppStyleComment() {
} }
bool OurReader::readNumber(bool checkInf) { bool OurReader::readNumber(bool checkInf) {
const char* p = current_; Location p = current_;
if (checkInf && p != end_ && *p == 'I') { if (checkInf && p != end_ && *p == 'I') {
current_ = ++p; current_ = ++p;
return false; return false;
@@ -1441,7 +1446,9 @@ bool OurReader::readObject(Token& token) {
initialTokenOk = readToken(tokenName); initialTokenOk = readToken(tokenName);
if (!initialTokenOk) if (!initialTokenOk)
break; break;
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object if (tokenName.type_ == tokenObjectEnd &&
(name.empty() ||
features_.allowTrailingCommas_)) // empty object or trailing comma
return true; return true;
name.clear(); name.clear();
if (tokenName.type_ == tokenString) { if (tokenName.type_ == tokenString) {
@@ -1495,15 +1502,19 @@ bool OurReader::readArray(Token& token) {
Value init(arrayValue); Value init(arrayValue);
currentValue().swapPayload(init); currentValue().swapPayload(init);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
skipSpaces();
if (current_ != end_ && *current_ == ']') // empty array
{
Token endArray;
readToken(endArray);
return true;
}
int index = 0; int index = 0;
for (;;) { 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++]; Value& value = currentValue()[index++];
nodes_.push(&value); nodes_.push(&value);
bool ok = readValue(); bool ok = readValue();
@@ -1544,20 +1555,45 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
// larger than the maximum supported value of an integer then // larger than the maximum supported value of an integer then
// we decode the number as a double. // we decode the number as a double.
Location current = token.start_; Location current = token.start_;
bool isNegative = *current == '-'; const bool isNegative = *current == '-';
if (isNegative) if (isNegative) {
++current; ++current;
}
// TODO(issue #960): Change to constexpr // We assume we can represent the largest and smallest integer types as
static const auto positive_threshold = Value::maxLargestUInt / 10; // unsigned integers with separate sign. This is only true if they can fit
static const auto positive_last_digit = Value::maxLargestUInt % 10; // into an unsigned integer.
static const auto negative_threshold = static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
Value::LargestUInt(Value::minLargestInt) / 10; "Int must be smaller than UInt");
static const auto negative_last_digit =
Value::LargestUInt(Value::minLargestInt) % 10;
const auto threshold = isNegative ? negative_threshold : positive_threshold; // We need to convert minLargestInt into a positive number. The easiest way
const auto last_digit = // to do this conversion is to assume our "threshold" value of minLargestInt
// divided by 10 can fit in maxLargestInt when absolute valued. This should
// be a safe assumption.
static_assert(Value::minLargestInt <= -Value::maxLargestInt,
"The absolute value of minLargestInt must be greater than or "
"equal to maxLargestInt");
static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
"The absolute value of minLargestInt must be only 1 magnitude "
"larger than maxLargest Int");
static constexpr Value::LargestUInt positive_threshold =
Value::maxLargestUInt / 10;
static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
// For the negative values, we have to be more careful. Since typically
// -Value::minLargestInt will cause an overflow, we first divide by 10 and
// 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 auto negative_threshold =
Value::LargestUInt(-(Value::minLargestInt / 10));
static constexpr auto negative_last_digit =
Value::UInt(-(Value::minLargestInt % 10));
const Value::LargestUInt threshold =
isNegative ? negative_threshold : positive_threshold;
const Value::UInt max_last_digit =
isNegative ? negative_last_digit : positive_last_digit; isNegative ? negative_last_digit : positive_last_digit;
Value::LargestUInt value = 0; Value::LargestUInt value = 0;
@@ -1573,19 +1609,23 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
// b) this is the last digit, or // b) this is the last digit, or
// c) it's small enough to fit in that rounding delta, we're okay. // c) it's small enough to fit in that rounding delta, we're okay.
// Otherwise treat this number as a double to avoid overflow. // Otherwise treat this number as a double to avoid overflow.
if (value > threshold || current != token.end_ || digit > last_digit) { if (value > threshold || current != token.end_ ||
digit > max_last_digit) {
return decodeDouble(token, decoded); return decodeDouble(token, decoded);
} }
} }
value = value * 10 + digit; value = value * 10 + digit;
} }
if (isNegative) if (isNegative) {
decoded = -Value::LargestInt(value); // We use the same magnitude assumption here, just in case.
else if (value <= Value::LargestUInt(Value::maxLargestInt)) 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); decoded = Value::LargestInt(value);
else } else {
decoded = value; decoded = value;
}
return true; return true;
} }
@@ -1602,37 +1642,12 @@ bool OurReader::decodeDouble(Token& token) {
bool OurReader::decodeDouble(Token& token, Value& decoded) { bool OurReader::decodeDouble(Token& token, Value& decoded) {
double value = 0; double value = 0;
const int bufferSize = 32; const String buffer(token.start_, token.end_);
int count; IStringStream is(buffer);
ptrdiff_t const length = token.end_ - token.start_; if (!(is >> value)) {
// Sanity check to avoid buffer overflow exploits.
if (length < 0) {
return addError("Unable to parse token length", token);
}
auto const ulength = static_cast<size_t>(length);
// Avoid using a string constant for the format control string given to
// sscanf, as this can cause hard to debug crashes on OS X. See here for more
// info:
//
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
char format[] = "%lf";
if (length <= bufferSize) {
Char buffer[bufferSize + 1];
memcpy(buffer, token.start_, ulength);
buffer[length] = 0;
fixNumericLocaleInput(buffer, buffer + length);
count = sscanf(buffer, format, &value);
} else {
String buffer(token.start_, token.end_);
count = sscanf(buffer.c_str(), format, &value);
}
if (count != 1)
return addError( return addError(
"'" + String(token.start_, token.end_) + "' is not a number.", token); "'" + String(token.start_, token.end_) + "' is not a number.", token);
}
decoded = value; decoded = value;
return true; return true;
} }
@@ -1656,7 +1671,7 @@ bool OurReader::decodeString(Token& token, String& decoded) {
Char c = *current++; Char c = *current++;
if (c == '"') if (c == '"')
break; break;
else if (c == '\\') { if (c == '\\') {
if (current == end) if (current == end)
return addError("Empty escape sequence in string", token, current); return addError("Empty escape sequence in string", token, current);
Char escape = *current++; Char escape = *current++;
@@ -1701,10 +1716,8 @@ bool OurReader::decodeString(Token& token, String& decoded) {
return true; return true;
} }
bool OurReader::decodeUnicodeCodePoint(Token& token, bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
Location& current, Location end, unsigned int& unicode) {
Location end,
unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false; return false;
@@ -1728,8 +1741,7 @@ bool OurReader::decodeUnicodeCodePoint(Token& token,
return true; return true;
} }
bool OurReader::decodeUnicodeEscapeSequence(Token& token, bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
Location& current,
Location end, Location end,
unsigned int& ret_unicode) { unsigned int& ret_unicode) {
if (end - current < 4) if (end - current < 4)
@@ -1777,8 +1789,7 @@ bool OurReader::recoverFromError(TokenType skipUntilToken) {
return false; return false;
} }
bool OurReader::addErrorAndRecover(const String& message, bool OurReader::addErrorAndRecover(const String& message, Token& token,
Token& token,
TokenType skipUntilToken) { TokenType skipUntilToken) {
addError(message, token); addError(message, token);
return recoverFromError(skipUntilToken); return recoverFromError(skipUntilToken);
@@ -1792,8 +1803,7 @@ OurReader::Char OurReader::getNextChar() {
return *current_++; return *current_++;
} }
void OurReader::getLocationLineAndColumn(Location location, void OurReader::getLocationLineAndColumn(Location location, int& line,
int& line,
int& column) const { int& column) const {
Location current = begin_; Location current = begin_;
Location lastLineStart = current; Location lastLineStart = current;
@@ -1848,43 +1858,6 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
return allErrors; return allErrors;
} }
bool OurReader::pushError(const Value& value, const String& message) {
ptrdiff_t length = end_ - begin_;
if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = begin_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = nullptr;
errors_.push_back(info);
return true;
}
bool OurReader::pushError(const Value& value,
const String& message,
const Value& extra) {
ptrdiff_t length = end_ - begin_;
if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
extra.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = begin_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = begin_ + extra.getOffsetStart();
errors_.push_back(info);
return true;
}
bool OurReader::good() const { return errors_.empty(); }
class OurCharReader : public CharReader { class OurCharReader : public CharReader {
bool const collectComments_; bool const collectComments_;
OurReader reader_; OurReader reader_;
@@ -1892,9 +1865,7 @@ class OurCharReader : public CharReader {
public: public:
OurCharReader(bool collectComments, OurFeatures const& features) OurCharReader(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {} : collectComments_(collectComments), reader_(features) {}
bool parse(char const* beginDoc, bool parse(char const* beginDoc, char const* endDoc, Value* root,
char const* endDoc,
Value* root,
String* errs) override { String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) { if (errs) {
@@ -1910,6 +1881,7 @@ CharReader* CharReaderBuilder::newCharReader() const {
bool collectComments = settings_["collectComments"].asBool(); bool collectComments = settings_["collectComments"].asBool();
OurFeatures features = OurFeatures::all(); OurFeatures features = OurFeatures::all();
features.allowComments_ = settings_["allowComments"].asBool(); features.allowComments_ = settings_["allowComments"].asBool();
features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
features.strictRoot_ = settings_["strictRoot"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool();
features.allowDroppedNullPlaceholders_ = features.allowDroppedNullPlaceholders_ =
settings_["allowDroppedNullPlaceholders"].asBool(); settings_["allowDroppedNullPlaceholders"].asBool();
@@ -1922,38 +1894,37 @@ CharReader* CharReaderBuilder::newCharReader() const {
features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.failIfExtra_ = settings_["failIfExtra"].asBool();
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
features.skipBom_ = settings_["skipBom"].asBool();
return new OurCharReader(collectComments, features); 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 { bool CharReaderBuilder::validate(Json::Value* invalid) const {
Json::Value my_invalid; static const auto& valid_keys = *new std::set<String>{
if (!invalid) "collectComments",
invalid = &my_invalid; // so we do not need to test for NULL "allowComments",
Json::Value& inv = *invalid; "allowTrailingCommas",
std::set<String> valid_keys; "strictRoot",
getValidReaderKeys(&valid_keys); "allowDroppedNullPlaceholders",
Value::Members keys = settings_.getMemberNames(); "allowNumericKeys",
size_t n = keys.size(); "allowSingleQuotes",
for (size_t i = 0; i < n; ++i) { "stackLimit",
String const& key = keys[i]; "failIfExtra",
if (valid_keys.find(key) == valid_keys.end()) { "rejectDupKeys",
inv[key] = settings_[key]; "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) { Value& CharReaderBuilder::operator[](const String& key) {
return settings_[key]; return settings_[key];
} }
@@ -1961,6 +1932,7 @@ Value& CharReaderBuilder::operator[](const String& key) {
void CharReaderBuilder::strictMode(Json::Value* settings) { void CharReaderBuilder::strictMode(Json::Value* settings) {
//! [CharReaderBuilderStrictMode] //! [CharReaderBuilderStrictMode]
(*settings)["allowComments"] = false; (*settings)["allowComments"] = false;
(*settings)["allowTrailingCommas"] = false;
(*settings)["strictRoot"] = true; (*settings)["strictRoot"] = true;
(*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false; (*settings)["allowNumericKeys"] = false;
@@ -1969,6 +1941,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings) {
(*settings)["failIfExtra"] = true; (*settings)["failIfExtra"] = true;
(*settings)["rejectDupKeys"] = true; (*settings)["rejectDupKeys"] = true;
(*settings)["allowSpecialFloats"] = false; (*settings)["allowSpecialFloats"] = false;
(*settings)["skipBom"] = true;
//! [CharReaderBuilderStrictMode] //! [CharReaderBuilderStrictMode]
} }
// static // static
@@ -1976,6 +1949,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
//! [CharReaderBuilderDefaults] //! [CharReaderBuilderDefaults]
(*settings)["collectComments"] = true; (*settings)["collectComments"] = true;
(*settings)["allowComments"] = true; (*settings)["allowComments"] = true;
(*settings)["allowTrailingCommas"] = true;
(*settings)["strictRoot"] = false; (*settings)["strictRoot"] = false;
(*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false; (*settings)["allowNumericKeys"] = false;
@@ -1984,15 +1958,14 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
(*settings)["failIfExtra"] = false; (*settings)["failIfExtra"] = false;
(*settings)["rejectDupKeys"] = false; (*settings)["rejectDupKeys"] = false;
(*settings)["allowSpecialFloats"] = false; (*settings)["allowSpecialFloats"] = false;
(*settings)["skipBom"] = true;
//! [CharReaderBuilderDefaults] //! [CharReaderBuilderDefaults]
} }
////////////////////////////////// //////////////////////////////////
// global functions // global functions
bool parseFromStream(CharReader::Factory const& fact, bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
IStream& sin,
Value* root,
String* errs) { String* errs) {
OStringStream ssin; OStringStream ssin;
ssin << sin.rdbuf(); ssin << sin.rdbuf();

View File

@@ -71,7 +71,7 @@ enum {
}; };
// Defines a char buffer for use with uintToString(). // Defines a char buffer for use with uintToString().
typedef char UIntToStringBuffer[uintToStringBufferSize]; using UIntToStringBuffer = char[uintToStringBufferSize];
/** Converts an unsigned integer to string. /** Converts an unsigned integer to string.
* @param value Unsigned integer to convert to string * @param value Unsigned integer to convert to string

View File

@@ -8,24 +8,20 @@
#include <json/value.h> #include <json/value.h>
#include <json/writer.h> #include <json/writer.h>
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#include <algorithm>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstddef>
#include <cstring> #include <cstring>
#include <iostream>
#include <sstream> #include <sstream>
#include <utility> #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 // Provide implementation equivalent of std::snprintf for older _MSC compilers
#if defined(_MSC_VER) && _MSC_VER < 1900 #if defined(_MSC_VER) && _MSC_VER < 1900
#include <stdarg.h> #include <stdarg.h>
static int msvc_pre1900_c99_vsnprintf(char* outBuf, static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size,
size_t size, const char* format, va_list ap) {
const char* format,
va_list ap) {
int count = -1; int count = -1;
if (size != 0) if (size != 0)
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
@@ -34,10 +30,8 @@ static int msvc_pre1900_c99_vsnprintf(char* outBuf,
return count; return count;
} }
int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
size_t size, const char* format, ...) {
const char* format,
...) {
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
@@ -88,31 +82,12 @@ Value const& Value::null = Value::nullSingleton();
Value const& Value::nullRef = Value::nullSingleton(); Value const& Value::nullRef = Value::nullSingleton();
#endif #endif
const Int Value::minInt = Int(~(UInt(-1) / 2));
const Int Value::maxInt = Int(UInt(-1) / 2);
const UInt Value::maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
const UInt64 Value::maxUInt64 = UInt64(-1);
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static const double maxUInt64AsDouble = 18446744073709551615.0;
#endif // defined(JSON_HAS_INT64)
const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
const UInt Value::defaultRealPrecision = 17;
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U> template <typename T, typename U>
static inline bool InRange(double d, T min, U max) { static inline bool InRange(double d, T min, U max) {
// The casts can lose precision, but we are looking only for // The casts can lose precision, but we are looking only for
// an approximate range. Might fail on edge cases though. ~cdunn // an approximate range. Might fail on edge cases though. ~cdunn
// return d >= static_cast<double>(min) && d <= static_cast<double>(max); return d >= static_cast<double>(min) && d <= static_cast<double>(max);
return d >= min && d <= max;
} }
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double integerToDouble(Json::UInt64 value) { static inline double integerToDouble(Json::UInt64 value) {
@@ -143,7 +118,7 @@ static inline char* duplicateStringValue(const char* value, size_t length) {
if (length >= static_cast<size_t>(Value::maxInt)) if (length >= static_cast<size_t>(Value::maxInt))
length = Value::maxInt - 1; length = Value::maxInt - 1;
char* newString = static_cast<char*>(malloc(length + 1)); auto newString = static_cast<char*>(malloc(length + 1));
if (newString == nullptr) { if (newString == nullptr) {
throwRuntimeError("in Json::Value::duplicateStringValue(): " throwRuntimeError("in Json::Value::duplicateStringValue(): "
"Failed to allocate string value buffer"); "Failed to allocate string value buffer");
@@ -163,8 +138,8 @@ static inline char* duplicateAndPrefixStringValue(const char* value,
sizeof(unsigned) - 1U, sizeof(unsigned) - 1U,
"in Json::Value::duplicateAndPrefixStringValue(): " "in Json::Value::duplicateAndPrefixStringValue(): "
"length too big for prefixing"); "length too big for prefixing");
unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; size_t actualLength = sizeof(length) + length + 1;
char* newString = static_cast<char*>(malloc(actualLength)); auto newString = static_cast<char*>(malloc(actualLength));
if (newString == nullptr) { if (newString == nullptr) {
throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
"Failed to allocate string value buffer"); "Failed to allocate string value buffer");
@@ -175,10 +150,8 @@ static inline char* duplicateAndPrefixStringValue(const char* value,
0; // to avoid buffer over-run accidents by users later 0; // to avoid buffer over-run accidents by users later
return newString; return newString;
} }
inline static void decodePrefixedString(bool isPrefixed, inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
char const* prefixed, unsigned* length, char const** value) {
unsigned* length,
char const** value) {
if (!isPrefixed) { if (!isPrefixed) {
*length = static_cast<unsigned>(strlen(prefixed)); *length = static_cast<unsigned>(strlen(prefixed));
*value = prefixed; *value = prefixed;
@@ -228,19 +201,25 @@ namespace Json {
#if JSON_USE_EXCEPTION #if JSON_USE_EXCEPTION
Exception::Exception(String msg) : msg_(std::move(msg)) {} Exception::Exception(String msg) : msg_(std::move(msg)) {}
Exception::~Exception() JSONCPP_NOEXCEPT {} Exception::~Exception() noexcept = default;
char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); } char const* Exception::what() const noexcept { return msg_.c_str(); }
RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
LogicError::LogicError(String const& msg) : Exception(msg) {} LogicError::LogicError(String const& msg) : Exception(msg) {}
[[noreturn]] void throwRuntimeError(String const& msg) { JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
throw RuntimeError(msg); throw RuntimeError(msg);
} }
[[noreturn]] void throwLogicError(String const& msg) { JSONCPP_NORETURN void throwLogicError(String const& msg) {
throw LogicError(msg); throw LogicError(msg);
} }
#else // !JSON_USE_EXCEPTION #else // !JSON_USE_EXCEPTION
[[noreturn]] void throwRuntimeError(String const& msg) { abort(); } JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
[[noreturn]] void throwLogicError(String const& msg) { abort(); } std::cerr << msg << std::endl;
abort();
}
JSONCPP_NORETURN void throwLogicError(String const& msg) {
std::cerr << msg << std::endl;
abort();
}
#endif #endif
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
@@ -256,8 +235,7 @@ LogicError::LogicError(String const& msg) : Exception(msg) {}
Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
Value::CZString::CZString(char const* str, Value::CZString::CZString(char const* str, unsigned length,
unsigned length,
DuplicationPolicy allocate) DuplicationPolicy allocate)
: cstr_(str) { : cstr_(str) {
// allocate != duplicate // allocate != duplicate
@@ -289,7 +267,7 @@ Value::CZString::CZString(CZString&& other)
Value::CZString::~CZString() { Value::CZString::~CZString() {
if (cstr_ && storage_.policy_ == duplicate) { if (cstr_ && storage_.policy_ == duplicate) {
releaseStringValue(const_cast<char*>(cstr_), releaseStringValue(const_cast<char*>(cstr_),
storage_.length_ + 1u); // +1 for null terminating storage_.length_ + 1U); // +1 for null terminating
// character for sake of // character for sake of
// completeness but not actually // completeness but not actually
// necessary // necessary
@@ -445,14 +423,6 @@ Value::Value(const StaticString& value) {
value_.string_ = const_cast<char*>(value.c_str()); 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) { Value::Value(bool value) {
initBasic(booleanValue); initBasic(booleanValue);
value_.bool_ = value; value_.bool_ = value;
@@ -520,7 +490,7 @@ int Value::compare(const Value& other) const {
bool Value::operator<(const Value& other) const { bool Value::operator<(const Value& other) const {
int typeDelta = type() - other.type(); int typeDelta = type() - other.type();
if (typeDelta) if (typeDelta)
return typeDelta < 0 ? true : false; return typeDelta < 0;
switch (type()) { switch (type()) {
case nullValue: case nullValue:
return false; return false;
@@ -534,10 +504,7 @@ bool Value::operator<(const Value& other) const {
return value_.bool_ < other.value_.bool_; return value_.bool_ < other.value_.bool_;
case stringValue: { case stringValue: {
if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
if (other.value_.string_) return other.value_.string_ != nullptr;
return true;
else
return false;
} }
unsigned this_len; unsigned this_len;
unsigned other_len; unsigned other_len;
@@ -558,9 +525,10 @@ bool Value::operator<(const Value& other) const {
} }
case arrayValue: case arrayValue:
case objectValue: { case objectValue: {
int delta = int(value_.map_->size() - other.value_.map_->size()); auto thisSize = value_.map_->size();
if (delta) auto otherSize = other.value_.map_->size();
return delta < 0; if (thisSize != otherSize)
return thisSize < otherSize;
return (*value_.map_) < (*other.value_.map_); return (*value_.map_) < (*other.value_.map_);
} }
default: default:
@@ -683,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 { Value::Int Value::asInt() const {
switch (type()) { switch (type()) {
case intValue: case intValue:
@@ -835,7 +794,7 @@ float Value::asFloat() const {
case nullValue: case nullValue:
return 0.0; return 0.0;
case booleanValue: case booleanValue:
return value_.bool_ ? 1.0f : 0.0f; return value_.bool_ ? 1.0F : 0.0F;
default: default:
break; break;
} }
@@ -849,9 +808,9 @@ bool Value::asBool() const {
case nullValue: case nullValue:
return false; return false;
case intValue: case intValue:
return value_.int_ ? true : false; return value_.int_ != 0;
case uintValue: case uintValue:
return value_.uint_ ? true : false; return value_.uint_ != 0;
case realValue: { case realValue: {
// According to JavaScript language zero or NaN is regarded as false // According to JavaScript language zero or NaN is regarded as false
const auto value_classification = std::fpclassify(value_.real_); const auto value_classification = std::fpclassify(value_.real_);
@@ -867,7 +826,7 @@ bool Value::isConvertibleTo(ValueType other) const {
switch (other) { switch (other) {
case nullValue: case nullValue:
return (isNumeric() && asDouble() == 0.0) || return (isNumeric() && asDouble() == 0.0) ||
(type() == booleanValue && value_.bool_ == false) || (type() == booleanValue && !value_.bool_) ||
(type() == stringValue && asString().empty()) || (type() == stringValue && asString().empty()) ||
(type() == arrayValue && value_.map_->empty()) || (type() == arrayValue && value_.map_->empty()) ||
(type() == objectValue && value_.map_->empty()) || (type() == objectValue && value_.map_->empty()) ||
@@ -922,9 +881,8 @@ ArrayIndex Value::size() const {
bool Value::empty() const { bool Value::empty() const {
if (isNull() || isArray() || isObject()) if (isNull() || isArray() || isObject())
return size() == 0u; return size() == 0U;
else return false;
return false;
} }
Value::operator bool() const { return !isNull(); } Value::operator bool() const { return !isNull(); }
@@ -1164,26 +1122,36 @@ Value& Value::operator[](const StaticString& key) {
return resolveReference(key.c_str()); return resolveReference(key.c_str());
} }
#ifdef JSON_USE_CPPTL Value& Value::append(const Value& value) { return append(Value(value)); }
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 (*this)[size()] = value; }
Value& Value::append(Value&& value) { Value& Value::append(Value&& value) {
return (*this)[size()] = std::move(value); JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
"in Json::Value::append: requires arrayValue");
if (type() == nullValue) {
*this = Value(arrayValue);
}
return this->value_.map_->emplace(size(), std::move(value)).first->second;
} }
Value Value::get(char const* begin, bool Value::insert(ArrayIndex index, const Value& newValue) {
char const* end, 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;
}
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,
Value const& defaultValue) const { Value const& defaultValue) const {
Value const* found = find(begin, end); Value const* found = find(begin, end);
return !found ? defaultValue : *found; return !found ? defaultValue : *found;
@@ -1250,13 +1218,6 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) {
return true; 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 { bool Value::isMember(char const* begin, char const* end) const {
Value const* value = find(begin, end); Value const* value = find(begin, end);
return nullptr != value; return nullptr != value;
@@ -1268,12 +1229,6 @@ bool Value::isMember(String const& key) const {
return isMember(key.data(), key.data() + key.length()); 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 { Value::Members Value::getMemberNames() const {
JSON_ASSERT_MESSAGE( JSON_ASSERT_MESSAGE(
type() == nullValue || type() == objectValue, type() == nullValue || type() == objectValue,
@@ -1289,31 +1244,6 @@ Value::Members Value::getMemberNames() const {
} }
return members; 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) { static bool IsIntegral(double d) {
double integral_part; double integral_part;
@@ -1469,7 +1399,10 @@ void Value::Comments::set(CommentPlacement slot, String comment) {
if (!ptr_) { if (!ptr_) {
ptr_ = std::unique_ptr<Array>(new Array()); ptr_ = std::unique_ptr<Array>(new Array());
} }
(*ptr_)[slot] = std::move(comment); // check comments array boundry.
if (slot < CommentPlacement::numberOfCommentPlacement) {
(*ptr_)[slot] = std::move(comment);
}
} }
void Value::setComment(String comment, CommentPlacement placement) { void Value::setComment(String comment, CommentPlacement placement) {
@@ -1565,25 +1498,20 @@ Value::iterator Value::end() {
// class PathArgument // class PathArgument
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
PathArgument::PathArgument() : key_() {} PathArgument::PathArgument() = default;
PathArgument::PathArgument(ArrayIndex index) PathArgument::PathArgument(ArrayIndex index)
: key_(), index_(index), kind_(kindIndex) {} : index_(index), kind_(kindIndex) {}
PathArgument::PathArgument(const char* key) PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
: key_(key), index_(), kind_(kindKey) {}
PathArgument::PathArgument(const String& key) PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}
: key_(key.c_str()), index_(), kind_(kindKey) {}
// class Path // class Path
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Path::Path(const String& path, Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2,
const PathArgument& a1, const PathArgument& a3, const PathArgument& a4,
const PathArgument& a2,
const PathArgument& a3,
const PathArgument& a4,
const PathArgument& a5) { const PathArgument& a5) {
InArgs in; InArgs in;
in.reserve(5); in.reserve(5);
@@ -1626,8 +1554,7 @@ void Path::makePath(const String& path, const InArgs& in) {
} }
} }
void Path::addPathInArg(const String& /*path*/, void Path::addPathInArg(const String& /*path*/, const InArgs& in,
const InArgs& in,
InArgs::const_iterator& itInArg, InArgs::const_iterator& itInArg,
PathArgument::Kind kind) { PathArgument::Kind kind) {
if (itInArg == in.end()) { if (itInArg == in.end()) {

View File

@@ -21,7 +21,8 @@ ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current) const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {} : current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() const { return current_->second; } Value& ValueIteratorBase::deref() { return current_->second; }
const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; } void ValueIteratorBase::increment() { ++current_; }
@@ -29,9 +30,6 @@ void ValueIteratorBase::decrement() { --current_; }
ValueIteratorBase::difference_type ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const { 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 // Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default // constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance // std::map::iterator. As begin() and end() are two instance
@@ -52,7 +50,6 @@ ValueIteratorBase::computeDistance(const SelfType& other) const {
++myDistance; ++myDistance;
} }
return myDistance; return myDistance;
#endif
} }
bool ValueIteratorBase::isEqual(const SelfType& other) const { bool ValueIteratorBase::isEqual(const SelfType& other) const {

View File

@@ -7,7 +7,9 @@
#include "json_tool.h" #include "json_tool.h"
#include <json/writer.h> #include <json/writer.h>
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#include <algorithm>
#include <cassert> #include <cassert>
#include <cctype>
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
@@ -84,9 +86,9 @@
namespace Json { namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
typedef std::unique_ptr<StreamWriter> StreamWriterPtr; using StreamWriterPtr = std::unique_ptr<StreamWriter>;
#else #else
typedef std::auto_ptr<StreamWriter> StreamWriterPtr; using StreamWriterPtr = std::auto_ptr<StreamWriter>;
#endif #endif
String valueToString(LargestInt value) { String valueToString(LargestInt value) {
@@ -122,10 +124,8 @@ String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
#endif // # if defined(JSON_HAS_INT64) #endif // # if defined(JSON_HAS_INT64)
namespace { namespace {
String valueToString(double value, String valueToString(double value, bool useSpecialFloats,
bool useSpecialFloats, unsigned int precision, PrecisionType precisionType) {
unsigned int precision,
PrecisionType precisionType) {
// Print into the buffer. We need not request the alternative representation // Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distinguish the // that always has a decimal point because JSON doesn't distinguish the
// concepts of reals and integers. // concepts of reals and integers.
@@ -168,8 +168,7 @@ String valueToString(double value,
} }
} // namespace } // namespace
String valueToString(double value, String valueToString(double value, unsigned int precision,
unsigned int precision,
PrecisionType precisionType) { PrecisionType precisionType) {
return valueToString(value, false, precision, precisionType); return valueToString(value, false, precision, precisionType);
} }
@@ -179,13 +178,9 @@ String valueToString(bool value) { return value ? "true" : "false"; }
static bool isAnyCharRequiredQuoting(char const* s, size_t n) { static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
assert(s || !n); assert(s || !n);
char const* const end = s + n; return std::any_of(s, s + n, [](unsigned char c) {
for (char const* cur = s; cur < end; ++cur) { return c == '\\' || c == '"' || !std::isprint(c);
if (*cur == '\\' || *cur == '\"' || *cur < ' ' || });
static_cast<unsigned char>(*cur) < 0x80)
return true;
}
return false;
} }
static unsigned int utf8ToCodepoint(const char*& s, const char* e) { static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
@@ -267,7 +262,16 @@ static String toHex16Bit(unsigned int x) {
return result; return result;
} }
static String valueToQuotedStringN(const char* value, unsigned length) { 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) if (value == nullptr)
return ""; return "";
@@ -313,21 +317,28 @@ static String valueToQuotedStringN(const char* value, unsigned length) {
// Should add a flag to allow this compatibility mode and prevent this // Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring. // sequence from occurring.
default: { default: {
unsigned int cp = utf8ToCodepoint(c, end); if (emitUTF8) {
// don't escape non-control characters unsigned codepoint = static_cast<unsigned char>(*c);
// (short escape sequence are applied above) if (codepoint < 0x20) {
if (cp < 0x80 && cp >= 0x20) appendHex(result, codepoint);
result += static_cast<char>(cp); } else {
else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane appendRaw(result, codepoint);
result += "\\u"; }
result += toHex16Bit(cp); } else {
} else { // codepoint is not in Basic Multilingual Plane unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
// convert to surrogate pair first if (codepoint < 0x20) {
cp -= 0x10000; appendHex(result, codepoint);
result += "\\u"; } else if (codepoint < 0x80) {
result += toHex16Bit((cp >> 10) + 0xD800); appendRaw(result, codepoint);
result += "\\u"; } else if (codepoint < 0x10000) {
result += toHex16Bit((cp & 0x3FF) + 0xDC00); // 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; } break;
} }
@@ -864,13 +875,10 @@ struct CommentStyle {
}; };
struct BuiltStyledStreamWriter : public StreamWriter { struct BuiltStyledStreamWriter : public StreamWriter {
BuiltStyledStreamWriter(String indentation, BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
CommentStyle::Enum cs, String colonSymbol, String nullSymbol,
String colonSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
String nullSymbol, bool emitUTF8, unsigned int precision,
String endingLineFeedSymbol,
bool useSpecialFloats,
unsigned int precision,
PrecisionType precisionType); PrecisionType precisionType);
int write(Value const& root, OStream* sout) override; int write(Value const& root, OStream* sout) override;
@@ -887,7 +895,7 @@ private:
void writeCommentAfterValueOnSameLine(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root);
static bool hasCommentForValue(const Value& value); static bool hasCommentForValue(const Value& value);
typedef std::vector<String> ChildValues; using ChildValues = std::vector<String>;
ChildValues childValues_; ChildValues childValues_;
String indentString_; String indentString_;
@@ -900,23 +908,20 @@ private:
bool addChildValues_ : 1; bool addChildValues_ : 1;
bool indented_ : 1; bool indented_ : 1;
bool useSpecialFloats_ : 1; bool useSpecialFloats_ : 1;
bool emitUTF8_ : 1;
unsigned int precision_; unsigned int precision_;
PrecisionType precisionType_; PrecisionType precisionType_;
}; };
BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation, BuiltStyledStreamWriter::BuiltStyledStreamWriter(
CommentStyle::Enum cs, String indentation, CommentStyle::Enum cs, String colonSymbol,
String colonSymbol, String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
String nullSymbol, bool emitUTF8, unsigned int precision, PrecisionType precisionType)
String endingLineFeedSymbol,
bool useSpecialFloats,
unsigned int precision,
PrecisionType precisionType)
: rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
addChildValues_(false), indented_(false), addChildValues_(false), indented_(false),
useSpecialFloats_(useSpecialFloats), precision_(precision), useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
precisionType_(precisionType) {} precision_(precision), precisionType_(precisionType) {}
int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
sout_ = sout; sout_ = sout;
addChildValues_ = false; addChildValues_ = false;
@@ -953,7 +958,8 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
char const* end; char const* end;
bool ok = value.getString(&str, &end); bool ok = value.getString(&str, &end);
if (ok) if (ok)
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str))); pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
emitUTF8_));
else else
pushValue(""); pushValue("");
break; break;
@@ -977,7 +983,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
Value const& childValue = value[name]; Value const& childValue = value[name];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedStringN( writeWithIndent(valueToQuotedStringN(
name.data(), static_cast<unsigned>(name.length()))); name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
*sout_ << colonSymbol_; *sout_ << colonSymbol_;
writeValue(childValue); writeValue(childValue);
if (++it == members.end()) { if (++it == members.end()) {
@@ -1153,12 +1159,13 @@ StreamWriter::Factory::~Factory() = default;
StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
StreamWriterBuilder::~StreamWriterBuilder() = default; StreamWriterBuilder::~StreamWriterBuilder() = default;
StreamWriter* StreamWriterBuilder::newStreamWriter() const { StreamWriter* StreamWriterBuilder::newStreamWriter() const {
String indentation = settings_["indentation"].asString(); const String indentation = settings_["indentation"].asString();
String cs_str = settings_["commentStyle"].asString(); const String cs_str = settings_["commentStyle"].asString();
String pt_str = settings_["precisionType"].asString(); const String pt_str = settings_["precisionType"].asString();
bool eyc = settings_["enableYAMLCompatibility"].asBool(); const bool eyc = settings_["enableYAMLCompatibility"].asBool();
bool dnp = settings_["dropNullPlaceholders"].asBool(); const bool dnp = settings_["dropNullPlaceholders"].asBool();
bool usf = settings_["useSpecialFloats"].asBool(); const bool usf = settings_["useSpecialFloats"].asBool();
const bool emitUTF8 = settings_["emitUTF8"].asBool();
unsigned int pre = settings_["precision"].asUInt(); unsigned int pre = settings_["precision"].asUInt();
CommentStyle::Enum cs = CommentStyle::All; CommentStyle::Enum cs = CommentStyle::All;
if (cs_str == "All") { if (cs_str == "All") {
@@ -1190,36 +1197,33 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const {
pre = 17; pre = 17;
String endingLineFeedSymbol; String endingLineFeedSymbol;
return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
endingLineFeedSymbol, usf, pre, endingLineFeedSymbol, usf, emitUTF8, pre,
precisionType); 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("precision");
valid_keys->insert("precisionType");
}
bool StreamWriterBuilder::validate(Json::Value* invalid) const { bool StreamWriterBuilder::validate(Json::Value* invalid) const {
Json::Value my_invalid; static const auto& valid_keys = *new std::set<String>{
if (!invalid) "indentation",
invalid = &my_invalid; // so we do not need to test for NULL "commentStyle",
Json::Value& inv = *invalid; "enableYAMLCompatibility",
std::set<String> valid_keys; "dropNullPlaceholders",
getValidWriterKeys(&valid_keys); "useSpecialFloats",
Value::Members keys = settings_.getMemberNames(); "emitUTF8",
size_t n = keys.size(); "precision",
for (size_t i = 0; i < n; ++i) { "precisionType",
String const& key = keys[i]; };
if (valid_keys.find(key) == valid_keys.end()) { for (auto si = settings_.begin(); si != settings_.end(); ++si) {
inv[key] = settings_[key]; 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) { Value& StreamWriterBuilder::operator[](const String& key) {
return settings_[key]; return settings_[key];
} }
@@ -1231,6 +1235,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings) {
(*settings)["enableYAMLCompatibility"] = false; (*settings)["enableYAMLCompatibility"] = false;
(*settings)["dropNullPlaceholders"] = false; (*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false; (*settings)["useSpecialFloats"] = false;
(*settings)["emitUTF8"] = false;
(*settings)["precision"] = 17; (*settings)["precision"] = 17;
(*settings)["precisionType"] = "significant"; (*settings)["precisionType"] = "significant";
//! [StreamWriterBuilderDefaults] //! [StreamWriterBuilderDefaults]

View File

@@ -1,22 +0,0 @@
// DO NOT EDIT. This file (and "version") is a template used by the build system
// (either CMake or Meson) to generate a "version.h" header file.
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
#define JSONCPP_VERSION_STRING "@JSONCPP_VERSION@"
#define JSONCPP_VERSION_MAJOR @JSONCPP_VERSION_MAJOR@
#define JSONCPP_VERSION_MINOR @JSONCPP_VERSION_MINOR@
#define JSONCPP_VERSION_PATCH @JSONCPP_VERSION_PATCH@
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) \
| (JSONCPP_VERSION_MINOR << 16) \
| (JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY @JSONCPP_USE_SECURE_MEMORY@
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED

View File

@@ -1,42 +1,37 @@
# vim: et ts=4 sts=4 sw=4 tw=0 # vim: et ts=4 sts=4 sw=4 tw=0
add_executable( jsoncpp_test add_executable(jsoncpp_test
jsontest.cpp jsontest.cpp
jsontest.h jsontest.h
fuzz.cpp fuzz.cpp
fuzz.h fuzz.h
main.cpp main.cpp
) )
if(BUILD_SHARED_LIBS) if(BUILD_SHARED_LIBS)
add_compile_definitions( JSON_DLL ) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
add_compile_definitions( JSON_DLL )
else()
add_definitions( -DJSON_DLL )
endif()
endif() endif()
target_link_libraries(jsoncpp_test jsoncpp_lib) target_link_libraries(jsoncpp_test jsoncpp_lib)
# another way to solve issue #90 # another way to solve issue #90
#set_target_properties(jsoncpp_test PROPERTIES COMPILE_FLAGS -ffloat-store) #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 # Run unit tests in post-build
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?) # (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
if(JSONCPP_WITH_POST_BUILD_UNITTEST) if(JSONCPP_WITH_POST_BUILD_UNITTEST)
if(BUILD_SHARED_LIBS) add_custom_command(TARGET jsoncpp_test
# First, copy the shared lib, for Microsoft. POST_BUILD
# Then, run the test executable. COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test>
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>
) )
endif() endif()
set_target_properties(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test)

View File

@@ -9,7 +9,6 @@
#include <json/config.h> #include <json/config.h>
#include <json/json.h> #include <json/json.h>
#include <memory> #include <memory>
#include <stdint.h>
#include <string> #include <string>
namespace Json { namespace Json {
@@ -23,8 +22,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return 0; return 0;
} }
uint32_t hash_settings = *(const uint32_t*)data; const uint32_t hash_settings = static_cast<uint32_t>(data[0]) |
(static_cast<uint32_t>(data[1]) << 8) |
(static_cast<uint32_t>(data[2]) << 16) |
(static_cast<uint32_t>(data[3]) << 24);
data += sizeof(uint32_t); data += sizeof(uint32_t);
size -= sizeof(uint32_t);
builder.settings_["failIfExtra"] = hash_settings & (1 << 0); builder.settings_["failIfExtra"] = hash_settings & (1 << 0);
builder.settings_["allowComments_"] = hash_settings & (1 << 1); builder.settings_["allowComments_"] = hash_settings & (1 << 1);
@@ -35,11 +38,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
builder.settings_["failIfExtra_"] = hash_settings & (1 << 6); builder.settings_["failIfExtra_"] = hash_settings & (1 << 6);
builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7); builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7);
builder.settings_["allowSpecialFloats_"] = hash_settings & (1 << 8); 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()); std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value root; Json::Value root;
const char* data_str = reinterpret_cast<const char*>(data); const auto data_str = reinterpret_cast<const char*>(data);
try { try {
reader->parse(data_str, data_str + size, &root, nullptr); reader->parse(data_str, data_str + size, &root, nullptr);
} catch (Json::Exception const&) { } catch (Json::Exception const&) {

View File

@@ -0,0 +1,54 @@
#
# AFL dictionary for JSON
# -----------------------
#
# Just the very basics.
#
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
# https://github.com/rc0r/afl-fuzz/blob/master/dictionaries/json.dict
#
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"

View File

@@ -82,8 +82,8 @@ TestResult::TestResult() {
void TestResult::setTestName(const Json::String& name) { name_ = name; } void TestResult::setTestName(const Json::String& name) { name_ = name; }
TestResult& TestResult& TestResult::addFailure(const char* file, unsigned int line,
TestResult::addFailure(const char* file, unsigned int line, const char* expr) { const char* expr) {
/// Walks the PredicateContext stack adding them to failures_ if not already /// Walks the PredicateContext stack adding them to failures_ if not already
/// added. /// added.
unsigned int nestingLevel = 0; unsigned int nestingLevel = 0;
@@ -107,10 +107,8 @@ TestResult::addFailure(const char* file, unsigned int line, const char* expr) {
return *this; return *this;
} }
void TestResult::addFailureInfo(const char* file, void TestResult::addFailureInfo(const char* file, unsigned int line,
unsigned int line, const char* expr, unsigned int nestingLevel) {
const char* expr,
unsigned int nestingLevel) {
Failure failure; Failure failure;
failure.file_ = file; failure.file_ = file;
failure.line_ = line; failure.line_ = line;
@@ -269,19 +267,18 @@ bool Runner::runAllTest(bool printSummary) const {
printf("All %zu tests passed\n", count); printf("All %zu tests passed\n", count);
} }
return true; return true;
} else {
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;
} }
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;
} }
bool Runner::testIndex(const Json::String& testName, size_t& indexOut) const { bool Runner::testIndex(const Json::String& testName, size_t& indexOut) const {
@@ -310,7 +307,8 @@ int Runner::runCommandLine(int argc, const char* argv[]) const {
if (opt == "--list-tests") { if (opt == "--list-tests") {
listTests(); listTests();
return 0; return 0;
} else if (opt == "--test-auto") { }
if (opt == "--test-auto") {
preventDialogOnCrash(); preventDialogOnCrash();
} else if (opt == "--test") { } else if (opt == "--test") {
++index; ++index;
@@ -342,8 +340,8 @@ int Runner::runCommandLine(int argc, const char* argv[]) const {
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
// Hook MSVCRT assertions to prevent dialog from appearing // Hook MSVCRT assertions to prevent dialog from appearing
static int static int msvcrtSilentReportHook(int reportType, char* message,
msvcrtSilentReportHook(int reportType, char* message, int* /*returnValue*/) { int* /*returnValue*/) {
// The default CRT handling of error and assertion is to display // The default CRT handling of error and assertion is to display
// an error dialog to the user. // an error dialog to the user.
// Instead, when an error or an assertion occurs, we force the // Instead, when an error or an assertion occurs, we force the
@@ -418,12 +416,9 @@ Json::String ToJsonString(std::string in) {
} }
#endif #endif
TestResult& checkStringEqual(TestResult& result, TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
const Json::String& expected, const Json::String& actual, const char* file,
const Json::String& actual, unsigned int line, const char* expr) {
const char* file,
unsigned int line,
const char* expr) {
if (expected != actual) { if (expected != actual) {
result.addFailure(file, line, expr); result.addFailure(file, line, expr);
result << "Expected: '" << expected << "'\n"; result << "Expected: '" << expected << "'\n";

View File

@@ -8,6 +8,7 @@
#include <cstdio> #include <cstdio>
#include <deque> #include <deque>
#include <iomanip>
#include <json/config.h> #include <json/config.h>
#include <json/value.h> #include <json/value.h>
#include <json/writer.h> #include <json/writer.h>
@@ -41,7 +42,7 @@ public:
/// Must be a POD to allow inline initialisation without stepping /// Must be a POD to allow inline initialisation without stepping
/// into the debugger. /// into the debugger.
struct PredicateContext { struct PredicateContext {
typedef unsigned int Id; using Id = unsigned int;
Id id_; Id id_;
const char* file_; const char* file_;
unsigned int line_; unsigned int line_;
@@ -68,8 +69,8 @@ public:
void setTestName(const Json::String& name); void setTestName(const Json::String& name);
/// Adds an assertion failure. /// Adds an assertion failure.
TestResult& TestResult& addFailure(const char* file, unsigned int line,
addFailure(const char* file, unsigned int line, const char* expr = nullptr); const char* expr = nullptr);
/// Removes the last PredicateContext added to the predicate stack /// Removes the last PredicateContext added to the predicate stack
/// chained list. /// chained list.
@@ -83,9 +84,7 @@ public:
// Generic operator that will work with anything ostream can deal with. // Generic operator that will work with anything ostream can deal with.
template <typename T> TestResult& operator<<(const T& value) { template <typename T> TestResult& operator<<(const T& value) {
Json::OStringStream oss; Json::OStringStream oss;
oss.precision(16); oss << std::setprecision(16) << std::hexfloat << value;
oss.setf(std::ios_base::floatfield);
oss << value;
return addToLastFailure(oss.str()); return addToLastFailure(oss.str());
} }
@@ -98,14 +97,12 @@ public:
private: private:
TestResult& addToLastFailure(const Json::String& message); TestResult& addToLastFailure(const Json::String& message);
/// Adds a failure or a predicate context /// Adds a failure or a predicate context
void addFailureInfo(const char* file, void addFailureInfo(const char* file, unsigned int line, const char* expr,
unsigned int line,
const char* expr,
unsigned int nestingLevel); unsigned int nestingLevel);
static Json::String indentText(const Json::String& text, static Json::String indentText(const Json::String& text,
const Json::String& indent); const Json::String& indent);
typedef std::deque<Failure> Failures; using Failures = std::deque<Failure>;
Failures failures_; Failures failures_;
Json::String name_; Json::String name_;
PredicateContext rootPredicateNode_; PredicateContext rootPredicateNode_;
@@ -132,7 +129,7 @@ private:
}; };
/// Function pointer type for TestCase factory /// Function pointer type for TestCase factory
typedef TestCase* (*TestCaseFactory)(); using TestCaseFactory = TestCase* (*)();
class Runner { class Runner {
public: public:
@@ -171,17 +168,13 @@ private:
static void preventDialogOnCrash(); static void preventDialogOnCrash();
private: private:
typedef std::deque<TestCaseFactory> Factories; using Factories = std::deque<TestCaseFactory>;
Factories tests_; Factories tests_;
}; };
template <typename T, typename U> template <typename T, typename U>
TestResult& checkEqual(TestResult& result, TestResult& checkEqual(TestResult& result, T expected, U actual,
T expected, const char* file, unsigned int line, const char* expr) {
U actual,
const char* file,
unsigned int line,
const char* expr) {
if (static_cast<U>(expected) != actual) { if (static_cast<U>(expected) != actual) {
result.addFailure(file, line, expr); result.addFailure(file, line, expr);
result << "Expected: " << static_cast<U>(expected) << "\n"; result << "Expected: " << static_cast<U>(expected) << "\n";
@@ -196,12 +189,9 @@ Json::String ToJsonString(Json::String in);
Json::String ToJsonString(std::string in); Json::String ToJsonString(std::string in);
#endif #endif
TestResult& checkStringEqual(TestResult& result, TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
const Json::String& expected, const Json::String& actual, const char* file,
const Json::String& actual, unsigned int line, const char* expr);
const char* file,
unsigned int line,
const char* expr);
} // namespace JsonTest } // namespace JsonTest
@@ -217,7 +207,7 @@ TestResult& checkStringEqual(TestResult& result,
/// The predicate may do other assertions and be a member function of the /// The predicate may do other assertions and be a member function of the
/// fixture. /// fixture.
#define JSONTEST_ASSERT_PRED(expr) \ #define JSONTEST_ASSERT_PRED(expr) \
{ \ do { \
JsonTest::PredicateContext _minitest_Context = { \ JsonTest::PredicateContext _minitest_Context = { \
result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL}; \ result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL}; \
result_->predicateStackTail_->next_ = &_minitest_Context; \ result_->predicateStackTail_->next_ = &_minitest_Context; \
@@ -225,7 +215,7 @@ TestResult& checkStringEqual(TestResult& result,
result_->predicateStackTail_ = &_minitest_Context; \ result_->predicateStackTail_ = &_minitest_Context; \
(expr); \ (expr); \
result_->popPredicateContext(); \ result_->popPredicateContext(); \
} } while (0)
/// \brief Asserts that two values are equals. /// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL(expected, actual) \ #define JSONTEST_ASSERT_EQUAL(expected, actual) \
@@ -240,7 +230,7 @@ TestResult& checkStringEqual(TestResult& result,
/// \brief Asserts that a given expression throws an exception /// \brief Asserts that a given expression throws an exception
#define JSONTEST_ASSERT_THROWS(expr) \ #define JSONTEST_ASSERT_THROWS(expr) \
{ \ do { \
bool _threw = false; \ bool _threw = false; \
try { \ try { \
expr; \ expr; \
@@ -250,7 +240,7 @@ TestResult& checkStringEqual(TestResult& result,
if (!_threw) \ if (!_threw) \
result_->addFailure(__FILE__, __LINE__, \ result_->addFailure(__FILE__, __LINE__, \
"expected exception thrown: " #expr); \ "expected exception thrown: " #expr); \
} } while (0)
/// \brief Begin a fixture test case. /// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE(FixtureType, name) \ #define JSONTEST_FIXTURE(FixtureType, name) \
@@ -273,4 +263,26 @@ TestResult& checkStringEqual(TestResult& result,
#define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \ #define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \
(runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name)) (runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name))
/// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE_V2(FixtureType, name, collections) \
class Test##FixtureType##name : public FixtureType { \
public: \
static JsonTest::TestCase* factory() { \
return new Test##FixtureType##name(); \
} \
static bool collect() { \
(collections).push_back(JSONTEST_FIXTURE_FACTORY(FixtureType, name)); \
return true; \
} \
\
public: /* overridden from TestCase */ \
const char* testName() const override { return #FixtureType "/" #name; } \
void runTestCase() override; \
}; \
\
static bool test##FixtureType##name##collect = \
Test##FixtureType##name::collect(); \
\
void Test##FixtureType##name::runTestCase()
#endif // ifndef JSONTEST_H_INCLUDED #endif // ifndef JSONTEST_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
[1,,]

View File

@@ -0,0 +1 @@
{ "count" : 1234,, }

Some files were not shown because too many files have changed in this diff Show More