Compare commits

..

96 Commits

Author SHA1 Message Date
Christopher Dunn
6764059395 fix stdexcept
https://sourceforge.net/p/jsoncpp/bugs/68/
2014-05-13 09:49:25 +00:00
Aaron Jacobs
5d32295a6e Fixed a test that causes a crash when exceptions are disabled.
While I was at it, corrected whitespace too.
2014-04-23 23:57:59 +00:00
Aaron Jacobs
68db655347 Added structured error reporting to Reader.
This allows applications for interactively viewing or editing JSON to do
a better job of highlighting errors. Also added offset accessors to
Value, offering the same sort of functionality even for non-errors.

Thanks to Zach Clifford (zacharyc@google.com) for the patch.
2014-04-23 23:41:12 +00:00
Aaron Jacobs
642befc836 Added features that allow the reader to accept common non-standard JSON.
This is a version of patch #17, from Clay Wood:

    http://sourceforge.net/p/jsoncpp/patches/17/
2014-04-23 23:28:23 +00:00
Christopher Dunn
77cd83890d vim modeline
http://vim.wikia.com/wiki/Modeline_magic
2014-04-19 21:41:03 +00:00
Christopher Dunn
09439b7bc7 Comment reading/write improvements
This patch fixes some aspects of reading and writing comments:
- Multiple C++-style comments before a Json value had extra newlines appended to them. This patch removes the addition of those newlines.
- Comments written before Json values in the StyledWriter were not indented to match the indentation level of the value. This patch adds indentation to comments.
- Fixed inconsistency in newlines following C- and C++-style comments being saved as part of the comment. All newlines at the end of a comment are now removed.
- Added an additional test of comments.

https://sourceforge.net/p/jsoncpp/patches/25/
2014-04-19 21:19:24 +00:00
Christopher Dunn
ea0797351f JSON_ASSERT -> JSON_ASSERT_MESSAGE
This way, assertions can produce exceptions.
https://sourceforge.net/p/jsoncpp/bugs/67/
2014-04-19 06:37:23 +00:00
Aaron Jacobs
94d17e9fdf Added missing includes for std::istream.
Thanks to Quentin Fiard for the report.
2014-01-29 00:13:38 +00:00
Baptiste Lepilleur
a3f19c23a0 Fixed broken build on VS 2012 2013-09-23 14:10:39 +00:00
Aaron Jacobs
d2618806ba Fixed some snprintf-related build breakages in Visual Studio. 2013-08-08 23:08:28 +00:00
Aaron Jacobs
36400ac0c1 Updated two calls to sprintf that I missed in r269. 2013-08-08 00:39:32 +00:00
Aaron Jacobs
32ffb931e7 Replaced the complex implementation of valueToString(double).
The previous one was confusing and prone to buffer overflows, and didn't
work correctly with 16-decimal-digit numbers. The new one simply uses
snprintf with a standard format string.

The major change is that we don't always print a decimal point now.
Fortunately, JSON doesn't distinguish between integers and reals.
2013-08-08 00:39:12 +00:00
Aaron Jacobs
bb53cd0899 Added more floating point tests.
The first demonstrates a bug that I will soon fix.
2013-08-08 00:37:39 +00:00
Aaron Jacobs
4c531bb584 Added further floating point tests. 2013-08-08 00:13:10 +00:00
Aaron Jacobs
42d918b7aa Switched away from sprintf, which is prone to buffer overflows.
Most reasonable platforms have this function. If you're here because
this broke the build for you, consider adding an ifdef for your platform
and using sprintf there (but not on other platforms).
2013-08-06 23:12:56 +00:00
Baptiste Lepilleur
700b38020e - CMake: added option to turn fail compilation if warning occurs, and warning level 4 with MSVC.
- Fixed some warnings
2013-05-09 18:42:33 +00:00
Baptiste Lepilleur
7b62ceacee - disabled warning 4786 for VS6 caused by STL (identifier was truncated to '255' characters in the debug information)
- added batchbuild config for XP VM
2013-05-09 16:24:13 +00:00
Baptiste Lepilleur
cb5ae30f6e Added simple batch build script for CMake. 2013-05-09 15:22:14 +00:00
Baptiste Lepilleur
58b6541478 Added missing source file to CMakeLists.txt. 2013-05-09 15:21:06 +00:00
Baptiste Lepilleur
1ccfdfcb9b 2013-05-09 15:20:32 +00:00
Baptiste Lepilleur
71860de813 Fixed continuous integration matrix for debug/release build. Made static debug build verbose. 2013-05-08 22:23:07 +00:00
Baptiste Lepilleur
c515b8ec30 Added continuous integration matrix for debug/release build. Made static debug build verbose. 2013-05-08 22:15:15 +00:00
Baptiste Lepilleur
5fff185aa4 Added continuous integration matrix for shared/static library (specified through environment variables). 2013-05-08 22:04:57 +00:00
Baptiste Lepilleur
10712e85d6 Added continuous integration failure e-mail notification. 2013-05-08 21:23:52 +00:00
Baptiste Lepilleur
53c08ad916 Added clang compiler for continuous integration. 2013-05-08 21:04:42 +00:00
Baptiste Lepilleur
79e90fba0b Added basic Travis CI integration contributed by Igor Okulist. 2013-05-08 20:46:56 +00:00
Baptiste Lepilleur
ce277aa6e4 Fixed CMake / Unix build instructions. 2013-05-08 20:37:54 +00:00
Baptiste Lepilleur
eafd702a17 - New CMake based build system. Based in part on contribution from
Igor Okulist and Damien Buhl (Patch #14). Added support for running
tests and building with DLL on Windows.
- added missing JSON_API
- Visual Studio DLL: suppressed warning "C4251: <data member>: <type> 
needs to have dll-interface to be used by..." via pragma push/pop
in json-cpp headers.
- New header json/version.h now contains version number macros
(JSONCPP_VERSION_MAJOR, JSONCPP_VERSION_MINOR, JSONCPP_VERSION_PATCH
and JSONCPP_VERSION_HEXA). While this header is generated by CMake,
it is committed to ease build with alternate build system 
(CMake only update the file when it changes avoid issues with VCS).
2013-05-08 20:21:11 +00:00
Baptiste Lepilleur
a8afdd40af - Patch #3393345: BOOST_FOREACH compatibility. Made Json::iterator more standard compliant, added missing iterator_category and value_type typedefs (contribued by Robert A. Iannucci).
- Patch #3474563: added missing JSON_API on some classes causing link issues when building as a dynamic library on Windows (contributed by Francis Bolduc).
2013-04-12 14:10:13 +00:00
Baptiste Lepilleur
f92ace5e82 Patch #3600941: Missing field copy in Json::Value::iterator causing infinite loop when using experimental internal map (#define JSON_VALUE_USE_INTERNAL_MAP) (contributed by Ming-Lin Kao). 2013-04-12 13:26:23 +00:00
Baptiste Lepilleur
3f124172ce Patch #3539678: Copy constructor does not initialize allocated_ for stringValue (contributed by rmongia). 2013-04-12 13:11:14 +00:00
Baptiste Lepilleur
f8715856f3 Fix gcc -Wall warnings (patch from Matt McCormick) 2013-02-18 15:53:47 +00:00
Baptiste Lepilleur
42321f24a6 Fixed warning(error?) on #if testing value of _MSC_VER without checking that it was defined. 2012-12-20 10:08:50 +00:00
Baptiste Lepilleur
aff1171153 Added missing "include/json/assertions.h" header in amalgamate.py. 2012-07-27 09:06:40 +00:00
Aaron Jacobs
ae3c7a7aab Made it possible to drop null placeholders from array output.
This can be used when it's clear that the consumer is able to deal with
this, as web browsers are. Thanks to Yatin Chawathe for the patch.
2012-03-12 04:53:57 +00:00
Aaron Jacobs
f572e8e42e Added an exit() to JSON_FAIL_MESSAGE to fix "no return" errors. 2012-01-08 23:49:55 +00:00
Aaron Jacobs
2b853c4067 Got rid of several unnecessary includes of <iostream>.
Including <iostream> causes the file to be polluted with a static
initializer for the __ioinit symbol. This can harm binary startup time.
For more info, see here:

    http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html
2011-12-22 03:18:24 +00:00
Aaron Jacobs
7c507d7eba Made JSON_USE_EXCEPTION's value in config.h a default that can be overridden.
This allows users to override it with their compiler invocation. For example:

    g++ -D JSON_USE_EXCEPTION=0 ...
2011-09-14 08:41:37 +00:00
Christopher Dunn
c3763f55da Updated bug-fix list. 2011-06-24 21:15:30 +00:00
Christopher Dunn
3b3540d9ef bug#2407932: strpbrk() could fail for NULL pointer. 2011-06-22 21:04:41 +00:00
Christopher Dunn
9d317c3794 bug#3306345: minor typo in Path::resolve() -- missing bang. 2011-06-22 08:30:21 +00:00
Christopher Dunn
03288e8eb6 (bug#3314841) Fixed JSON_IS_AMALGAMATION. Using os.path for OSX filename compatibility. 2011-06-22 00:43:31 +00:00
Christopher Dunn
c9f91dd929 More missing constructor initializers found by Coverity. 2011-06-21 23:02:06 +00:00
Christopher Dunn
2ba3bc3252 Another simple addition for constructor initialization, PathArgument. 2011-06-21 22:08:49 +00:00
Christopher Dunn
ac5df77bbc Simple changes to Reader initialization, from Chromium folks. (I do not think this was submitted as a bug.) 2011-06-21 21:56:54 +00:00
Christopher Dunn
468564b3fe More eol changes. 2011-06-21 21:53:02 +00:00
Christopher Dunn
dc0f736f59 Switched CRLF to LF in repo, and added svn:eol-style native. I might have missed a few files though. Just committing what I have so far. 2011-06-21 21:18:49 +00:00
Christopher Dunn
139da63aef Just testing whether I can still commit changes. I cannot tell my access-level from the sf project page. 2011-06-21 20:34:40 +00:00
Baptiste Lepilleur
d496e044b1 Fixed unit tests execution on MSVC 6 by removing usage of std::numeric_limits. It was returning 0 value in some max cases. Fixed Value::asFloat() to use integerToDouble(). 2011-05-27 08:12:41 +00:00
Baptiste Lepilleur
f587e6a420 Fixed compilation issues with MSVC 6: replace usage of ostringstream with valueToString to support 64 bits integer and high precision floating point conversion to string. Replace usage of ULL and LL literal with UInt64(expr) and Int64(expr). Introduced helper function uint64ToDouble() to work-around missing conversion. Unit tests do not pass yet. 2011-05-26 22:55:24 +00:00
Baptiste Lepilleur
f0b24e705f Fixed MSVS 2003, 2005 and 2008 tests execution by normalizing floating-point string representation using helper normalizeFloatingPointStr(). 2011-05-26 20:14:32 +00:00
Baptiste Lepilleur
e807a7640e Fixed unit test failure on IBM AIX xlC by hard-coding the maxUInt64AsDouble as double constant instead of relying on double(Value::maxUInt64) which produces an incorrect value. 2011-05-26 17:14:26 +00:00
Baptiste Lepilleur
d3cd9a7fc5 - Fixed unit test compilation on MSVS 2003, 2005 and 2008.
- Worked-around unit test failure with MSVS* by "forcing" all floating-point numbers to be loaded from memory instead of FPU registers.
2011-05-26 07:32:36 +00:00
Aaron Jacobs
a2fb7fb918 Fixed some test bugs that show up when 64-bit mode is disabled. 2011-05-26 06:58:52 +00:00
Aaron Jacobs
4b819c2309 Added a few test cases that Google is using internally for patches made
in the past.
2011-05-26 06:58:14 +00:00
Aaron Jacobs
c649badb95 Another round of attempting to fix VC++ errors... 2011-05-26 03:44:02 +00:00
Aaron Jacobs
a9eb1eccc0 Fixed more default cases. 2011-05-26 03:32:11 +00:00
Aaron Jacobs
6ffff91c54 Got rid of some unreachable code. 2011-05-26 03:27:44 +00:00
Aaron Jacobs
acdefb0869 Fixed a double -> float compilation warning/error. 2011-05-26 03:04:01 +00:00
Aaron Jacobs
c025697ea5 Reworked the type conversion system again, so that:A
*  isFoo methods determine exact representability.
 *  asFoo methods cause casting when safe.
 *  isConvertibleTo indicates whether casting is safe.

See NEWS.txt for details.
2011-05-26 02:46:28 +00:00
Aaron Jacobs
b0ec41c3e3 Made the unit test's output more readable, adding to jsontest's
capabilities (and simplifying its implementation) in the process.
2011-05-26 00:30:39 +00:00
Aaron Jacobs
2a2b5cf3ad Made jsontest work with 64-bit integers, and fixed an error. 2011-05-26 00:12:48 +00:00
Aaron Jacobs
b6620e2801 Removed some out of date TODOs. 2011-05-25 23:26:58 +00:00
Aaron Jacobs
ccde848fd1 Fixed test failures with 64-bit support disabled. 2011-05-25 05:53:59 +00:00
Aaron Jacobs
e082248001 Fixed a 'comparison between signed and unsigned' error. 2011-05-25 05:50:13 +00:00
Aaron Jacobs
7b5edd9859 Added line breaks to make error messages easier to read. 2011-05-25 04:59:57 +00:00
Aaron Jacobs
e91a68cb9e Fixed a compilation warning/error. 2011-05-25 04:34:57 +00:00
Aaron Jacobs
1b138e8544 Gave a more consistent behavior to the Value::isFoo methods. See
NEWS.txt for more details.
2011-05-25 04:19:17 +00:00
Aaron Jacobs
4f081b50e6 Fixed bugs in asInt64 and asUInt64. 2011-05-25 03:16:49 +00:00
Aaron Jacobs
3c9fdeb859 Added tests for default numeric values. 2011-05-25 02:54:11 +00:00
Aaron Jacobs
4b79fd1a00 Fixed a test bug. 2011-05-25 01:51:30 +00:00
Aaron Jacobs
e12d84ebaa Made tests more comprehensive. 2011-05-25 01:46:50 +00:00
Aaron Jacobs
078e0d7c37 Gave tests more general names in preparation for making them much more
comprehensive.
2011-05-25 01:24:23 +00:00
Aaron Jacobs
fee49b1a37 Fixed some whitespace. 2011-05-25 01:23:47 +00:00
Aaron Jacobs
22eede44c1 Added tests for 64-bit integers. 2011-05-25 01:23:08 +00:00
Aaron Jacobs
d9ec234fc2 Greatly fleshed out numeric type tests. 2011-05-25 01:04:07 +00:00
Aaron Jacobs
3e5b347f75 Added some missing checks. 2011-05-25 01:03:29 +00:00
Aaron Jacobs
96408a30e1 Renamed test cases to make more sense with the upcoming new behavior of
isFoo methods.
2011-05-25 00:39:55 +00:00
Aaron Jacobs
1d648f089a Fixed a whitespace problem. 2011-05-25 00:39:17 +00:00
Aaron Jacobs
f40c880585 Fixed a "comparison between signed and unsigned" warning/error. 2011-05-24 23:08:59 +00:00
Aaron Jacobs
39ba2dbea9 Added a .gitignore file, for ease of use with git-svn. 2011-05-24 23:05:56 +00:00
Aaron Jacobs
a761530f14 Fixed a missing include error. 2011-05-24 06:27:36 +00:00
Aaron Jacobs
ae9ffb5443 Fixed a parsing bug in decodeNumber, updating the failing test cases to be
correct in the process. (The test cases incorrectly used exact integers instead
of scientific notation.)
2011-05-24 03:59:24 +00:00
Aaron Jacobs
e656c5fa2d Added some test cases that catch a parsing bug. 2011-05-24 03:19:50 +00:00
Aaron Jacobs
f1053e7acb Fixed a bunch of compilation errors when JSON_HAS_INT64 is set. 2011-05-24 03:18:02 +00:00
Aaron Jacobs
e3d0eca9f4 Centralized assertion macros and made them obey JSON_USE_EXCEPTION. 2011-05-24 01:03:22 +00:00
Aaron Jacobs
a77a803c85 Made two security fixes. 2011-05-24 00:43:59 +00:00
Aaron Jacobs
785ba2675d Updated a cast to use a more appropriate type. 2011-05-24 00:43:30 +00:00
Aaron Jacobs
3b556ec633 Fixed constructor initializer list order warnings/errors. 2011-05-24 00:42:58 +00:00
Aaron Jacobs
5fb0f09cbb Removed an unused typedef. 2011-05-24 00:42:15 +00:00
Aaron Jacobs
73911f2e33 Fixed a hard to debug crash on OS X related to sscanf format strings.
See here for more info:
    http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
2011-05-24 00:41:12 +00:00
Baptiste Lepilleur
d21c256fae Released 0.6.0-rc2 2011-05-02 22:07:18 +00:00
Baptiste Lepilleur
72c406b550 Release 0.6.0-rc2 2011-05-02 21:30:42 +00:00
Baptiste Lepilleur
eadc478e50 Fixed typo: amalga*ma*te. Replaced macro JSON_IS_AMALGATED with JSON_IS_AMALGAMATION 2011-05-02 21:09:30 +00:00
Baptiste Lepilleur
1837a1c508 Value::compare() is now const and has an actual implementation with unit tests. 2011-05-02 20:11:48 +00:00
Baptiste Lepilleur
e3cc0f004b Untabified some sources 2011-05-02 18:41:01 +00:00
77 changed files with 5592 additions and 1027 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
*.pyc
*.swp
*.actual
*.actual-rewrite
*.process-output
*.rewrite
bin/
buildscons/
libs/

23
.travis.yml Normal file
View File

@@ -0,0 +1,23 @@
# Build matrix / environment variable are explained on:
# http://about.travis-ci.org/docs/user/build-configuration/
# This file can be validated on:
# http://lint.travis-ci.org/
before_install: sudo apt-get install cmake
language: cpp
compiler:
- gcc
- clang
script: cmake -DJSONCPP_LIB_BUILD_SHARED=$SHARED_LIBRARY -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE=$VERBOSE_MAKE . && make
env:
global:
- JSONCPP_CONTINUOUS_INTERATION=1
matrix:
- SHARED_LIBRARY=ON BUILD_TYPE=release VERBOSE_MAKE=false
- SHARED_LIBRARY=OFF BUILD_TYPE=release VERBOSE_MAKE=false
- SHARED_LIBRARY=OFF BUILD_TYPE=debug VERBOSE VERBOSE_MAKE=true
notifications:
recipients:
- baptiste.lepilleur@gmail.com
email:
on_success: change
on_failure: always

90
CMakeLists.txt Normal file
View File

@@ -0,0 +1,90 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(jsoncpp)
ENABLE_TESTING()
OPTION(JSONCPP_WITH_TESTS "Compile and run JsonCpp test executables" ON)
OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
OPTION(JSONCPP_WITH_WARNING_AS_ERROR "Force compilation to fail if a warning occurs" OFF)
# Ensures that CMAKE_BUILD_TYPE is visible in cmake-gui on Unix
IF(NOT WIN32)
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage."
FORCE)
ENDIF(NOT CMAKE_BUILD_TYPE)
ENDIF(NOT WIN32)
# This ensures shared DLL are in the same dir as executable on Windows.
# Put all executables / libraries are in a project global directory.
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
CACHE PATH "Single directory for all static libraries.")
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib
CACHE PATH "Single directory for all dynamic libraries on Unix.")
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin
CACHE PATH "Single directory for all executable and dynamic libraries on Windows.")
MARK_AS_ADVANCED( CMAKE_RUNTIME_OUTPUT_DIRECTORY CMAKE_LIBRARY_OUTPUT_DIRECTORY CMAKE_ARCHIVE_OUTPUT_DIRECTORY )
# Set variable named ${VAR_NAME} to value ${VALUE}
FUNCTION(set_using_dynamic_name VAR_NAME VALUE)
SET( "${VAR_NAME}" "${VALUE}" PARENT_SCOPE)
ENDFUNCTION(set_using_dynamic_name)
# Extract major, minor, patch and qualifier from version text
# Parse a version string "X.Y.Z[-qualifier]" and outputs
# version parts in ${OUPUT_PREFIX}_MAJOR, _MINOR, _PATCH, _QUALIFIER.
# If parse succed then ${OUPUT_PREFIX}_FOUND is TRUE.
MACRO(jsoncpp_parse_version VERSION_TEXT OUPUT_PREFIX)
SET(VERSION_REGEX "[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9_]+)?")
IF( ${VERSION_TEXT} MATCHES ${VERSION_REGEX} )
STRING(REGEX MATCHALL "[0-9]+|-([A-Za-z0-9_]+)" VERSION_PARTS ${VERSION_TEXT})
list(APPEND VERSION_PARTS "") # empty qualifier to handle no qualifier case
LIST(GET VERSION_PARTS 0 ${OUPUT_PREFIX}_MAJOR)
LIST(GET VERSION_PARTS 1 ${OUPUT_PREFIX}_MINOR)
LIST(GET VERSION_PARTS 2 ${OUPUT_PREFIX}_PATCH)
LIST(GET VERSION_PARTS 3 ${OUPUT_PREFIX}_QUALIFIER)
set_using_dynamic_name( "${OUPUT_PREFIX}_FOUND" TRUE )
ELSE( ${VERSION_TEXT} MATCHES ${VERSION_REGEX} )
set_using_dynamic_name( "${OUPUT_PREFIX}_FOUND" FALSE )
ENDIF( ${VERSION_TEXT} MATCHES ${VERSION_REGEX} )
ENDMACRO(jsoncpp_parse_version)
# Read out version from "version" file
FILE(STRINGS "version" JSONCPP_VERSION)
jsoncpp_parse_version( ${JSONCPP_VERSION} JSONCPP_VERSION )
IF(NOT JSONCPP_VERSION_FOUND)
MESSAGE(FATAL_ERROR "Failed to parse version string properly. Expect X.Y.Z[-qualifier]")
ENDIF(NOT JSONCPP_VERSION_FOUND)
MESSAGE(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}${JSONCPP_VERSION_QUALIFIER}")
# File version.h is only regenerated on CMake configure step
CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/src/lib_json/version.h.in"
"${PROJECT_SOURCE_DIR}/include/json/version.h" )
macro(UseCompilationWarningAsError)
if ( MSVC )
# Only enabled in debug because some old versions of VS STL generate
# warnings when compiled in release configuration.
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /WX ")
endif( MSVC )
endmacro()
# Include our configuration header
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include )
if ( MSVC )
# Only enabled in debug because some old versions of VS STL generate
# unreachable code warning when compiled in release configuration.
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 ")
endif( MSVC )
IF(JSONCPP_WITH_WARNING_AS_ERROR)
UseCompilationWarningAsError()
ENDIF(JSONCPP_WITH_WARNING_AS_ERROR)
# Build the different applications
ADD_SUBDIRECTORY( src )
#install the includes
ADD_SUBDIRECTORY( include )

View File

@@ -1,3 +1,69 @@
New in SVN
----------
* Updated the type system's behavior, in order to better support backwards
compatibility with code that was written before 64-bit integer support was
introduced. Here's how it works now:
* isInt, isInt64, isUInt, and isUInt64 return true if and only if the
value can be exactly represented as that type. In particular, a value
constructed with a double like 17.0 will now return true for all of
these methods.
* isDouble and isFloat now return true for all numeric values, since all
numeric values can be converted to a double or float without
truncation. Note however that the conversion may not be exact -- for
example, doubles cannot exactly represent all integers above 2^53 + 1.
* isBool, isNull, isString, isArray, and isObject now return true if and
only if the value is of that type.
* isConvertibleTo(fooValue) indicates that it is safe to call asFoo.
(For each type foo, isFoo always implies isConvertibleTo(fooValue).)
asFoo returns an approximate or exact representation as appropriate.
For example, a double value may be truncated when asInt is called.
* For backwards compatibility with old code, isConvertibleTo(intValue)
may return false even if type() == intValue. This is because the value
may have been constructed with a 64-bit integer larger than maxInt,
and calling asInt() would cause an exception. If you're writing new
code, use isInt64 to find out whether the value is exactly
representable using an Int64, or asDouble() combined with minInt64 and
maxInt64 to figure out whether it is approximately representable.
* Value
- Patch #10: BOOST_FOREACH compatibility. Made Json::iterator more
standard compliant, added missing iterator_category and value_type
typedefs (contribued by Robert A. Iannucci).
* Compilation
- New CMake based build system. Based in part on contribution from
Igor Okulist and Damien Buhl (Patch #14).
- New header json/version.h now contains version number macros
(JSONCPP_VERSION_MAJOR, JSONCPP_VERSION_MINOR, JSONCPP_VERSION_PATCH
and JSONCPP_VERSION_HEXA).
- Patch #11: added missing JSON_API on some classes causing link issues
when building as a dynamic library on Windows
(contributed by Francis Bolduc).
- Visual Studio DLL: suppressed warning "C4251: <data member>: <type>
needs to have dll-interface to be used by..." via pragma push/pop
in json-cpp headers.
- Added Travis CI intregration: https://travis-ci.org/blep/jsoncpp-mirror
* Bug fixes
- Patch #15: Copy constructor does not initialize allocated_ for stringValue
(contributed by rmongia).
- Patch #16: Missing field copy in Json::Value::iterator causing infinite
loop when using experimental internal map (#define JSON_VALUE_USE_INTERNAL_MAP)
(contributed by Ming-Lin Kao).
New in JsonCpp 0.6.0:
---------------------
@@ -88,10 +154,18 @@
length of 32 characters.
- Fixed Value::operator <= implementation (had the semantic of operator >=).
Found when addigin unit tests for comparison operators.
Found when adding unit tests for comparison operators.
- Value::compare() is now const and has an actual implementation with
unit tests.
- Bug #2407932: strpbrk() can fail for NULL pointer.
- Bug #3306345: Fixed minor typo in Path::resolve().
- Bug #3314841/#3306896: errors in amalgamate.py
- Fixed some Coverity warnings and line-endings.
* License

View File

@@ -13,9 +13,66 @@ making it a convenient format to store user input files.
Unserialization parsing is user friendly and provides precise error reports.
* Using json-cpp in your project:
===============================
* Building/Testing:
=================
The recommended approach to integrate json-cpp in your project is to
build the the amalgamated source (a single .cpp) with your own build
system. This ensures compilation flags consistency and ABI compatibility.
See section "Generating amalgamated source and header" to generate them
from the source distribution.
Directory include/ should be added to your compiler include path.
json-cpp headers should be included as follow:
#include <json/json.h>
If json-cpp was build as a dynamic library on Windows, then your project
need to define macro "JSON_DLL" to JSON_API should import exported symbols.
* Building/Testing with new CMake build system:
=============================================
CMake is a C++ Makefiles/Solution generator that can be downloaded from:
http://www.cmake.org
It is usually available on most Linux system as package. On Ubuntu:
sudo apt-get install cmake
Notes that python is also required to run JSON reader/writer tests. If
missing, the build will skip running those tests.
When running CMake, a few parameters are required:
- a build directory where the makefiles/solution are generated. It is
also used to store objects, libraries and executables files.
- the generator to use: makefiles or Visual Studio solution? What version
or Visual Studio, 32 or 64 bits solution?
Generating solution/makefiles using cmake-gui:
- Makes "source code" points the source directory
- Makes "where to build the binary" points to the directory to use for
the build.
- Click on the "Grouped" check box
- Review JsonCpp build option (tick JSONCPP_LIB_BUILD_SHARED to build as
a dynamic library)
- Click configure button at the bottom, then the generate button.
- The generated solution/makefiles can be found in the binary directory.
Alternatively, from the command-line on Unix in the source directory:
mkdir -p ../build/debug
cd ../build/debug
cmake -DCMAKE_BUILD_TYPE=debug -DJSONCPP_LIB_BUILD_SHARED=OFF -G "Unix Makefiles" ../../jsoncpp-src
make
Running "cmake -h" will display the list of available generators (passed as -G option).
By default CMake hides compilation command-line. This can be modified by specifying:
-DCMAKE_VERBOSE_MAKEFILE=true when generating makefiles.
* Building/Testing with the legacy build system based on SCons:
=============================================================
JsonCpp uses Scons (http://www.scons.org) as a build system. Scons requires
python to be installed (http://www.python.org).
@@ -47,7 +104,6 @@ to do so.
and TARGET may be:
check: build library and run unit tests.
* Running the test manually:
==========================
@@ -115,15 +171,6 @@ The amalgamated sources are generated by concatenating JsonCpp source in the
correct order and defining macro JSON_IS_AMALGAMATION to prevent inclusion
of other headers.
* Using json-cpp in your project:
===============================
include/ should be added to your compiler include path. jsoncpp headers
should be included as follow:
#include <json/json.h>
* Adding a reader/writer test:
============================
@@ -170,3 +217,4 @@ test_complex_01.process-output: jsontest.exe output, typically useful to
See file LICENSE for details. Basically JsonCpp is licensed under
MIT license, or public domain if desired and recognized in your jurisdiction.

View File

@@ -1,147 +1,150 @@
"""Amalgate json-cpp library sources into a single source and header file.
Requires Python 2.6
Example of invocation (must be invoked from json-cpp top directory):
python amalgate.py
"""
import os
import os.path
import sys
class AmalgamationFile:
def __init__( self, top_dir ):
self.top_dir = top_dir
self.blocks = []
def add_text( self, text ):
if not text.endswith( '\n' ):
text += '\n'
self.blocks.append( text )
def add_file( self, relative_input_path, wrap_in_comment=False ):
def add_marker( prefix ):
self.add_text( '' )
self.add_text( '// ' + '/'*70 )
self.add_text( '// %s of content of file: %s' % (prefix, relative_input_path.replace('\\','/')) )
self.add_text( '// ' + '/'*70 )
self.add_text( '' )
add_marker( 'Beginning' )
f = open( os.path.join( self.top_dir, relative_input_path ), 'rt' )
content = f.read()
if wrap_in_comment:
content = '/*\n' + content + '\n*/'
self.add_text( content )
f.close()
add_marker( 'End' )
self.add_text( '\n\n\n\n' )
def get_value( self ):
return ''.join( self.blocks ).replace('\r\n','\n')
def write_to( self, output_path ):
output_dir = os.path.dirname( output_path )
if output_dir and not os.path.isdir( output_dir ):
os.makedirs( output_dir )
f = open( output_path, 'wb' )
f.write( self.get_value() )
f.close()
def amalgamate_source( source_top_dir=None,
target_source_path=None,
header_include_path=None ):
"""Produces amalgated source.
Parameters:
source_top_dir: top-directory
target_source_path: output .cpp path
header_include_path: generated header path relative to target_source_path.
"""
print 'Amalgating header...'
header = AmalgamationFile( source_top_dir )
header.add_text( '/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).' )
header.add_text( '/// It is intented to be used with #include <%s>' % header_include_path )
header.add_file( 'LICENSE', wrap_in_comment=True )
header.add_text( '#ifndef JSON_AMALGATED_H_INCLUDED' )
header.add_text( '# define JSON_AMALGATED_H_INCLUDED' )
header.add_text( '/// If defined, indicates that the source file is amalgated' )
header.add_text( '/// to prevent private header inclusion.' )
header.add_text( '#define JSON_IS_AMALGATED' )
header.add_file( 'include/json/config.h' )
header.add_file( 'include/json/forwards.h' )
header.add_file( 'include/json/features.h' )
header.add_file( 'include/json/value.h' )
header.add_file( 'include/json/reader.h' )
header.add_file( 'include/json/writer.h' )
header.add_text( '#endif //ifndef JSON_AMALGATED_H_INCLUDED' )
target_header_path = os.path.join( os.path.dirname(target_source_path), header_include_path )
print 'Writing amalgated header to %r' % target_header_path
header.write_to( target_header_path )
base, ext = os.path.splitext( header_include_path )
forward_header_include_path = base + '-forwards' + ext
print 'Amalgating forward header...'
header = AmalgamationFile( source_top_dir )
header.add_text( '/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).' )
header.add_text( '/// It is intented to be used with #include <%s>' % forward_header_include_path )
header.add_text( '/// This header provides forward declaration for all JsonCpp types.' )
header.add_file( 'LICENSE', wrap_in_comment=True )
header.add_text( '#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED' )
header.add_text( '# define JSON_FORWARD_AMALGATED_H_INCLUDED' )
header.add_text( '/// If defined, indicates that the source file is amalgated' )
header.add_text( '/// to prevent private header inclusion.' )
header.add_text( '#define JSON_IS_AMALGATED' )
header.add_file( 'include/json/config.h' )
header.add_file( 'include/json/forwards.h' )
header.add_text( '#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED' )
target_forward_header_path = os.path.join( os.path.dirname(target_source_path),
forward_header_include_path )
print 'Writing amalgated forward header to %r' % target_forward_header_path
header.write_to( target_forward_header_path )
print 'Amalgating source...'
source = AmalgamationFile( source_top_dir )
source.add_text( '/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).' )
source.add_text( '/// It is intented to be used with #include <%s>' % header_include_path )
source.add_file( 'LICENSE', wrap_in_comment=True )
source.add_text( '' )
source.add_text( '#include <%s>' % header_include_path )
source.add_text( '' )
source.add_file( 'src/lib_json\json_tool.h' )
source.add_file( 'src/lib_json\json_reader.cpp' )
source.add_file( 'src/lib_json\json_batchallocator.h' )
source.add_file( 'src/lib_json\json_valueiterator.inl' )
source.add_file( 'src/lib_json\json_value.cpp' )
source.add_file( 'src/lib_json\json_writer.cpp' )
print 'Writing amalgated source to %r' % target_source_path
source.write_to( target_source_path )
def main():
usage = """%prog [options]
Generate a single amalgated source and header file from the sources.
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option('-s', '--source', dest="target_source_path", action='store', default='dist/jsoncpp.cpp',
help="""Output .cpp source path. [Default: %default]""")
parser.add_option('-i', '--include', dest="header_include_path", action='store', default='json/json.h',
help="""Header include path. Used to include the header from the amalgated source file. [Default: %default]""")
parser.add_option('-t', '--top-dir', dest="top_dir", action='store', default=os.getcwd(),
help="""Source top-directory. [Default: %default]""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
msg = amalgamate_source( source_top_dir=options.top_dir,
target_source_path=options.target_source_path,
header_include_path=options.header_include_path )
if msg:
sys.stderr.write( msg + '\n' )
sys.exit( 1 )
else:
print 'Source succesfully amalagated'
if __name__ == '__main__':
main()
"""Amalgate json-cpp library sources into a single source and header file.
Requires Python 2.6
Example of invocation (must be invoked from json-cpp top directory):
python amalgate.py
"""
import os
import os.path
import sys
class AmalgamationFile:
def __init__( self, top_dir ):
self.top_dir = top_dir
self.blocks = []
def add_text( self, text ):
if not text.endswith( '\n' ):
text += '\n'
self.blocks.append( text )
def add_file( self, relative_input_path, wrap_in_comment=False ):
def add_marker( prefix ):
self.add_text( '' )
self.add_text( '// ' + '/'*70 )
self.add_text( '// %s of content of file: %s' % (prefix, relative_input_path.replace('\\','/')) )
self.add_text( '// ' + '/'*70 )
self.add_text( '' )
add_marker( 'Beginning' )
f = open( os.path.join( self.top_dir, relative_input_path ), 'rt' )
content = f.read()
if wrap_in_comment:
content = '/*\n' + content + '\n*/'
self.add_text( content )
f.close()
add_marker( 'End' )
self.add_text( '\n\n\n\n' )
def get_value( self ):
return ''.join( self.blocks ).replace('\r\n','\n')
def write_to( self, output_path ):
output_dir = os.path.dirname( output_path )
if output_dir and not os.path.isdir( output_dir ):
os.makedirs( output_dir )
f = open( output_path, 'wb' )
f.write( self.get_value() )
f.close()
def amalgamate_source( source_top_dir=None,
target_source_path=None,
header_include_path=None ):
"""Produces amalgated source.
Parameters:
source_top_dir: top-directory
target_source_path: output .cpp path
header_include_path: generated header path relative to target_source_path.
"""
print 'Amalgating header...'
header = AmalgamationFile( source_top_dir )
header.add_text( '/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).' )
header.add_text( '/// It is intented to be used with #include <%s>' % header_include_path )
header.add_file( 'LICENSE', wrap_in_comment=True )
header.add_text( '#ifndef JSON_AMALGATED_H_INCLUDED' )
header.add_text( '# define JSON_AMALGATED_H_INCLUDED' )
header.add_text( '/// If defined, indicates that the source file is amalgated' )
header.add_text( '/// to prevent private header inclusion.' )
header.add_text( '#define JSON_IS_AMALGAMATION' )
header.add_file( 'include/json/version.h' )
header.add_file( 'include/json/config.h' )
header.add_file( 'include/json/forwards.h' )
header.add_file( 'include/json/features.h' )
header.add_file( 'include/json/value.h' )
header.add_file( 'include/json/reader.h' )
header.add_file( 'include/json/writer.h' )
header.add_file( 'include/json/assertions.h' )
header.add_text( '#endif //ifndef JSON_AMALGATED_H_INCLUDED' )
target_header_path = os.path.join( os.path.dirname(target_source_path), header_include_path )
print 'Writing amalgated header to %r' % target_header_path
header.write_to( target_header_path )
base, ext = os.path.splitext( header_include_path )
forward_header_include_path = base + '-forwards' + ext
print 'Amalgating forward header...'
header = AmalgamationFile( source_top_dir )
header.add_text( '/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).' )
header.add_text( '/// It is intented to be used with #include <%s>' % forward_header_include_path )
header.add_text( '/// This header provides forward declaration for all JsonCpp types.' )
header.add_file( 'LICENSE', wrap_in_comment=True )
header.add_text( '#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED' )
header.add_text( '# define JSON_FORWARD_AMALGATED_H_INCLUDED' )
header.add_text( '/// If defined, indicates that the source file is amalgated' )
header.add_text( '/// to prevent private header inclusion.' )
header.add_text( '#define JSON_IS_AMALGAMATION' )
header.add_file( 'include/json/config.h' )
header.add_file( 'include/json/forwards.h' )
header.add_text( '#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED' )
target_forward_header_path = os.path.join( os.path.dirname(target_source_path),
forward_header_include_path )
print 'Writing amalgated forward header to %r' % target_forward_header_path
header.write_to( target_forward_header_path )
print 'Amalgating source...'
source = AmalgamationFile( source_top_dir )
source.add_text( '/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).' )
source.add_text( '/// It is intented to be used with #include <%s>' % header_include_path )
source.add_file( 'LICENSE', wrap_in_comment=True )
source.add_text( '' )
source.add_text( '#include <%s>' % header_include_path )
source.add_text( '' )
lib_json = 'src/lib_json'
source.add_file( os.path.join(lib_json, 'json_tool.h') )
source.add_file( os.path.join(lib_json, 'json_reader.cpp') )
source.add_file( os.path.join(lib_json, 'json_batchallocator.h') )
source.add_file( os.path.join(lib_json, 'json_valueiterator.inl') )
source.add_file( os.path.join(lib_json, 'json_value.cpp') )
source.add_file( os.path.join(lib_json, 'json_writer.cpp') )
print 'Writing amalgated source to %r' % target_source_path
source.write_to( target_source_path )
def main():
usage = """%prog [options]
Generate a single amalgated source and header file from the sources.
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option('-s', '--source', dest="target_source_path", action='store', default='dist/jsoncpp.cpp',
help="""Output .cpp source path. [Default: %default]""")
parser.add_option('-i', '--include', dest="header_include_path", action='store', default='json/json.h',
help="""Header include path. Used to include the header from the amalgated source file. [Default: %default]""")
parser.add_option('-t', '--top-dir', dest="top_dir", action='store', default=os.getcwd(),
help="""Source top-directory. [Default: %default]""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
msg = amalgamate_source( source_top_dir=options.top_dir,
target_source_path=options.target_source_path,
header_include_path=options.header_include_path )
if msg:
sys.stderr.write( msg + '\n' )
sys.exit( 1 )
else:
print 'Source succesfully amalagated'
if __name__ == '__main__':
main()

33
devtools/agent_vmw7.json Normal file
View File

@@ -0,0 +1,33 @@
{
"cmake_variants" : [
{"name": "generator",
"generators": [
{"generator": [
"Visual Studio 7 .NET 2003",
"Visual Studio 9 2008",
"Visual Studio 9 2008 Win64",
"Visual Studio 10",
"Visual Studio 10 Win64",
"Visual Studio 11",
"Visual Studio 11 Win64"
]
},
{"generator": ["MinGW Makefiles"],
"env_prepend": [{"path": "c:/wut/prg/MinGW/bin"}]
}
]
},
{"name": "shared_dll",
"variables": [
["JSONCPP_LIB_BUILD_SHARED=true"],
["JSONCPP_LIB_BUILD_SHARED=false"]
]
},
{"name": "build_type",
"build_types": [
"debug",
"release"
]
}
]
}

26
devtools/agent_vmxp.json Normal file
View File

@@ -0,0 +1,26 @@
{
"cmake_variants" : [
{"name": "generator",
"generators": [
{"generator": [
"Visual Studio 6",
"Visual Studio 7",
"Visual Studio 8 2005"
]
}
]
},
{"name": "shared_dll",
"variables": [
["JSONCPP_LIB_BUILD_SHARED=true"],
["JSONCPP_LIB_BUILD_SHARED=false"]
]
},
{"name": "build_type",
"build_types": [
"debug",
"release"
]
}
]
}

View File

@@ -55,20 +55,20 @@ ALL = DIR | FILE | LINKS
_ANT_RE = re.compile( r'(/\*\*/)|(\*\*/)|(/\*\*)|(\*)|(/)|([^\*/]*)' )
def ant_pattern_to_re( ant_pattern ):
"""Generates a regular expression from the ant pattern.
Matching convention:
**/a: match 'a', 'dir/a', 'dir1/dir2/a'
a/**/b: match 'a/b', 'a/c/b', 'a/d/c/b'
*.py: match 'script.py' but not 'a/script.py'
def ant_pattern_to_re( ant_pattern ):
"""Generates a regular expression from the ant pattern.
Matching convention:
**/a: match 'a', 'dir/a', 'dir1/dir2/a'
a/**/b: match 'a/b', 'a/c/b', 'a/d/c/b'
*.py: match 'script.py' but not 'a/script.py'
"""
rex = ['^']
next_pos = 0
sep_rex = r'(?:/|%s)' % re.escape( os.path.sep )
## print 'Converting', ant_pattern
for match in _ANT_RE.finditer( ant_pattern ):
## print 'Matched', match.group()
## print match.start(0), next_pos
sep_rex = r'(?:/|%s)' % re.escape( os.path.sep )
## print 'Converting', ant_pattern
for match in _ANT_RE.finditer( ant_pattern ):
## print 'Matched', match.group()
## print match.start(0), next_pos
if match.start(0) != next_pos:
raise ValueError( "Invalid ant pattern" )
if match.group(1): # /**/
@@ -83,14 +83,14 @@ def ant_pattern_to_re( ant_pattern ):
rex.append( sep_rex )
else: # somepath
rex.append( re.escape(match.group(6)) )
next_pos = match.end()
next_pos = match.end()
rex.append('$')
return re.compile( ''.join( rex ) )
def _as_list( l ):
if isinstance(l, basestring):
return l.split()
return l
def _as_list( l ):
if isinstance(l, basestring):
return l.split()
return l
def glob(dir_path,
includes = '**/*',
@@ -99,8 +99,8 @@ def glob(dir_path,
prune_dirs = prune_dirs,
max_depth = 25):
include_filter = [ant_pattern_to_re(p) for p in _as_list(includes)]
exclude_filter = [ant_pattern_to_re(p) for p in _as_list(excludes)]
prune_dirs = [p.replace('/',os.path.sep) for p in _as_list(prune_dirs)]
exclude_filter = [ant_pattern_to_re(p) for p in _as_list(excludes)]
prune_dirs = [p.replace('/',os.path.sep) for p in _as_list(prune_dirs)]
dir_path = dir_path.replace('/',os.path.sep)
entry_type_filter = entry_type
@@ -117,37 +117,37 @@ def glob(dir_path,
return True
return False
def glob_impl( root_dir_path ):
child_dirs = [root_dir_path]
while child_dirs:
def glob_impl( root_dir_path ):
child_dirs = [root_dir_path]
while child_dirs:
dir_path = child_dirs.pop()
for entry in listdir( dir_path ):
full_path = os.path.join( dir_path, entry )
## print 'Testing:', full_path,
is_dir = os.path.isdir( full_path )
if is_dir and not is_pruned_dir( entry ): # explore child directory ?
## print '===> marked for recursion',
child_dirs.append( full_path )
included = apply_filter( full_path, include_filter )
rejected = apply_filter( full_path, exclude_filter )
if not included or rejected: # do not include entry ?
## print '=> not included or rejected'
continue
link = os.path.islink( full_path )
is_file = os.path.isfile( full_path )
if not is_file and not is_dir:
## print '=> unknown entry type'
continue
if link:
entry_type = is_file and FILE_LINK or DIR_LINK
else:
entry_type = is_file and FILE or DIR
## print '=> type: %d' % entry_type,
if (entry_type & entry_type_filter) != 0:
## print ' => KEEP'
yield os.path.join( dir_path, entry )
## else:
## print ' => TYPE REJECTED'
for entry in listdir( dir_path ):
full_path = os.path.join( dir_path, entry )
## print 'Testing:', full_path,
is_dir = os.path.isdir( full_path )
if is_dir and not is_pruned_dir( entry ): # explore child directory ?
## print '===> marked for recursion',
child_dirs.append( full_path )
included = apply_filter( full_path, include_filter )
rejected = apply_filter( full_path, exclude_filter )
if not included or rejected: # do not include entry ?
## print '=> not included or rejected'
continue
link = os.path.islink( full_path )
is_file = os.path.isfile( full_path )
if not is_file and not is_dir:
## print '=> unknown entry type'
continue
if link:
entry_type = is_file and FILE_LINK or DIR_LINK
else:
entry_type = is_file and FILE or DIR
## print '=> type: %d' % entry_type,
if (entry_type & entry_type_filter) != 0:
## print ' => KEEP'
yield os.path.join( dir_path, entry )
## else:
## print ' => TYPE REJECTED'
return list( glob_impl( dir_path ) )
@@ -155,47 +155,47 @@ if __name__ == "__main__":
import unittest
class AntPatternToRETest(unittest.TestCase):
## def test_conversion( self ):
## self.assertEqual( '^somepath$', ant_pattern_to_re( 'somepath' ).pattern )
def test_matching( self ):
test_cases = [ ( 'path',
['path'],
['somepath', 'pathsuffix', '/path', '/path'] ),
( '*.py',
['source.py', 'source.ext.py', '.py'],
['path/source.py', '/.py', 'dir.py/z', 'z.pyc', 'z.c'] ),
( '**/path',
['path', '/path', '/a/path', 'c:/a/path', '/a/b/path', '//a/path', '/a/path/b/path'],
['path/', 'a/path/b', 'dir.py/z', 'somepath', 'pathsuffix', 'a/somepath'] ),
( 'path/**',
['path/a', 'path/path/a', 'path//'],
['path', 'somepath/a', 'a/path', 'a/path/a', 'pathsuffix/a'] ),
( '/**/path',
['/path', '/a/path', '/a/b/path/path', '/path/path'],
['path', 'path/', 'a/path', '/pathsuffix', '/somepath'] ),
( 'a/b',
['a/b'],
['somea/b', 'a/bsuffix', 'a/b/c'] ),
( '**/*.py',
['script.py', 'src/script.py', 'a/b/script.py', '/a/b/script.py'],
['script.pyc', 'script.pyo', 'a.py/b'] ),
( 'src/**/*.py',
['src/a.py', 'src/dir/a.py'],
['a/src/a.py', '/src/a.py'] ),
]
for ant_pattern, accepted_matches, rejected_matches in list(test_cases):
def local_path( paths ):
return [ p.replace('/',os.path.sep) for p in paths ]
test_cases.append( (ant_pattern, local_path(accepted_matches), local_path( rejected_matches )) )
for ant_pattern, accepted_matches, rejected_matches in test_cases:
rex = ant_pattern_to_re( ant_pattern )
print 'ant_pattern:', ant_pattern, ' => ', rex.pattern
for accepted_match in accepted_matches:
print 'Accepted?:', accepted_match
self.assert_( rex.match( accepted_match ) is not None )
for rejected_match in rejected_matches:
print 'Rejected?:', rejected_match
self.assert_( rex.match( rejected_match ) is None )
## def test_conversion( self ):
## self.assertEqual( '^somepath$', ant_pattern_to_re( 'somepath' ).pattern )
def test_matching( self ):
test_cases = [ ( 'path',
['path'],
['somepath', 'pathsuffix', '/path', '/path'] ),
( '*.py',
['source.py', 'source.ext.py', '.py'],
['path/source.py', '/.py', 'dir.py/z', 'z.pyc', 'z.c'] ),
( '**/path',
['path', '/path', '/a/path', 'c:/a/path', '/a/b/path', '//a/path', '/a/path/b/path'],
['path/', 'a/path/b', 'dir.py/z', 'somepath', 'pathsuffix', 'a/somepath'] ),
( 'path/**',
['path/a', 'path/path/a', 'path//'],
['path', 'somepath/a', 'a/path', 'a/path/a', 'pathsuffix/a'] ),
( '/**/path',
['/path', '/a/path', '/a/b/path/path', '/path/path'],
['path', 'path/', 'a/path', '/pathsuffix', '/somepath'] ),
( 'a/b',
['a/b'],
['somea/b', 'a/bsuffix', 'a/b/c'] ),
( '**/*.py',
['script.py', 'src/script.py', 'a/b/script.py', '/a/b/script.py'],
['script.pyc', 'script.pyo', 'a.py/b'] ),
( 'src/**/*.py',
['src/a.py', 'src/dir/a.py'],
['a/src/a.py', '/src/a.py'] ),
]
for ant_pattern, accepted_matches, rejected_matches in list(test_cases):
def local_path( paths ):
return [ p.replace('/',os.path.sep) for p in paths ]
test_cases.append( (ant_pattern, local_path(accepted_matches), local_path( rejected_matches )) )
for ant_pattern, accepted_matches, rejected_matches in test_cases:
rex = ant_pattern_to_re( ant_pattern )
print 'ant_pattern:', ant_pattern, ' => ', rex.pattern
for accepted_match in accepted_matches:
print 'Accepted?:', accepted_match
self.assert_( rex.match( accepted_match ) is not None )
for rejected_match in rejected_matches:
print 'Rejected?:', rejected_match
self.assert_( rex.match( rejected_match ) is None )
unittest.main()

280
devtools/batchbuild.py Normal file
View File

@@ -0,0 +1,280 @@
import collections
import itertools
import json
import os
import os.path
import re
import shutil
import string
import subprocess
import sys
import cgi
class BuildDesc:
def __init__(self, prepend_envs=None, variables=None, build_type=None, generator=None):
self.prepend_envs = prepend_envs or [] # [ { "var": "value" } ]
self.variables = variables or []
self.build_type = build_type
self.generator = generator
def merged_with( self, build_desc ):
"""Returns a new BuildDesc by merging field content.
Prefer build_desc fields to self fields for single valued field.
"""
return BuildDesc( self.prepend_envs + build_desc.prepend_envs,
self.variables + build_desc.variables,
build_desc.build_type or self.build_type,
build_desc.generator or self.generator )
def env( self ):
environ = os.environ.copy()
for values_by_name in self.prepend_envs:
for var, value in values_by_name.items():
var = var.upper()
if type(value) is unicode:
value = value.encode( sys.getdefaultencoding() )
if var in environ:
environ[var] = value + os.pathsep + environ[var]
else:
environ[var] = value
return environ
def cmake_args( self ):
args = ["-D%s" % var for var in self.variables]
# skip build type for Visual Studio solution as it cause warning
if self.build_type and 'Visual' not in self.generator:
args.append( "-DCMAKE_BUILD_TYPE=%s" % self.build_type )
if self.generator:
args.extend( ['-G', self.generator] )
return args
def __repr__( self ):
return "BuildDesc( %s, build_type=%s )" % (" ".join( self.cmake_args()), self.build_type)
class BuildData:
def __init__( self, desc, work_dir, source_dir ):
self.desc = desc
self.work_dir = work_dir
self.source_dir = source_dir
self.cmake_log_path = os.path.join( work_dir, 'batchbuild_cmake.log' )
self.build_log_path = os.path.join( work_dir, 'batchbuild_build.log' )
self.cmake_succeeded = False
self.build_succeeded = False
def execute_build(self):
print 'Build %s' % self.desc
self._make_new_work_dir( )
self.cmake_succeeded = self._generate_makefiles( )
if self.cmake_succeeded:
self.build_succeeded = self._build_using_makefiles( )
return self.build_succeeded
def _generate_makefiles(self):
print ' Generating makefiles: ',
cmd = ['cmake'] + self.desc.cmake_args( ) + [os.path.abspath( self.source_dir )]
succeeded = self._execute_build_subprocess( cmd, self.desc.env(), self.cmake_log_path )
print 'done' if succeeded else 'FAILED'
return succeeded
def _build_using_makefiles(self):
print ' Building:',
cmd = ['cmake', '--build', self.work_dir]
if self.desc.build_type:
cmd += ['--config', self.desc.build_type]
succeeded = self._execute_build_subprocess( cmd, self.desc.env(), self.build_log_path )
print 'done' if succeeded else 'FAILED'
return succeeded
def _execute_build_subprocess(self, cmd, env, log_path):
process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.work_dir,
env=env )
stdout, _ = process.communicate( )
succeeded = (process.returncode == 0)
with open( log_path, 'wb' ) as flog:
log = ' '.join( cmd ) + '\n' + stdout + '\nExit code: %r\n' % process.returncode
flog.write( fix_eol( log ) )
return succeeded
def _make_new_work_dir(self):
if os.path.isdir( self.work_dir ):
print ' Removing work directory', self.work_dir
shutil.rmtree( self.work_dir, ignore_errors=True )
if not os.path.isdir( self.work_dir ):
os.makedirs( self.work_dir )
def fix_eol( stdout ):
"""Fixes wrong EOL produced by cmake --build on Windows (\r\r\n instead of \r\n).
"""
return re.sub( '\r*\n', os.linesep, stdout )
def load_build_variants_from_config( config_path ):
with open( config_path, 'rb' ) as fconfig:
data = json.load( fconfig )
variants = data[ 'cmake_variants' ]
build_descs_by_axis = collections.defaultdict( list )
for axis in variants:
axis_name = axis["name"]
build_descs = []
if "generators" in axis:
for generator_data in axis["generators"]:
for generator in generator_data["generator"]:
build_desc = BuildDesc( generator=generator,
prepend_envs=generator_data.get("env_prepend") )
build_descs.append( build_desc )
elif "variables" in axis:
for variables in axis["variables"]:
build_desc = BuildDesc( variables=variables )
build_descs.append( build_desc )
elif "build_types" in axis:
for build_type in axis["build_types"]:
build_desc = BuildDesc( build_type=build_type )
build_descs.append( build_desc )
build_descs_by_axis[axis_name].extend( build_descs )
return build_descs_by_axis
def generate_build_variants( build_descs_by_axis ):
"""Returns a list of BuildDesc generated for the partial BuildDesc for each axis."""
axis_names = build_descs_by_axis.keys()
build_descs = []
for axis_name, axis_build_descs in build_descs_by_axis.items():
if len(build_descs):
# for each existing build_desc and each axis build desc, create a new build_desc
new_build_descs = []
for prototype_build_desc, axis_build_desc in itertools.product( build_descs, axis_build_descs):
new_build_descs.append( prototype_build_desc.merged_with( axis_build_desc ) )
build_descs = new_build_descs
else:
build_descs = axis_build_descs
return build_descs
HTML_TEMPLATE = string.Template('''<html>
<head>
<title>$title</title>
<style type="text/css">
td.failed {background-color:#f08080;}
td.ok {background-color:#c0eec0;}
</style>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>Variables</th>
$th_vars
</tr>
<tr>
<th>Build type</th>
$th_build_types
</tr>
</thead>
<tbody>
$tr_builds
</tbody>
</table>
</body></html>''')
def generate_html_report( html_report_path, builds ):
report_dir = os.path.dirname( html_report_path )
# Vertical axis: generator
# Horizontal: variables, then build_type
builds_by_generator = collections.defaultdict( list )
variables = set()
build_types_by_variable = collections.defaultdict( set )
build_by_pos_key = {} # { (generator, var_key, build_type): build }
for build in builds:
builds_by_generator[build.desc.generator].append( build )
var_key = tuple(sorted(build.desc.variables))
variables.add( var_key )
build_types_by_variable[var_key].add( build.desc.build_type )
pos_key = (build.desc.generator, var_key, build.desc.build_type)
build_by_pos_key[pos_key] = build
variables = sorted( variables )
th_vars = []
th_build_types = []
for variable in variables:
build_types = sorted( build_types_by_variable[variable] )
nb_build_type = len(build_types_by_variable[variable])
th_vars.append( '<th colspan="%d">%s</th>' % (nb_build_type, cgi.escape( ' '.join( variable ) ) ) )
for build_type in build_types:
th_build_types.append( '<th>%s</th>' % cgi.escape(build_type) )
tr_builds = []
for generator in sorted( builds_by_generator ):
tds = [ '<td>%s</td>\n' % cgi.escape( generator ) ]
for variable in variables:
build_types = sorted( build_types_by_variable[variable] )
for build_type in build_types:
pos_key = (generator, variable, build_type)
build = build_by_pos_key.get(pos_key)
if build:
cmake_status = 'ok' if build.cmake_succeeded else 'FAILED'
build_status = 'ok' if build.build_succeeded else 'FAILED'
cmake_log_url = os.path.relpath( build.cmake_log_path, report_dir )
build_log_url = os.path.relpath( build.build_log_path, report_dir )
td = '<td class="%s"><a href="%s" class="%s">CMake: %s</a>' % (
build_status.lower(), cmake_log_url, cmake_status.lower(), cmake_status)
if build.cmake_succeeded:
td += '<br><a href="%s" class="%s">Build: %s</a>' % (
build_log_url, build_status.lower(), build_status)
td += '</td>'
else:
td = '<td></td>'
tds.append( td )
tr_builds.append( '<tr>%s</tr>' % '\n'.join( tds ) )
html = HTML_TEMPLATE.substitute(
title='Batch build report',
th_vars=' '.join(th_vars),
th_build_types=' '.join( th_build_types),
tr_builds='\n'.join( tr_builds ) )
with open( html_report_path, 'wt' ) as fhtml:
fhtml.write( html )
print 'HTML report generated in:', html_report_path
def main():
usage = r"""%prog WORK_DIR SOURCE_DIR CONFIG_JSON_PATH [CONFIG2_JSON_PATH...]
Build a given CMake based project located in SOURCE_DIR with multiple generators/options.dry_run
as described in CONFIG_JSON_PATH building in WORK_DIR.
Example of call:
python devtools\batchbuild.py e:\buildbots\jsoncpp\build . devtools\agent_vmw7.json
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = True
# parser.add_option('-v', '--verbose', dest="verbose", action='store_true',
# help="""Be verbose.""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
if len(args) < 3:
parser.error( "Missing one of WORK_DIR SOURCE_DIR CONFIG_JSON_PATH." )
work_dir = args[0]
source_dir = args[1].rstrip('/\\')
config_paths = args[2:]
for config_path in config_paths:
if not os.path.isfile( config_path ):
parser.error( "Can not read: %r" % config_path )
# generate build variants
build_descs = []
for config_path in config_paths:
build_descs_by_axis = load_build_variants_from_config( config_path )
build_descs.extend( generate_build_variants( build_descs_by_axis ) )
print 'Build variants (%d):' % len(build_descs)
# assign build directory for each variant
if not os.path.isdir( work_dir ):
os.makedirs( work_dir )
builds = []
with open( os.path.join( work_dir, 'matrix-dir-map.txt' ), 'wt' ) as fmatrixmap:
for index, build_desc in enumerate( build_descs ):
build_desc_work_dir = os.path.join( work_dir, '%03d' % (index+1) )
builds.append( BuildData( build_desc, build_desc_work_dir, source_dir ) )
fmatrixmap.write( '%s: %s\n' % (build_desc_work_dir, build_desc) )
for build in builds:
build.execute_build()
html_report_path = os.path.join( work_dir, 'batchbuild-report.html' )
generate_html_report( html_report_path, builds )
print 'Done'
if __name__ == '__main__':
main()

View File

@@ -1,63 +1,63 @@
import os.path
def fix_source_eol( path, is_dry_run = True, verbose = True, eol = '\n' ):
"""Makes sure that all sources have the specified eol sequence (default: unix)."""
if not os.path.isfile( path ):
raise ValueError( 'Path "%s" is not a file' % path )
try:
f = open(path, 'rb')
except IOError, msg:
print >> sys.stderr, "%s: I/O Error: %s" % (file, str(msg))
return False
try:
raw_lines = f.readlines()
finally:
f.close()
fixed_lines = [line.rstrip('\r\n') + eol for line in raw_lines]
if raw_lines != fixed_lines:
print '%s =>' % path,
if not is_dry_run:
f = open(path, "wb")
try:
f.writelines(fixed_lines)
finally:
f.close()
if verbose:
print is_dry_run and ' NEED FIX' or ' FIXED'
return True
##
##
##
##def _do_fix( is_dry_run = True ):
## from waftools import antglob
## python_sources = antglob.glob( '.',
## includes = '**/*.py **/wscript **/wscript_build',
## excludes = antglob.default_excludes + './waf.py',
## prune_dirs = antglob.prune_dirs + 'waf-* ./build' )
## for path in python_sources:
## _fix_python_source( path, is_dry_run )
##
## cpp_sources = antglob.glob( '.',
## includes = '**/*.cpp **/*.h **/*.inl',
## prune_dirs = antglob.prune_dirs + 'waf-* ./build' )
## for path in cpp_sources:
## _fix_source_eol( path, is_dry_run )
##
##
##def dry_fix(context):
## _do_fix( is_dry_run = True )
##
##def fix(context):
## _do_fix( is_dry_run = False )
##
##def shutdown():
## pass
##
##def check(context):
## # Unit tests are run when "check" target is used
## ut = UnitTest.unit_test()
## ut.change_to_testfile_dir = True
## ut.want_to_see_test_output = True
## ut.want_to_see_test_error = True
## ut.run()
## ut.print_results()
import os.path
def fix_source_eol( path, is_dry_run = True, verbose = True, eol = '\n' ):
"""Makes sure that all sources have the specified eol sequence (default: unix)."""
if not os.path.isfile( path ):
raise ValueError( 'Path "%s" is not a file' % path )
try:
f = open(path, 'rb')
except IOError, msg:
print >> sys.stderr, "%s: I/O Error: %s" % (file, str(msg))
return False
try:
raw_lines = f.readlines()
finally:
f.close()
fixed_lines = [line.rstrip('\r\n') + eol for line in raw_lines]
if raw_lines != fixed_lines:
print '%s =>' % path,
if not is_dry_run:
f = open(path, "wb")
try:
f.writelines(fixed_lines)
finally:
f.close()
if verbose:
print is_dry_run and ' NEED FIX' or ' FIXED'
return True
##
##
##
##def _do_fix( is_dry_run = True ):
## from waftools import antglob
## python_sources = antglob.glob( '.',
## includes = '**/*.py **/wscript **/wscript_build',
## excludes = antglob.default_excludes + './waf.py',
## prune_dirs = antglob.prune_dirs + 'waf-* ./build' )
## for path in python_sources:
## _fix_python_source( path, is_dry_run )
##
## cpp_sources = antglob.glob( '.',
## includes = '**/*.cpp **/*.h **/*.inl',
## prune_dirs = antglob.prune_dirs + 'waf-* ./build' )
## for path in cpp_sources:
## _fix_source_eol( path, is_dry_run )
##
##
##def dry_fix(context):
## _do_fix( is_dry_run = True )
##
##def fix(context):
## _do_fix( is_dry_run = False )
##
##def shutdown():
## pass
##
##def check(context):
## # Unit tests are run when "check" target is used
## ut = UnitTest.unit_test()
## ut.change_to_testfile_dir = True
## ut.want_to_see_test_output = True
## ut.want_to_see_test_error = True
## ut.run()
## ut.print_results()

View File

@@ -1,93 +1,93 @@
"""Updates the license text in source file.
"""
# An existing license is found if the file starts with the string below,
# and ends with the first blank line.
LICENSE_BEGIN = "// Copyright "
BRIEF_LICENSE = LICENSE_BEGIN + """2007-2010 Baptiste Lepilleur
// 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
""".replace('\r\n','\n')
def update_license( path, dry_run, show_diff ):
"""Update the license statement in the specified file.
Parameters:
path: path of the C++ source file to update.
dry_run: if True, just print the path of the file that would be updated,
but don't change it.
show_diff: if True, print the path of the file that would be modified,
as well as the change made to the file.
"""
with open( path, 'rt' ) as fin:
original_text = fin.read().replace('\r\n','\n')
newline = fin.newlines and fin.newlines[0] or '\n'
if not original_text.startswith( LICENSE_BEGIN ):
# No existing license found => prepend it
new_text = BRIEF_LICENSE + original_text
else:
license_end_index = original_text.index( '\n\n' ) # search first blank line
new_text = BRIEF_LICENSE + original_text[license_end_index+2:]
if original_text != new_text:
if not dry_run:
with open( path, 'wb' ) as fout:
fout.write( new_text.replace('\n', newline ) )
print 'Updated', path
if show_diff:
import difflib
print '\n'.join( difflib.unified_diff( original_text.split('\n'),
new_text.split('\n') ) )
return True
return False
def update_license_in_source_directories( source_dirs, dry_run, show_diff ):
"""Updates license text in C++ source files found in directory source_dirs.
Parameters:
source_dirs: list of directory to scan for C++ sources. Directories are
scanned recursively.
dry_run: if True, just print the path of the file that would be updated,
but don't change it.
show_diff: if True, print the path of the file that would be modified,
as well as the change made to the file.
"""
from devtools import antglob
prune_dirs = antglob.prune_dirs + 'scons-local* ./build* ./libs ./dist'
for source_dir in source_dirs:
cpp_sources = antglob.glob( source_dir,
includes = '''**/*.h **/*.cpp **/*.inl''',
prune_dirs = prune_dirs )
for source in cpp_sources:
update_license( source, dry_run, show_diff )
def main():
usage = """%prog DIR [DIR2...]
Updates license text in sources of the project in source files found
in the directory specified on the command-line.
Example of call:
python devtools\licenseupdater.py include src -n --diff
=> Show change that would be made to the sources.
python devtools\licenseupdater.py include src
=> Update license statement on all sources in directories include/ and src/.
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option('-n', '--dry-run', dest="dry_run", action='store_true', default=False,
help="""Only show what files are updated, do not update the files""")
parser.add_option('--diff', dest="show_diff", action='store_true', default=False,
help="""On update, show change made to the file.""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
update_license_in_source_directories( args, options.dry_run, options.show_diff )
print 'Done'
if __name__ == '__main__':
import sys
import os.path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
main()
"""Updates the license text in source file.
"""
# An existing license is found if the file starts with the string below,
# and ends with the first blank line.
LICENSE_BEGIN = "// Copyright "
BRIEF_LICENSE = LICENSE_BEGIN + """2007-2010 Baptiste Lepilleur
// 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
""".replace('\r\n','\n')
def update_license( path, dry_run, show_diff ):
"""Update the license statement in the specified file.
Parameters:
path: path of the C++ source file to update.
dry_run: if True, just print the path of the file that would be updated,
but don't change it.
show_diff: if True, print the path of the file that would be modified,
as well as the change made to the file.
"""
with open( path, 'rt' ) as fin:
original_text = fin.read().replace('\r\n','\n')
newline = fin.newlines and fin.newlines[0] or '\n'
if not original_text.startswith( LICENSE_BEGIN ):
# No existing license found => prepend it
new_text = BRIEF_LICENSE + original_text
else:
license_end_index = original_text.index( '\n\n' ) # search first blank line
new_text = BRIEF_LICENSE + original_text[license_end_index+2:]
if original_text != new_text:
if not dry_run:
with open( path, 'wb' ) as fout:
fout.write( new_text.replace('\n', newline ) )
print 'Updated', path
if show_diff:
import difflib
print '\n'.join( difflib.unified_diff( original_text.split('\n'),
new_text.split('\n') ) )
return True
return False
def update_license_in_source_directories( source_dirs, dry_run, show_diff ):
"""Updates license text in C++ source files found in directory source_dirs.
Parameters:
source_dirs: list of directory to scan for C++ sources. Directories are
scanned recursively.
dry_run: if True, just print the path of the file that would be updated,
but don't change it.
show_diff: if True, print the path of the file that would be modified,
as well as the change made to the file.
"""
from devtools import antglob
prune_dirs = antglob.prune_dirs + 'scons-local* ./build* ./libs ./dist'
for source_dir in source_dirs:
cpp_sources = antglob.glob( source_dir,
includes = '''**/*.h **/*.cpp **/*.inl''',
prune_dirs = prune_dirs )
for source in cpp_sources:
update_license( source, dry_run, show_diff )
def main():
usage = """%prog DIR [DIR2...]
Updates license text in sources of the project in source files found
in the directory specified on the command-line.
Example of call:
python devtools\licenseupdater.py include src -n --diff
=> Show change that would be made to the sources.
python devtools\licenseupdater.py include src
=> Update license statement on all sources in directories include/ and src/.
"""
from optparse import OptionParser
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option('-n', '--dry-run', dest="dry_run", action='store_true', default=False,
help="""Only show what files are updated, do not update the files""")
parser.add_option('--diff', dest="show_diff", action='store_true', default=False,
help="""On update, show change made to the file.""")
parser.enable_interspersed_args()
options, args = parser.parse_args()
update_license_in_source_directories( args, options.dry_run, options.show_diff )
print 'Done'
if __name__ == '__main__':
import sys
import os.path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
main()

View File

@@ -1,53 +1,53 @@
import os.path
import gzip
import tarfile
TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
def make_tarball(tarball_path, sources, base_dir, prefix_dir=''):
"""Parameters:
tarball_path: output path of the .tar.gz file
sources: list of sources to include in the tarball, relative to the current directory
base_dir: if a source file is in a sub-directory of base_dir, then base_dir is stripped
from path in the tarball.
prefix_dir: all files stored in the tarball be sub-directory of prefix_dir. Set to ''
to make them child of root.
"""
base_dir = os.path.normpath( os.path.abspath( base_dir ) )
def archive_name( path ):
"""Makes path relative to base_dir."""
path = os.path.normpath( os.path.abspath( path ) )
common_path = os.path.commonprefix( (base_dir, path) )
archive_name = path[len(common_path):]
if os.path.isabs( archive_name ):
archive_name = archive_name[1:]
return os.path.join( prefix_dir, archive_name )
def visit(tar, dirname, names):
for name in names:
path = os.path.join(dirname, name)
if os.path.isfile(path):
path_in_tar = archive_name(path)
tar.add(path, path_in_tar )
compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
tar = tarfile.TarFile.gzopen( tarball_path, 'w', compresslevel=compression )
try:
for source in sources:
source_path = source
if os.path.isdir( source ):
os.path.walk(source_path, visit, tar)
else:
path_in_tar = archive_name(source_path)
tar.add(source_path, path_in_tar ) # filename, arcname
finally:
tar.close()
def decompress( tarball_path, base_dir ):
"""Decompress the gzipped tarball into directory base_dir.
"""
# !!! This class method is not documented in the online doc
# nor is bz2open!
tar = tarfile.TarFile.gzopen(tarball_path, mode='r')
try:
tar.extractall( base_dir )
finally:
tar.close()
import os.path
import gzip
import tarfile
TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
def make_tarball(tarball_path, sources, base_dir, prefix_dir=''):
"""Parameters:
tarball_path: output path of the .tar.gz file
sources: list of sources to include in the tarball, relative to the current directory
base_dir: if a source file is in a sub-directory of base_dir, then base_dir is stripped
from path in the tarball.
prefix_dir: all files stored in the tarball be sub-directory of prefix_dir. Set to ''
to make them child of root.
"""
base_dir = os.path.normpath( os.path.abspath( base_dir ) )
def archive_name( path ):
"""Makes path relative to base_dir."""
path = os.path.normpath( os.path.abspath( path ) )
common_path = os.path.commonprefix( (base_dir, path) )
archive_name = path[len(common_path):]
if os.path.isabs( archive_name ):
archive_name = archive_name[1:]
return os.path.join( prefix_dir, archive_name )
def visit(tar, dirname, names):
for name in names:
path = os.path.join(dirname, name)
if os.path.isfile(path):
path_in_tar = archive_name(path)
tar.add(path, path_in_tar )
compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
tar = tarfile.TarFile.gzopen( tarball_path, 'w', compresslevel=compression )
try:
for source in sources:
source_path = source
if os.path.isdir( source ):
os.path.walk(source_path, visit, tar)
else:
path_in_tar = archive_name(source_path)
tar.add(source_path, path_in_tar ) # filename, arcname
finally:
tar.close()
def decompress( tarball_path, base_dir ):
"""Decompress the gzipped tarball into directory base_dir.
"""
# !!! This class method is not documented in the online doc
# nor is bz2open!
tar = tarfile.TarFile.gzopen(tarball_path, mode='r')
try:
tar.extractall( base_dir )
finally:
tar.close()

View File

@@ -2,6 +2,7 @@
\section ms_release Makes JsonCpp ready for release
- Build system clean-up:
- Fix build on Windows (shared-library build is broken)
- Compile and run tests using shared library on Windows to ensure no JSON_API macro is missing.
- Add enable/disable flag for static and shared library build
- Enhance help
- Platform portability check: (Notes: was ok on last check)

2
include/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
FILE(GLOB INCLUDE_FILES "json/*.h")
INSTALL(FILES ${INCLUDE_FILES} DESTINATION include/json)

32
include/json/assertions.h Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2007-2010 Baptiste Lepilleur
// 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 CPPTL_JSON_ASSERTIONS_H_INCLUDED
# define CPPTL_JSON_ASSERTIONS_H_INCLUDED
#include <stdlib.h>
#if !defined(JSON_IS_AMALGAMATION)
# include <json/config.h>
#endif // if !defined(JSON_IS_AMALGAMATION)
#if JSON_USE_EXCEPTION
# include <stdexcept>
#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw
#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message );
#else // JSON_USE_EXCEPTION
#define JSON_ASSERT( condition ) assert( condition );
// The call to assert() will show the failure message in debug builds. In
// release bugs we write to invalid memory in order to crash hard, so that a
// debugger or crash reporter gets the chance to take over. We still call exit()
// afterward in order to tell the compiler that this macro doesn't return.
#define JSON_FAIL_MESSAGE( message ) { assert(false && message); strcpy(reinterpret_cast<char*>(666), message); exit(123); }
#endif
#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) }
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED

View File

@@ -24,9 +24,11 @@
/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
/// If defined, indicates that Json use exception to report invalid type manipulation
/// instead of C assert macro.
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
# ifndef JSON_USE_EXCEPTION
# define JSON_USE_EXCEPTION 1
# endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
@@ -44,10 +46,17 @@
# ifdef JSON_IN_CPPTL
# define JSON_API CPPTL_API
# elif defined(JSON_DLL_BUILD)
# define JSON_API __declspec(dllexport)
# if defined(_MSC_VER)
# define JSON_API __declspec(dllexport)
# define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
# endif // if defined(_MSC_VER)
# elif defined(JSON_DLL)
# define JSON_API __declspec(dllimport)
# else
# if defined(_MSC_VER)
# define JSON_API __declspec(dllimport)
# define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
# endif // if defined(_MSC_VER)
# endif // ifdef JSON_IN_CPPTL
# if !defined(JSON_API)
# define JSON_API
# endif
@@ -59,6 +68,9 @@
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering with pragma push/pop).
#pragma warning(disable : 4786)
#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008

View File

@@ -42,6 +42,12 @@ namespace Json {
/// \c true if root must be either an array or an object value. Default: \c false.
bool strictRoot_;
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_;
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_;
};
} // namespace Json

View File

@@ -11,9 +11,16 @@
# include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
# include <deque>
# include <iosfwd>
# include <stack>
# include <string>
# include <iostream>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(push)
# pragma warning(disable:4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json {
@@ -26,6 +33,19 @@ namespace Json {
typedef char Char;
typedef const Char *Location;
/** \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
* that this is bytes, not codepoints.
*
*/
struct StructuredError
{
size_t offset_start;
size_t offset_limit;
std::string message;
};
/** \brief Constructs a Reader allowing all features
* for parsing.
*/
@@ -88,6 +108,14 @@ namespace Json {
*/
std::string getFormattedErrorMessages() const;
/** \brief Returns a vector of structured erros encounted while parsing.
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate multiple
* errors. This can occur if the parser recovers from a non-fatal
* parse error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
private:
enum TokenType
{
@@ -139,9 +167,11 @@ namespace Json {
bool readObject( Token &token );
bool readArray( Token &token );
bool decodeNumber( Token &token );
bool decodeNumber( Token &token, Value &decoded );
bool decodeString( Token &token );
bool decodeString( Token &token, std::string &decoded );
bool decodeDouble( Token &token );
bool decodeDouble( Token &token, Value &decoded );
bool decodeUnicodeCodePoint( Token &token,
Location &current,
Location end,
@@ -207,8 +237,13 @@ namespace Json {
\throw std::exception on parse error.
\see Json::operator<<()
*/
std::istream& operator>>( std::istream&, Value& );
JSON_API std::istream& operator>>( std::istream&, Value& );
} // namespace Json
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_READER_H_INCLUDED

View File

@@ -21,6 +21,13 @@
# include <cpptl/forwards.h>
# endif
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(push)
# pragma warning(disable:4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
@@ -151,12 +158,14 @@ namespace Json {
/// Maximum unsigned int value that can be stored in a Json::Value.
static const UInt maxUInt;
# if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 minInt64;
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 maxInt64;
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static const UInt64 maxUInt64;
#endif // defined(JSON_HAS_INT64)
private:
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
@@ -265,8 +274,10 @@ namespace Json {
# endif
Int asInt() const;
UInt asUInt() const;
#if defined(JSON_HAS_INT64)
Int64 asInt64() const;
UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
float asFloat() const;
@@ -276,7 +287,9 @@ namespace Json {
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;
@@ -429,6 +442,13 @@ namespace Json {
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart( size_t start );
void setOffsetLimit( size_t limit );
size_t getOffsetStart() const;
size_t getOffsetLimit() const;
private:
Value &resolveReference( const char *key,
bool isStatic );
@@ -496,12 +516,17 @@ namespace Json {
int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
# endif
CommentInfo *comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
size_t start_;
size_t limit_;
};
/** \brief Experimental and untested: represents an element of the "path" to access a node.
*/
class PathArgument
class JSON_API PathArgument
{
public:
friend class Path;
@@ -534,7 +559,7 @@ namespace Json {
* - ".%" => member name is provided as parameter
* - ".[%]" => index is provied as parameter
*/
class Path
class JSON_API Path
{
public:
Path( const std::string &path,
@@ -910,9 +935,10 @@ public: // overridden from ValueArrayAllocator
/** \brief base class for Value iterators.
*
*/
class ValueIteratorBase
class JSON_API ValueIteratorBase
{
public:
typedef std::bidirectional_iterator_tag iterator_category;
typedef unsigned int size_t;
typedef int difference_type;
typedef ValueIteratorBase SelfType;
@@ -980,10 +1006,11 @@ public: // overridden from ValueArrayAllocator
/** \brief const iterator for object and array value.
*
*/
class ValueConstIterator : public ValueIteratorBase
class JSON_API ValueConstIterator : public ValueIteratorBase
{
friend class Value;
public:
typedef const Value value_type;
typedef unsigned int size_t;
typedef int difference_type;
typedef const Value &reference;
@@ -1038,10 +1065,11 @@ public: // overridden from ValueArrayAllocator
/** \brief Iterator for object and array value.
*/
class ValueIterator : public ValueIteratorBase
class JSON_API ValueIterator : public ValueIteratorBase
{
friend class Value;
public:
typedef Value value_type;
typedef unsigned int size_t;
typedef int difference_type;
typedef Value &reference;
@@ -1100,4 +1128,9 @@ public: // overridden from ValueArrayAllocator
} // namespace Json
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_H_INCLUDED

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

@@ -0,0 +1,14 @@
// DO NOT EDIT. This file is generated by CMake from "version"
// and "version.h.in" files.
// Run CMake configure step to update it.
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "0.6.0-dev"
# define JSONCPP_VERSION_MAJOR 0
# define JSONCPP_VERSION_MINOR 6
# define JSONCPP_VERSION_PATCH 0
# define JSONCPP_VERSION_QUALIFIER -dev
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
#endif // JSON_VERSION_H_INCLUDED

View File

@@ -11,7 +11,13 @@
#endif // if !defined(JSON_IS_AMALGAMATION)
# include <vector>
# include <string>
# include <iostream>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(push)
# pragma warning(disable:4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json {
@@ -41,6 +47,13 @@ namespace Json {
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* 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
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
public: // overridden from Writer
virtual std::string write( const Value &root );
@@ -49,6 +62,7 @@ namespace Json {
std::string document_;
bool yamlCompatiblityEnabled_;
bool dropNullPlaceholders_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
@@ -176,10 +190,14 @@ namespace Json {
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
std::ostream& operator<<( std::ostream&, const Value &root );
JSON_API std::ostream& operator<<( std::ostream&, const Value &root );
} // namespace Json
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
# pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED

View File

@@ -1,53 +1,53 @@
import fnmatch
import os
def generate( env ):
def Glob( env, includes = None, excludes = None, dir = '.' ):
"""Adds Glob( includes = Split( '*' ), excludes = None, dir = '.')
helper function to environment.
Glob both the file-system files.
includes: list of file name pattern included in the return list when matched.
excludes: list of file name pattern exluced from the return list.
Example:
sources = env.Glob( ("*.cpp", '*.h'), "~*.cpp", "#src" )
"""
def filterFilename(path):
abs_path = os.path.join( dir, path )
if not os.path.isfile(abs_path):
return 0
fn = os.path.basename(path)
match = 0
for include in includes:
if fnmatch.fnmatchcase( fn, include ):
match = 1
break
if match == 1 and not excludes is None:
for exclude in excludes:
if fnmatch.fnmatchcase( fn, exclude ):
match = 0
break
return match
if includes is None:
includes = ('*',)
elif type(includes) in ( type(''), type(u'') ):
includes = (includes,)
if type(excludes) in ( type(''), type(u'') ):
excludes = (excludes,)
dir = env.Dir(dir).abspath
paths = os.listdir( dir )
def makeAbsFileNode( path ):
return env.File( os.path.join( dir, path ) )
nodes = filter( filterFilename, paths )
return map( makeAbsFileNode, nodes )
from SCons.Script import Environment
import fnmatch
import os
def generate( env ):
def Glob( env, includes = None, excludes = None, dir = '.' ):
"""Adds Glob( includes = Split( '*' ), excludes = None, dir = '.')
helper function to environment.
Glob both the file-system files.
includes: list of file name pattern included in the return list when matched.
excludes: list of file name pattern exluced from the return list.
Example:
sources = env.Glob( ("*.cpp", '*.h'), "~*.cpp", "#src" )
"""
def filterFilename(path):
abs_path = os.path.join( dir, path )
if not os.path.isfile(abs_path):
return 0
fn = os.path.basename(path)
match = 0
for include in includes:
if fnmatch.fnmatchcase( fn, include ):
match = 1
break
if match == 1 and not excludes is None:
for exclude in excludes:
if fnmatch.fnmatchcase( fn, exclude ):
match = 0
break
return match
if includes is None:
includes = ('*',)
elif type(includes) in ( type(''), type(u'') ):
includes = (includes,)
if type(excludes) in ( type(''), type(u'') ):
excludes = (excludes,)
dir = env.Dir(dir).abspath
paths = os.listdir( dir )
def makeAbsFileNode( path ):
return env.File( os.path.join( dir, path ) )
nodes = filter( filterFilename, paths )
return map( makeAbsFileNode, nodes )
from SCons.Script import Environment
Environment.Glob = Glob
def exists(env):
"""
Tool always exists.
"""
return True
def exists(env):
"""
Tool always exists.
"""
return True

5
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
ADD_SUBDIRECTORY(lib_json)
IF(JSONCPP_WITH_TESTS)
ADD_SUBDIRECTORY(jsontestrunner)
ADD_SUBDIRECTORY(test_lib_json)
ENDIF(JSONCPP_WITH_TESTS)

View File

@@ -0,0 +1,23 @@
FIND_PACKAGE(PythonInterp 2.6 REQUIRED)
IF(JSONCPP_LIB_BUILD_SHARED)
ADD_DEFINITIONS( -DJSON_DLL )
ENDIF(JSONCPP_LIB_BUILD_SHARED)
ADD_EXECUTABLE(jsontestrunner_exe
main.cpp
)
TARGET_LINK_LIBRARIES(jsontestrunner_exe jsoncpp_lib)
SET_TARGET_PROPERTIES(jsontestrunner_exe PROPERTIES OUTPUT_NAME jsontestrunner_exe)
IF(PYTHONINTERP_FOUND)
# Run end to end parser/writer tests
GET_PROPERTY(JSONTESTRUNNER_EXE_PATH TARGET jsontestrunner_exe PROPERTY LOCATION)
SET(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../test)
SET(RUNJSONTESTS_PATH ${TEST_DIR}/runjsontests.py)
ADD_CUSTOM_TARGET(jsoncpp_readerwriter_tests ALL
"${PYTHON_EXECUTABLE}" -B "${RUNJSONTESTS_PATH}" "${JSONTESTRUNNER_EXE_PATH}" "${TEST_DIR}/data"
DEPENDS jsontestrunner_exe jsoncpp_test
)
ADD_CUSTOM_TARGET(jsoncpp_check DEPENDS jsoncpp_readerwriter_tests)
ENDIF(PYTHONINTERP_FOUND)

View File

@@ -15,6 +15,35 @@
# pragma warning( disable: 4996 ) // disable fopen deprecation warning
#endif
static std::string
normalizeFloatingPointStr( double value )
{
char buffer[32];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
sprintf_s( buffer, sizeof(buffer), "%.16g", value );
#else
snprintf( buffer, sizeof(buffer), "%.16g", value );
#endif
buffer[sizeof(buffer)-1] = 0;
std::string s( buffer );
std::string::size_type index = s.find_last_of( "eE" );
if ( index != std::string::npos )
{
std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0;
std::string::size_type exponentStartIndex = index + 1 + hasSign;
std::string normalized = s.substr( 0, exponentStartIndex );
std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex );
std::string exponent = "0";
if ( indexDigit != std::string::npos ) // There is an exponent different from 0
{
exponent = s.substr( indexDigit );
}
return normalized + exponent;
}
return s;
}
static std::string
readInputTestFile( const char *path )
{
@@ -34,7 +63,6 @@ readInputTestFile( const char *path )
return text;
}
static void
printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
{
@@ -50,7 +78,7 @@ printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() );
break;
case Json::realValue:
fprintf( fout, "%s=%.16g\n", path.c_str(), value.asDouble() );
fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() );
break;
case Json::stringValue:
fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
@@ -65,7 +93,11 @@ printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
for ( int index =0; index < size; ++index )
{
static char buffer[16];
sprintf( buffer, "[%d]", index );
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
sprintf_s( buffer, sizeof(buffer), "[%d]", index );
#else
snprintf( buffer, sizeof(buffer), "[%d]", index );
#endif
printValueTree( fout, value[index], path + buffer );
}
}
@@ -266,4 +298,4 @@ int main( int argc, const char *argv[] )
return exitCode;
}
// vim: et ts=4 sts=4 sw=4 tw=0

View File

@@ -0,0 +1,43 @@
OPTION(JSONCPP_LIB_BUILD_SHARED "Build jsoncpp_lib as a shared library." OFF)
IF(JSONCPP_LIB_BUILD_SHARED)
SET(JSONCPP_LIB_TYPE SHARED)
ADD_DEFINITIONS( -DJSON_DLL_BUILD )
ELSE(JSONCPP_LIB_BUILD_SHARED)
SET(JSONCPP_LIB_TYPE STATIC)
ENDIF(JSONCPP_LIB_BUILD_SHARED)
SET( JSONCPP_INCLUDE_DIR ../../include )
SET( PUBLIC_HEADERS
${JSONCPP_INCLUDE_DIR}/json/config.h
${JSONCPP_INCLUDE_DIR}/json/forwards.h
${JSONCPP_INCLUDE_DIR}/json/features.h
${JSONCPP_INCLUDE_DIR}/json/value.h
${JSONCPP_INCLUDE_DIR}/json/reader.h
${JSONCPP_INCLUDE_DIR}/json/writer.h
${JSONCPP_INCLUDE_DIR}/json/assertions.h
${JSONCPP_INCLUDE_DIR}/json/version.h
)
SOURCE_GROUP( "Public API" FILES ${PUBLIC_HEADERS} )
ADD_LIBRARY( jsoncpp_lib ${JSONCPP_LIB_TYPE}
${PUBLIC_HEADERS}
json_tool.h
json_reader.cpp
json_batchallocator.h
json_valueiterator.inl
json_value.cpp
json_writer.cpp
version.h.in
)
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES OUTPUT_NAME jsoncpp )
SET_TARGET_PROPERTIES( jsoncpp_lib PROPERTIES VERSION ${JSON_CPP_VERSION} SOVERSION ${JSON_CPP_VERSION} )
# Install instructions for this target
INSTALL( TARGETS jsoncpp_lib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@@ -30,8 +30,6 @@ template<typename AllocatedType
class BatchAllocator
{
public:
typedef AllocatedType Type;
BatchAllocator( unsigned int objectsPerPage = 255 )
: freeHead_( 0 )
, objectsPerPage_( objectsPerPage )
@@ -127,4 +125,4 @@ private:
# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -53,8 +53,7 @@ public: // overridden from ValueArrayAllocator
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc.");
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
@@ -117,8 +116,7 @@ public: // overridden from ValueArrayAllocator
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc.");
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
@@ -258,8 +256,8 @@ ValueInternalArray::ValueInternalArray()
ValueInternalArray::ValueInternalArray( const ValueInternalArray &other )
: pages_( 0 )
, pageCount_( 0 )
, size_( other.size_ )
, pageCount_( 0 )
{
PageIndex minNewPages = other.size_ / itemsPerPage;
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
@@ -454,3 +452,4 @@ ValueInternalArray::compare( const ValueInternalArray &other ) const
}
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -613,3 +613,4 @@ ValueInternalMap::distance( const IteratorState &x, const IteratorState &y )
}
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -1,9 +1,10 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Copyright 2007-2011 Baptiste Lepilleur
// 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
#if !defined(JSON_IS_AMALGAMATION)
# include <json/assertions.h>
# include <json/reader.h>
# include <json/value.h>
# include "json_tool.h"
@@ -12,10 +13,9 @@
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <istream>
#if _MSC_VER >= 1400 // VC++ 8.0
#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
@@ -27,6 +27,8 @@ namespace Json {
Features::Features()
: allowComments_( true )
, strictRoot_( false )
, allowDroppedNullPlaceholders_ ( false )
, allowNumericKeys_ ( false )
{
}
@@ -44,6 +46,8 @@ Features::strictMode()
Features features;
features.allowComments_ = false;
features.strictRoot_ = true;
features.allowDroppedNullPlaceholders_ = false;
features.allowNumericKeys_ = false;
return features;
}
@@ -79,13 +83,31 @@ containsNewLine( Reader::Location begin,
// //////////////////////////////////////////////////////////////////
Reader::Reader()
: features_( Features::all() )
: errors_(),
document_(),
begin_(),
end_(),
current_(),
lastValueEnd_(),
lastValue_(),
commentsBefore_(),
features_( Features::all() ),
collectComments_()
{
}
Reader::Reader( const Features &features )
: features_( features )
: errors_(),
document_(),
begin_(),
end_(),
current_(),
lastValueEnd_(),
lastValue_(),
commentsBefore_(),
features_( features ),
collectComments_()
{
}
@@ -172,6 +194,17 @@ Reader::readValue()
if ( collectComments_ && !commentsBefore_.empty() )
{
// Remove newline characters at the end of the comments
size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n");
if (lastNonNewline != std::string::npos)
{
commentsBefore_.erase(lastNonNewline+1);
}
else
{
commentsBefore_.clear();
}
currentValue().setComment( commentsBefore_, commentBefore );
commentsBefore_ = "";
}
@@ -181,9 +214,11 @@ Reader::readValue()
{
case tokenObjectBegin:
successful = readObject( token );
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenArrayBegin:
successful = readArray( token );
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenNumber:
successful = decodeNumber( token );
@@ -193,14 +228,34 @@ Reader::readValue()
break;
case tokenTrue:
currentValue() = true;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenFalse:
currentValue() = false;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenNull:
currentValue() = Value();
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenArraySeparator:
if ( features_.allowDroppedNullPlaceholders_ )
{
// "Un-read" the current token and mark the current value as a null
// token.
current_--;
currentValue() = Value();
currentValue().setOffsetStart(current_ - begin_ - 1);
currentValue().setOffsetLimit(current_ - begin_);
break;
}
// Else, fall through...
default:
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return addError( "Syntax error: value, object or array expected.", token );
}
@@ -449,11 +504,12 @@ Reader::readString()
bool
Reader::readObject( Token &/*tokenStart*/ )
Reader::readObject( Token &tokenStart )
{
Token tokenName;
std::string name;
currentValue() = Value( objectValue );
currentValue().setOffsetStart(tokenStart.start_ - begin_);
while ( readToken( tokenName ) )
{
bool initialTokenOk = true;
@@ -463,12 +519,24 @@ Reader::readObject( Token &/*tokenStart*/ )
break;
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
return true;
if ( tokenName.type_ != tokenString )
break;
name = "";
if ( !decodeString( tokenName, name ) )
return recoverFromError( tokenObjectEnd );
if ( tokenName.type_ == tokenString )
{
if ( !decodeString( tokenName, name ) )
return recoverFromError( tokenObjectEnd );
}
else if ( tokenName.type_ == tokenNumber &&
features_.allowNumericKeys_ )
{
Value numberName;
if ( !decodeNumber( tokenName, numberName ) )
return recoverFromError( tokenObjectEnd );
name = numberName.asString();
}
else
{
break;
}
Token colon;
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
@@ -508,9 +576,10 @@ Reader::readObject( Token &/*tokenStart*/ )
bool
Reader::readArray( Token &/*tokenStart*/ )
Reader::readArray( Token &tokenStart )
{
currentValue() = Value( arrayValue );
currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces();
if ( *current_ == ']' ) // empty array
{
@@ -552,6 +621,19 @@ Reader::readArray( Token &/*tokenStart*/ )
bool
Reader::decodeNumber( Token &token )
{
Value decoded;
if ( !decodeNumber( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
bool
Reader::decodeNumber( Token &token, Value &decoded )
{
bool isDouble = false;
for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
@@ -561,7 +643,7 @@ Reader::decodeNumber( Token &token )
|| ( *inspect == '-' && inspect != token.start_ );
}
if ( isDouble )
return decodeDouble( token );
return decodeDouble( token, decoded );
// Attempts to parse the number as an integer. If the number is
// larger than the maximum supported value of an integer then
// we decode the number as a double.
@@ -572,8 +654,6 @@ Reader::decodeNumber( Token &token )
Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt)
: Value::maxLargestUInt;
Value::LargestUInt threshold = maxIntegerValue / 10;
Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 );
assert( lastDigitThreshold >=0 && lastDigitThreshold <= 9 );
Value::LargestUInt value = 0;
while ( current < token.end_ )
{
@@ -583,49 +663,78 @@ Reader::decodeNumber( Token &token )
Value::UInt digit(c - '0');
if ( value >= threshold )
{
// If the current digit is not the last one, or if it is
// greater than the last digit of the maximum integer value,
// the parse the number as a double.
if ( current != token.end_ || digit > lastDigitThreshold )
// We've hit or exceeded the max value divided by 10 (rounded down). If
// a) we've only just touched the limit, b) this is the last digit, and
// c) it's small enough to fit in that rounding delta, we're okay.
// Otherwise treat this number as a double to avoid overflow.
if (value > threshold ||
current != token.end_ ||
digit > maxIntegerValue % 10)
{
return decodeDouble( token );
return decodeDouble( token, decoded );
}
}
value = value * 10 + digit;
}
if ( isNegative )
currentValue() = -Value::LargestInt( value );
decoded = -Value::LargestInt( value );
else if ( value <= Value::LargestUInt(Value::maxInt) )
currentValue() = Value::LargestInt( value );
decoded = Value::LargestInt( value );
else
currentValue() = value;
decoded = value;
return true;
}
bool
Reader::decodeDouble( Token &token )
{
Value decoded;
if ( !decodeDouble( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
bool
Reader::decodeDouble( Token &token, Value &decoded )
{
double value = 0;
const int bufferSize = 32;
int count;
int length = int(token.end_ - token.start_);
// Sanity check to avoid buffer overflow exploits.
if (length < 0) {
return addError( "Unable to parse token length", token );
}
// 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_, length );
buffer[length] = 0;
count = sscanf( buffer, "%lf", &value );
count = sscanf( buffer, format, &value );
}
else
{
std::string buffer( token.start_, token.end_ );
count = sscanf( buffer.c_str(), "%lf", &value );
count = sscanf( buffer.c_str(), format, &value );
}
if ( count != 1 )
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
currentValue() = value;
decoded = value;
return true;
}
@@ -637,6 +746,8 @@ Reader::decodeString( Token &token )
if ( !decodeString( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -836,7 +947,11 @@ Reader::getLocationLineAndColumn( Location location ) const
int line, column;
getLocationLineAndColumn( location, line, column );
char buffer[18+16+16+1];
sprintf( buffer, "Line %d, Column %d", line, column );
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#else
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#endif
return buffer;
}
@@ -867,14 +982,40 @@ Reader::getFormattedErrorMessages() const
}
std::vector<Reader::StructuredError>
Reader::getStructuredErrors() const
{
std::vector<Reader::StructuredError> allErrors;
for ( Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError )
{
const ErrorInfo &error = *itError;
Reader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_;
allErrors.push_back(structured);
}
return allErrors;
}
std::istream& operator>>( std::istream &sin, Value &root )
{
Json::Reader reader;
bool ok = reader.parse(sin, root, true);
//JSON_ASSERT( ok );
if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages());
if (!ok) {
fprintf(
stderr,
"Error from reader: %s",
reader.getFormattedErrorMessages().c_str());
JSON_FAIL_MESSAGE("reader error");
}
return sin;
}
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -1,18 +1,19 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Copyright 2011 Baptiste Lepilleur
// 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
#if !defined(JSON_IS_AMALGAMATION)
# include <json/assertions.h>
# include <json/value.h>
# include <json/writer.h>
# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
# include "json_batchallocator.h"
# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <iostream>
#include <math.h>
#include <sstream>
#include <utility>
#include <stdexcept>
#include <cstring>
#include <cassert>
#ifdef JSON_USE_CPPTL
@@ -21,9 +22,6 @@
#include <cstddef> // size_t
#define JSON_ASSERT_UNREACHABLE assert( false )
#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw
#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message );
#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message )
namespace Json {
@@ -31,9 +29,15 @@ const Value Value::null;
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);
@@ -42,6 +46,29 @@ const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
/// Unknown size marker
static const unsigned int unknown = (unsigned)-1;
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U>
static inline bool InRange(double d, T min, U max) {
return d >= min && d <= max;
}
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double integerToDouble( Json::UInt64 value )
{
return static_cast<double>( Int64(value/2) ) * 2.0 + Int64(value & 1);
}
template<typename T>
static inline double integerToDouble( T value )
{
return static_cast<double>( value );
}
template <typename T, typename U>
static inline bool InRange(double d, T min, U max) {
return d >= integerToDouble(min) && d <= integerToDouble(max);
}
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
/** Duplicates the specified string value.
* @param value Pointer to the string to duplicate. Must be zero-terminated if
@@ -56,8 +83,14 @@ duplicateStringValue( const char *value,
{
if ( length == unknown )
length = (unsigned int)strlen(value);
// Avoid an integer overflow in the call to malloc below by limiting length
// to a sane value.
if (length >= (unsigned)Value::maxInt)
length = Value::maxInt - 1;
char *newString = static_cast<char *>( malloc( length + 1 ) );
JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" );
JSON_ASSERT_MESSAGE( newString != 0, "in Json::Value::duplicateStringValue(): Failed to allocate string value buffer" );
memcpy( newString, value, length );
newString[length] = 0;
return newString;
@@ -121,7 +154,7 @@ Value::CommentInfo::setComment( const char *text )
if ( comment_ )
releaseStringValue( comment_ );
JSON_ASSERT( text != 0 );
JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /");
JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "in Json::Value::setComment(): Comments must start with /");
// It seems that /**/ style comments are acceptable as well.
comment_ = duplicateStringValue( text );
}
@@ -235,11 +268,13 @@ Value::CZString::isStaticString() const
*/
Value::Value( ValueType type )
: type_( type )
, allocated_( 0 )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
switch ( type )
{
@@ -277,36 +312,43 @@ Value::Value( ValueType type )
}
#if defined(JSON_HAS_INT64)
Value::Value( UInt value )
: type_( uintValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.uint_ = value;
}
Value::Value( Int value )
: type_( intValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.int_ = value;
}
#endif // if defined(JSON_HAS_INT64)
# if defined(JSON_HAS_INT64)
Value::Value( Int64 value )
: type_( intValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.int_ = value;
}
@@ -314,20 +356,27 @@ Value::Value( Int64 value )
Value::Value( UInt64 value )
: type_( uintValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.uint_ = value;
}
#endif // defined(JSON_HAS_INT64)
Value::Value( double value )
: type_( realValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.real_ = value;
}
@@ -335,10 +384,12 @@ Value::Value( double value )
Value::Value( const char *value )
: type_( stringValue )
, allocated_( true )
, comments_( 0 )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value );
}
@@ -348,10 +399,12 @@ Value::Value( const char *beginValue,
const char *endValue )
: type_( stringValue )
, allocated_( true )
, comments_( 0 )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( beginValue,
(unsigned int)(endValue - beginValue) );
@@ -361,10 +414,12 @@ Value::Value( const char *beginValue,
Value::Value( const std::string &value )
: type_( stringValue )
, allocated_( true )
, comments_( 0 )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value.c_str(),
(unsigned int)value.length() );
@@ -374,10 +429,12 @@ Value::Value( const std::string &value )
Value::Value( const StaticString &value )
: type_( stringValue )
, allocated_( false )
, comments_( 0 )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = const_cast<char *>( value.c_str() );
}
@@ -387,10 +444,12 @@ Value::Value( const StaticString &value )
Value::Value( const CppTL::ConstString &value )
: type_( stringValue )
, allocated_( true )
, comments_( 0 )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value, value.length() );
}
@@ -398,10 +457,13 @@ Value::Value( const CppTL::ConstString &value )
Value::Value( bool value )
: type_( booleanValue )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.bool_ = value;
}
@@ -409,10 +471,13 @@ Value::Value( bool value )
Value::Value( const Value &other )
: type_( other.type_ )
, comments_( 0 )
, allocated_( false )
# ifdef JSON_VALUE_USE_INTERNAL_MAP
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( other.start_ )
, limit_( other.limit_ )
{
switch ( type_ )
{
@@ -430,7 +495,10 @@ Value::Value( const Value &other )
allocated_ = true;
}
else
{
value_.string_ = 0;
allocated_ = false;
}
break;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue:
@@ -514,6 +582,8 @@ Value::swap( Value &other )
int temp2 = allocated_;
allocated_ = other.allocated_;
other.allocated_ = temp2;
std::swap( start_, other.start_ );
std::swap( limit_, other.limit_ );
}
ValueType
@@ -649,7 +719,7 @@ Value::operator !=( const Value &other ) const
const char *
Value::asCString() const
{
JSON_ASSERT( type_ == stringValue );
JSON_ASSERT_MESSAGE( type_ == stringValue, "in Json::Value::asCString(): requires stringValue" );
return value_.string_;
}
@@ -666,15 +736,14 @@ Value::asString() const
case booleanValue:
return value_.bool_ ? "true" : "false";
case intValue:
return valueToString( value_.int_ );
case uintValue:
return valueToString( value_.uint_ );
case realValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to string" );
return valueToString( value_.real_ );
default:
JSON_ASSERT_UNREACHABLE;
JSON_FAIL_MESSAGE( "Type is not convertible to string" );
}
return ""; // unreachable
}
# ifdef JSON_USE_CPPTL
@@ -691,27 +760,23 @@ Value::asInt() const
{
switch ( type_ )
{
case nullValue:
return 0;
case intValue:
JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" );
JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
return Int(value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" );
JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
return Int(value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" );
return Int( value_.real_ );
JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range");
return Int(value_.real_);
case nullValue:
return 0;
case booleanValue:
return value_.bool_ ? 1 : 0;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to int" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to Int.");
}
@@ -720,28 +785,23 @@ Value::asUInt() const
{
switch ( type_ )
{
case nullValue:
return 0;
case intValue:
JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" );
JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" );
JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
return UInt(value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" );
JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
return UInt(value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" );
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range");
return UInt( value_.real_ );
case nullValue:
return 0;
case booleanValue:
return value_.bool_ ? 1 : 0;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to uint" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
}
@@ -752,26 +812,22 @@ Value::asInt64() const
{
switch ( type_ )
{
case intValue:
return Int64(value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
return Int64(value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range");
return Int64(value_.real_);
case nullValue:
return 0;
case intValue:
return value_.int_;
case uintValue:
JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" );
return value_.uint_;
case realValue:
JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" );
return Int( value_.real_ );
case booleanValue:
return value_.bool_ ? 1 : 0;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to Int64" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
}
@@ -780,26 +836,22 @@ Value::asUInt64() const
{
switch ( type_ )
{
case intValue:
JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
return UInt64(value_.int_);
case uintValue:
return UInt64(value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range");
return UInt64( value_.real_ );
case nullValue:
return 0;
case intValue:
JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" );
return value_.int_;
case uintValue:
return value_.uint_;
case realValue:
JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" );
return UInt( value_.real_ );
case booleanValue:
return value_.bool_ ? 1 : 0;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
}
# endif // if defined(JSON_HAS_INT64)
@@ -831,28 +883,24 @@ Value::asDouble() const
{
switch ( type_ )
{
case nullValue:
return 0.0;
case intValue:
return static_cast<double>( value_.int_ );
case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<double>( value_.uint_ );
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<double>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
return integerToDouble( value_.uint_ );
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
case realValue:
return value_.real_;
case nullValue:
return 0.0;
case booleanValue:
return value_.bool_ ? 1.0 : 0.0;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to double" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to double.");
}
float
@@ -860,28 +908,24 @@ Value::asFloat() const
{
switch ( type_ )
{
case nullValue:
return 0.0f;
case intValue:
return static_cast<float>( value_.int_ );
case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<float>( value_.uint_ );
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<float>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
return integerToDouble( value_.uint_ );
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
case realValue:
return static_cast<float>( value_.real_ );
case nullValue:
return 0.0;
case booleanValue:
return value_.bool_ ? 1.0f : 0.0f;
case stringValue:
case arrayValue:
case objectValue:
JSON_FAIL_MESSAGE( "Type is not convertible to float" );
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return 0.0f; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to float.");
}
bool
@@ -889,75 +933,67 @@ Value::asBool() const
{
switch ( type_ )
{
case booleanValue:
return value_.bool_;
case nullValue:
return false;
case intValue:
return value_.int_ ? true : false;
case uintValue:
return value_.int_ != 0;
return value_.uint_ ? true : false;
case realValue:
return value_.real_ != 0.0;
case booleanValue:
return value_.bool_;
case stringValue:
return value_.string_ && value_.string_[0] != 0;
case arrayValue:
case objectValue:
return value_.map_->size() != 0;
return value_.real_ ? true : false;
default:
JSON_ASSERT_UNREACHABLE;
break;
}
return false; // unreachable;
JSON_FAIL_MESSAGE("Value is not convertible to bool.");
}
bool
Value::isConvertibleTo( ValueType other ) const
{
switch ( type_ )
switch ( other )
{
case nullValue:
return true;
return ( isNumeric() && asDouble() == 0.0 )
|| ( type_ == booleanValue && value_.bool_ == false )
|| ( type_ == stringValue && asString() == "" )
|| ( type_ == arrayValue && value_.map_->size() == 0 )
|| ( type_ == objectValue && value_.map_->size() == 0 )
|| type_ == nullValue;
case intValue:
return ( other == nullValue && value_.int_ == 0 )
|| other == intValue
|| ( other == uintValue && value_.int_ >= 0 )
|| other == realValue
|| other == stringValue
|| other == booleanValue;
return isInt()
|| (type_ == realValue && InRange(value_.real_, minInt, maxInt))
|| type_ == booleanValue
|| type_ == nullValue;
case uintValue:
return ( other == nullValue && value_.uint_ == 0 )
|| ( other == intValue && value_.uint_ <= (unsigned)maxInt )
|| other == uintValue
|| other == realValue
|| other == stringValue
|| other == booleanValue;
return isUInt()
|| (type_ == realValue && InRange(value_.real_, 0, maxUInt))
|| type_ == booleanValue
|| type_ == nullValue;
case realValue:
return ( other == nullValue && value_.real_ == 0.0 )
|| ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt )
|| ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt )
|| other == realValue
|| other == stringValue
|| other == booleanValue;
return isNumeric()
|| type_ == booleanValue
|| type_ == nullValue;
case booleanValue:
return ( other == nullValue && value_.bool_ == false )
|| other == intValue
|| other == uintValue
|| other == realValue
|| other == stringValue
|| other == booleanValue;
return isNumeric()
|| type_ == booleanValue
|| type_ == nullValue;
case stringValue:
return other == stringValue
|| ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) );
return isNumeric()
|| type_ == booleanValue
|| type_ == stringValue
|| type_ == nullValue;
case arrayValue:
return other == arrayValue
|| ( other == nullValue && value_.map_->size() == 0 );
return type_ == arrayValue
|| type_ == nullValue;
case objectValue:
return other == objectValue
|| ( other == nullValue && value_.map_->size() == 0 );
default:
JSON_ASSERT_UNREACHABLE;
return type_ == objectValue
|| type_ == nullValue;
}
return false; // unreachable;
JSON_ASSERT_UNREACHABLE;
return false;
}
@@ -991,9 +1027,8 @@ Value::size() const
case objectValue:
return Int( value_.map_->size() );
#endif
default:
JSON_ASSERT_UNREACHABLE;
}
JSON_ASSERT_UNREACHABLE;
return 0; // unreachable;
}
@@ -1018,8 +1053,9 @@ Value::operator!() const
void
Value::clear()
{
JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue || type_ == objectValue, "in Json::Value::clear(): requires complex value" );
start_ = 0;
limit_ = 0;
switch ( type_ )
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1043,7 +1079,7 @@ Value::clear()
void
Value::resize( ArrayIndex newSize )
{
JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::resize(): requires arrayValue" );
if ( type_ == nullValue )
*this = Value( arrayValue );
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1069,7 +1105,7 @@ Value::resize( ArrayIndex newSize )
Value &
Value::operator[]( ArrayIndex index )
{
JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue" );
if ( type_ == nullValue )
*this = Value( arrayValue );
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1090,7 +1126,7 @@ Value::operator[]( ArrayIndex index )
Value &
Value::operator[]( int index )
{
JSON_ASSERT( index >= 0 );
JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index): index cannot be negative" );
return (*this)[ ArrayIndex(index) ];
}
@@ -1098,7 +1134,7 @@ Value::operator[]( int index )
const Value &
Value::operator[]( ArrayIndex index ) const
{
JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue" );
if ( type_ == nullValue )
return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1117,7 +1153,7 @@ Value::operator[]( ArrayIndex index ) const
const Value &
Value::operator[]( int index ) const
{
JSON_ASSERT( index >= 0 );
JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index) const: index cannot be negative" );
return (*this)[ ArrayIndex(index) ];
}
@@ -1133,7 +1169,7 @@ Value &
Value::resolveReference( const char *key,
bool isStatic )
{
JSON_ASSERT( type_ == nullValue || type_ == objectValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::resolveReference(): requires objectValue" );
if ( type_ == nullValue )
*this = Value( objectValue );
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1173,7 +1209,7 @@ Value::isValidIndex( ArrayIndex index ) const
const Value &
Value::operator[]( const char *key ) const
{
JSON_ASSERT( type_ == nullValue || type_ == objectValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::operator[](char const*)const: requires objectValue" );
if ( type_ == nullValue )
return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1251,7 +1287,7 @@ Value::get( const std::string &key,
Value
Value::removeMember( const char* key )
{
JSON_ASSERT( type_ == nullValue || type_ == objectValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::removeMember(): requires objectValue" );
if ( type_ == nullValue )
return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1315,7 +1351,7 @@ Value::isMember( const CppTL::ConstString &key ) const
Value::Members
Value::getMemberNames() const
{
JSON_ASSERT( type_ == nullValue || type_ == objectValue );
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::getMemberNames(), value must be objectValue" );
if ( type_ == nullValue )
return Value::Members();
Members members;
@@ -1361,6 +1397,11 @@ Value::getMemberNames() const
//
//# endif
static bool IsIntegral(double d) {
double integral_part;
return modf(d, &integral_part) == 0.0;
}
bool
Value::isNull() const
@@ -1379,30 +1420,106 @@ Value::isBool() const
bool
Value::isInt() const
{
return type_ == intValue;
switch ( type_ )
{
case intValue:
return value_.int_ >= minInt && value_.int_ <= maxInt;
case uintValue:
return value_.uint_ <= UInt(maxInt);
case realValue:
return value_.real_ >= minInt &&
value_.real_ <= maxInt &&
IsIntegral(value_.real_);
default:
break;
}
return false;
}
bool
Value::isUInt() const
{
return type_ == uintValue;
switch ( type_ )
{
case intValue:
return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
case uintValue:
return value_.uint_ <= maxUInt;
case realValue:
return value_.real_ >= 0 &&
value_.real_ <= maxUInt &&
IsIntegral(value_.real_);
default:
break;
}
return false;
}
bool
Value::isInt64() const
{
# if defined(JSON_HAS_INT64)
switch ( type_ )
{
case intValue:
return true;
case uintValue:
return value_.uint_ <= UInt64(maxInt64);
case realValue:
// Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
// double, so double(maxInt64) will be rounded up to 2^63. Therefore we
// require the value to be strictly less than the limit.
return value_.real_ >= double(minInt64) &&
value_.real_ < double(maxInt64) &&
IsIntegral(value_.real_);
default:
break;
}
# endif // JSON_HAS_INT64
return false;
}
bool
Value::isUInt64() const
{
# if defined(JSON_HAS_INT64)
switch ( type_ )
{
case intValue:
return value_.int_ >= 0;
case uintValue:
return true;
case realValue:
// Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
// double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
// require the value to be strictly less than the limit.
return value_.real_ >= 0 &&
value_.real_ < maxUInt64AsDouble &&
IsIntegral(value_.real_);
default:
break;
}
# endif // JSON_HAS_INT64
return false;
}
bool
Value::isIntegral() const
{
return type_ == intValue
|| type_ == uintValue
|| type_ == booleanValue;
#if defined(JSON_HAS_INT64)
return isInt64() || isUInt64();
#else
return isInt() || isUInt();
#endif
}
bool
Value::isDouble() const
{
return type_ == realValue;
return type_ == realValue || isIntegral();
}
@@ -1423,14 +1540,14 @@ Value::isString() const
bool
Value::isArray() const
{
return type_ == nullValue || type_ == arrayValue;
return type_ == arrayValue;
}
bool
Value::isObject() const
{
return type_ == nullValue || type_ == objectValue;
return type_ == objectValue;
}
@@ -1467,6 +1584,34 @@ Value::getComment( CommentPlacement placement ) const
}
void
Value::setOffsetStart( size_t start )
{
start_ = start;
}
void
Value::setOffsetLimit( size_t limit )
{
limit_ = limit;
}
size_t
Value::getOffsetStart() const
{
return start_;
}
size_t
Value::getOffsetLimit() const
{
return limit_;
}
std::string
Value::toStyledString() const
{
@@ -1621,13 +1766,16 @@ Value::end()
// //////////////////////////////////////////////////////////////////
PathArgument::PathArgument()
: kind_( kindNone )
: key_()
, index_()
, kind_( kindNone )
{
}
PathArgument::PathArgument( ArrayIndex index )
: index_( index )
: key_()
, index_( index )
, kind_( kindIndex )
{
}
@@ -1635,6 +1783,7 @@ PathArgument::PathArgument( ArrayIndex index )
PathArgument::PathArgument( const char *key )
: key_( key )
, index_()
, kind_( kindKey )
{
}
@@ -1642,6 +1791,7 @@ PathArgument::PathArgument( const char *key )
PathArgument::PathArgument( const std::string &key )
: key_( key.c_str() )
, index_()
, kind_( kindKey )
{
}
@@ -1711,7 +1861,7 @@ Path::makePath( const std::string &path,
void
Path::addPathInArg( const std::string &path,
Path::addPathInArg( const std::string &/*path*/,
const InArgs &in,
InArgs::const_iterator &itInArg,
PathArgument::Kind kind )
@@ -1732,8 +1882,8 @@ Path::addPathInArg( const std::string &path,
void
Path::invalidPath( const std::string &path,
int location )
Path::invalidPath( const std::string &/*path*/,
int /*location*/ )
{
// Error: invalid path.
}
@@ -1748,7 +1898,7 @@ Path::resolve( const Value &root ) const
const PathArgument &arg = *it;
if ( arg.kind_ == PathArgument::kindIndex )
{
if ( !node->isArray() || node->isValidIndex( arg.index_ ) )
if ( !node->isArray() || !node->isValidIndex( arg.index_ ) )
{
// Error: unable to resolve path (array value expected at position...
}
@@ -1781,7 +1931,7 @@ Path::resolve( const Value &root,
const PathArgument &arg = *it;
if ( arg.kind_ == PathArgument::kindIndex )
{
if ( !node->isArray() || node->isValidIndex( arg.index_ ) )
if ( !node->isArray() || !node->isValidIndex( arg.index_ ) )
return defaultValue;
node = &((*node)[arg.index_]);
}
@@ -1827,3 +1977,4 @@ Path::make( Value &root ) const
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -149,6 +149,7 @@ ValueIteratorBase::copy( const SelfType &other )
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
current_ = other.current_;
isNull_ = other.isNull_;
#else
if ( isArray_ )
iterator_.array_ = other.iterator_.array_;
@@ -297,3 +298,4 @@ ValueIterator::operator =( const SelfType &other )
}
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

View File

@@ -1,4 +1,4 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Copyright 2011 Baptiste Lepilleur
// 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
@@ -11,11 +11,10 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#if _MSC_VER >= 1400 // VC++ 8.0
#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
@@ -74,40 +73,19 @@ std::string valueToString( UInt value )
std::string valueToString( double value )
{
// Allocate a buffer that is more than large enough to store the 16 digits of
// precision requested below.
char buffer[32];
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distingish the
// concepts of reals and integers.
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
#else
sprintf(buffer, "%#.16g", value);
sprintf_s(buffer, sizeof(buffer), "%.16g", value);
#else
snprintf(buffer, sizeof(buffer), "%.16g", value);
#endif
char* ch = buffer + strlen(buffer) - 1;
if (*ch != '0') return buffer; // nothing to truncate, so save time
while(ch > buffer && *ch == '0'){
--ch;
}
char* last_nonzero = ch;
while(ch >= buffer){
switch(*ch){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--ch;
continue;
case '.':
// Truncate zeroes to save bytes in output, but keep one.
*(last_nonzero+2) = '\0';
return buffer;
default:
return buffer;
}
}
return buffer;
}
@@ -119,6 +97,8 @@ std::string valueToString( bool value )
std::string valueToQuotedString( const char *value )
{
if (value == NULL)
return "";
// Not sure how to handle unicode...
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
return std::string("\"") + value + "\"";
@@ -191,7 +171,8 @@ Writer::~Writer()
// //////////////////////////////////////////////////////////////////
FastWriter::FastWriter()
: yamlCompatiblityEnabled_( false )
: yamlCompatiblityEnabled_( false ),
dropNullPlaceholders_( false )
{
}
@@ -203,6 +184,13 @@ FastWriter::enableYAMLCompatibility()
}
void
FastWriter::dropNullPlaceholders()
{
dropNullPlaceholders_ = true;
}
std::string
FastWriter::write( const Value &root )
{
@@ -219,7 +207,7 @@ FastWriter::writeValue( const Value &value )
switch ( value.type() )
{
case nullValue:
document_ += "null";
if (!dropNullPlaceholders_) document_ += "null";
break;
case intValue:
document_ += valueToString( value.asLargestInt() );
@@ -278,6 +266,7 @@ FastWriter::writeValue( const Value &value )
StyledWriter::StyledWriter()
: rightMargin_( 74 )
, indentSize_( 3 )
, addChildValues_()
{
}
@@ -494,7 +483,20 @@ StyledWriter::writeCommentBeforeValue( const Value &root )
{
if ( !root.hasComment( commentBefore ) )
return;
document_ += normalizeEOL( root.getComment( commentBefore ) );
document_ += "\n";
writeIndent();
std::string normalizedComment = normalizeEOL( root.getComment( commentBefore ) );
std::string::const_iterator iter = normalizedComment.begin();
while ( iter != normalizedComment.end() )
{
document_ += *iter;
if ( *iter == '\n' && *(iter+1) == '/' )
writeIndent();
++iter;
}
// Comments are stripped of newlines, so add one here
document_ += "\n";
}
@@ -554,6 +556,7 @@ StyledStreamWriter::StyledStreamWriter( std::string indentation )
: document_(NULL)
, rightMargin_( 74 )
, indentation_( indentation )
, addChildValues_()
{
}
@@ -836,3 +839,4 @@ std::ostream& operator<<( std::ostream &sout, const Value &root )
} // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0

14
src/lib_json/version.h.in Normal file
View File

@@ -0,0 +1,14 @@
// DO NOT EDIT. This file is generated by CMake from "version"
// and "version.h.in" files.
// Run CMake configure step to update it.
#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 @JSONCPP_VERSION_QUALIFIER@
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
#endif // JSON_VERSION_H_INCLUDED

View File

@@ -0,0 +1,22 @@
IF(JSONCPP_LIB_BUILD_SHARED)
ADD_DEFINITIONS( -DJSON_DLL )
ENDIF(JSONCPP_LIB_BUILD_SHARED)
ADD_EXECUTABLE( jsoncpp_test
jsontest.cpp
jsontest.h
main.cpp
)
TARGET_LINK_LIBRARIES(jsoncpp_test jsoncpp_lib)
# Run unit tests in post-build
# (default cmake workflow hides away the test result into a file, resulting in poor dev workflow?!?)
IF(JSONCPP_WITH_POST_BUILD_UNITTEST)
ADD_CUSTOM_COMMAND( TARGET jsoncpp_test
POST_BUILD
COMMAND jsoncpp_test)
ENDIF(JSONCPP_WITH_POST_BUILD_UNITTEST)
SET_TARGET_PROPERTIES(jsoncpp_test PROPERTIES OUTPUT_NAME jsoncpp_test)

View File

@@ -249,57 +249,24 @@ TestResult::addToLastFailure( const std::string &message )
return *this;
}
TestResult &
TestResult::operator << ( bool value )
{
return addToLastFailure( value ? "true" : "false" );
TestResult::operator << ( Json::Int64 value ) {
return addToLastFailure( Json::valueToString(value) );
}
TestResult &
TestResult::operator << ( int value )
{
char buffer[32];
sprintf( buffer, "%d", value );
return addToLastFailure( buffer );
TestResult::operator << ( Json::UInt64 value ) {
return addToLastFailure( Json::valueToString(value) );
}
TestResult &
TestResult::operator << ( unsigned int value )
{
char buffer[32];
sprintf( buffer, "%u", value );
return addToLastFailure( buffer );
TestResult::operator << ( bool value ) {
return addToLastFailure(value ? "true" : "false");
}
TestResult &
TestResult::operator << ( double value )
{
char buffer[32];
sprintf( buffer, "%16g", value );
return addToLastFailure( buffer );
}
TestResult &
TestResult::operator << ( const char *value )
{
return addToLastFailure( value ? value
: "<NULL>" );
}
TestResult &
TestResult::operator << ( const std::string &value )
{
return addToLastFailure( value );
}
// class TestCase
// //////////////////////////////////////////////////////////////////
@@ -373,7 +340,7 @@ Runner::runTestAt( unsigned int index, TestResult &result ) const
catch ( const std::exception &e )
{
result.addFailure( __FILE__, __LINE__,
"Unexpected exception caugth:" ) << e.what();
"Unexpected exception caught:" ) << e.what();
}
#endif // if JSON_USE_EXCEPTION
delete test;
@@ -513,10 +480,10 @@ Runner::runCommandLine( int argc, const char *argv[] ) const
}
#if defined(_MSC_VER)
#if defined(_MSC_VER) && defined(_DEBUG)
// Hook MSVCRT assertions to prevent dialog from appearing
static int
msvcrtSilentReportHook( int reportType, char *message, int *returnValue )
msvcrtSilentReportHook( int reportType, char *message, int * /*returnValue*/ )
{
// The default CRT handling of error and assertion is to display
// an error dialog to the user.
@@ -550,9 +517,11 @@ msvcrtSilentReportHook( int reportType, char *message, int *returnValue )
void
Runner::preventDialogOnCrash()
{
#if defined(_MSC_VER)
#if defined(_MSC_VER) && defined(_DEBUG)
// Install a hook to prevent MSVCRT error and assertion from
// popping a dialog.
// popping a dialog
// This function a NO-OP in release configuration
// (which cause warning since msvcrtSilentReportHook is not referenced)
_CrtSetReportHook( &msvcrtSilentReportHook );
#endif // if defined(_MSC_VER)
@@ -606,3 +575,4 @@ checkStringEqual( TestResult &result,
} // namespace JsonTest
// vim: et ts=4 sts=4 sw=4 tw=0

View File

@@ -7,8 +7,11 @@
# define JSONTEST_H_INCLUDED
# include <json/config.h>
# include <json/value.h>
# include <json/writer.h>
# include <stdio.h>
# include <deque>
# include <sstream>
# include <string>
// //////////////////////////////////////////////////////////////////
@@ -84,12 +87,21 @@ namespace JsonTest {
void printFailure( bool printTestName ) const;
// Generic operator that will work with anything ostream can deal with.
template <typename T>
TestResult &operator << ( const T& value ) {
std::ostringstream oss;
oss.precision( 16 );
oss.setf( std::ios_base::floatfield );
oss << value;
return addToLastFailure(oss.str());
}
// Specialized versions.
TestResult &operator << ( bool value );
TestResult &operator << ( int value );
TestResult &operator << ( unsigned int value );
TestResult &operator << ( double value );
TestResult &operator << ( const char *value );
TestResult &operator << ( const std::string &value );
// std:ostream does not support 64bits integers on all STL implementation
TestResult &operator << ( Json::Int64 value );
TestResult &operator << ( Json::UInt64 value );
private:
TestResult &addToLastFailure( const std::string &message );
@@ -173,20 +185,21 @@ namespace JsonTest {
Factories tests_;
};
template<typename T>
template<typename T, typename U>
TestResult &
checkEqual( TestResult &result, const T &expected, const T &actual,
checkEqual( TestResult &result, const T &expected, const U &actual,
const char *file, unsigned int line, const char *expr )
{
if ( expected != actual )
if ( static_cast< U >( expected ) != actual )
{
result.addFailure( file, line, expr );
result << "Expected: " << expected << "\n";
result << "Expected: " << static_cast< U >( expected ) << "\n";
result << "Actual : " << actual;
}
return result;
}
TestResult &
checkStringEqual( TestResult &result,
const std::string &expected, const std::string &actual,
@@ -216,8 +229,7 @@ namespace JsonTest {
result_->predicateStackTail_ = &_minitest_Context; \
(expr); \
result_->popPredicateContext(); \
} \
*result_
}
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL( expected, actual ) \
@@ -229,6 +241,7 @@ namespace JsonTest {
#define JSONTEST_ASSERT_STRING_EQUAL( expected, actual ) \
JsonTest::checkStringEqual( *result_, \
std::string(expected), std::string(actual), \
__FILE__, __LINE__, \
#expected " == " #actual )
/// \brief Begin a fixture test case.
@@ -257,3 +270,4 @@ namespace JsonTest {
(runner).add( JSONTEST_FIXTURE_FACTORY( FixtureType, name ) )
#endif // ifndef JSONTEST_H_INCLUDED
// vim: et ts=4 sts=4 sw=4 tw=0

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
[ 1 2 3]
[ 1 2 3]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
.={}
.test=[]
.test[0]={}
.test[0].a="aaa"
.test[1]={}
.test[1].b="bbb"
.test[2]={}
.test[2].c="ccc"
.={}
.test=[]
.test[0]={}
.test[0].a="aaa"
.test[1]={}
.test[1].b="bbb"
.test[2]={}
.test[2].c="ccc"

View File

@@ -1,8 +1,8 @@
{
"test":
[
{ "a" : "aaa" }, // Comment for a
{ "b" : "bbb" }, // Comment for b
{ "c" : "ccc" } // Comment for c
]
}
{
"test":
[
{ "a" : "aaa" }, // Comment for a
{ "b" : "bbb" }, // Comment for b
{ "c" : "ccc" } // Comment for c
]
}

View File

@@ -0,0 +1,7 @@
.={}
.c-test={}
.c-test.a=1
.c-test.b=2
.cpp-test={}
.cpp-test.c=3
.cpp-test.d=4

View File

@@ -0,0 +1,16 @@
{
/* C-style comment
C-style-2 comment */
"c-test" : {
"a" : 1,
/* Internal comment c-style */
"b" : 2
},
// C++-style comment
"cpp-test" : {
// Internal comment cpp-style
"c" : 3,
"d" : 4
}
}

View File

@@ -1 +1 @@
.=9223372036854775808
.=9223372036854775808

View File

@@ -1,2 +1,2 @@
9223372036854775808
9223372036854775808

View File

@@ -1 +1 @@
.=-9223372036854775808
.=-9223372036854775808

View File

@@ -1,2 +1,2 @@
-9223372036854775808
-9223372036854775808

View File

@@ -1 +1 @@
.=18446744073709551615
.=18446744073709551615

View File

@@ -1,2 +1,2 @@
18446744073709551615
18446744073709551615

View File

@@ -0,0 +1 @@
.=4300000001

View File

@@ -0,0 +1,4 @@
// Out of 32-bit integer range, switch to double in 32-bit mode. Length the
// same as UINT_MAX in base 10 and digit less than UINT_MAX's last digit in
// order to catch a bug in the parsing code.
4300000001

View File

@@ -0,0 +1 @@
.=1.9e+19

View File

@@ -0,0 +1,4 @@
// Out of 64-bit integer range, switch to double in all modes. Length the same
// as ULONG_MAX in base 10 and digit less than ULONG_MAX's last digit in order
// to catch a bug in the parsing code.
19000000000000000001

View File

@@ -0,0 +1 @@
.=-2200000001

View File

@@ -0,0 +1,4 @@
// Out of 32-bit signed integer range, switch to double in all modes. Length
// the same as INT_MIN in base 10 and digit less than INT_MIN's last digit in
// order to catch a bug in the parsing code.
-2200000001

View File

@@ -0,0 +1 @@
.=-9.3e+18

View File

@@ -0,0 +1,4 @@
// Out of 64-bit signed integer range, switch to double in all modes. Length
// the same as LONG_MIN in base 10 and digit less than LONG_MIN's last digit in
// order to catch a bug in the parsing code.
-9300000000000000001

View File

@@ -0,0 +1 @@
.=1.844674407370955e+19

View File

@@ -0,0 +1,2 @@
// 2^64 -> switch to double.
18446744073709551616

View File

@@ -1 +1 @@
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

View File

@@ -1 +1 @@
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

View File

@@ -1 +1 @@
"http:\/\/jsoncpp.sourceforge.net\/"
"http:\/\/jsoncpp.sourceforge.net\/"

View File

@@ -0,0 +1,2 @@
.=""abc\def""

View File

@@ -0,0 +1,2 @@
"\"abc\\def\""

View File

@@ -0,0 +1,2 @@
.="\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"

View File

@@ -0,0 +1,2 @@
"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"

View File

@@ -1 +1 @@
.="a"
.="a"

View File

@@ -1 +1 @@
.="¢"
.="¢"

View File

@@ -1 +1 @@
.="€"
.="€"

View File

@@ -1 +1 @@
.="𝄞"
.="𝄞"

View File

@@ -1,2 +1,2 @@
.="Zażółć gęślą jaźń"
.="Zażółć gęślą jaźń"

View File

@@ -1,3 +1,3 @@
Test suite from http://json.org/JSON_checker/.
If the JSON_checker is working correctly, it must accept all of the pass*.json files and reject all of the fail*.json files.
Test suite from http://json.org/JSON_checker/.
If the JSON_checker is working correctly, it must accept all of the pass*.json files and reject all of the fail*.json files.

View File

@@ -1,73 +1,73 @@
import sys
import os
import os.path
import subprocess
from glob import glob
import optparse
VALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes'
class TestProxy(object):
def __init__( self, test_exe_path, use_valgrind=False ):
self.test_exe_path = os.path.normpath( os.path.abspath( test_exe_path ) )
self.use_valgrind = use_valgrind
def run( self, options ):
if self.use_valgrind:
cmd = VALGRIND_CMD.split()
else:
cmd = []
cmd.extend( [self.test_exe_path, '--test-auto'] + options )
process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
stdout = process.communicate()[0]
if process.returncode:
return False, stdout
return True, stdout
def runAllTests( exe_path, use_valgrind=False ):
test_proxy = TestProxy( exe_path, use_valgrind=use_valgrind )
status, test_names = test_proxy.run( ['--list-tests'] )
if not status:
print >> sys.stderr, "Failed to obtain unit tests list:\n" + test_names
return 1
test_names = [name.strip() for name in test_names.strip().split('\n')]
failures = []
for name in test_names:
print 'TESTING %s:' % name,
succeed, result = test_proxy.run( ['--test', name] )
if succeed:
print 'OK'
else:
failures.append( (name, result) )
print 'FAILED'
failed_count = len(failures)
pass_count = len(test_names) - failed_count
if failed_count:
print
for name, result in failures:
print result
print '%d/%d tests passed (%d failure(s))' % (
pass_count, len(test_names), failed_count)
return 1
else:
print 'All %d tests passed' % len(test_names)
return 0
def main():
from optparse import OptionParser
parser = OptionParser( usage="%prog [options] <path to test_lib_json.exe>" )
parser.add_option("--valgrind",
action="store_true", dest="valgrind", default=False,
help="run all the tests using valgrind to detect memory leaks")
parser.enable_interspersed_args()
options, args = parser.parse_args()
if len(args) != 1:
parser.error( 'Must provides at least path to test_lib_json executable.' )
sys.exit( 1 )
exit_code = runAllTests( args[0], use_valgrind=options.valgrind )
sys.exit( exit_code )
if __name__ == '__main__':
main()
import sys
import os
import os.path
import subprocess
from glob import glob
import optparse
VALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes'
class TestProxy(object):
def __init__( self, test_exe_path, use_valgrind=False ):
self.test_exe_path = os.path.normpath( os.path.abspath( test_exe_path ) )
self.use_valgrind = use_valgrind
def run( self, options ):
if self.use_valgrind:
cmd = VALGRIND_CMD.split()
else:
cmd = []
cmd.extend( [self.test_exe_path, '--test-auto'] + options )
process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
stdout = process.communicate()[0]
if process.returncode:
return False, stdout
return True, stdout
def runAllTests( exe_path, use_valgrind=False ):
test_proxy = TestProxy( exe_path, use_valgrind=use_valgrind )
status, test_names = test_proxy.run( ['--list-tests'] )
if not status:
print >> sys.stderr, "Failed to obtain unit tests list:\n" + test_names
return 1
test_names = [name.strip() for name in test_names.strip().split('\n')]
failures = []
for name in test_names:
print 'TESTING %s:' % name,
succeed, result = test_proxy.run( ['--test', name] )
if succeed:
print 'OK'
else:
failures.append( (name, result) )
print 'FAILED'
failed_count = len(failures)
pass_count = len(test_names) - failed_count
if failed_count:
print
for name, result in failures:
print result
print '%d/%d tests passed (%d failure(s))' % (
pass_count, len(test_names), failed_count)
return 1
else:
print 'All %d tests passed' % len(test_names)
return 0
def main():
from optparse import OptionParser
parser = OptionParser( usage="%prog [options] <path to test_lib_json.exe>" )
parser.add_option("--valgrind",
action="store_true", dest="valgrind", default=False,
help="run all the tests using valgrind to detect memory leaks")
parser.enable_interspersed_args()
options, args = parser.parse_args()
if len(args) != 1:
parser.error( 'Must provides at least path to test_lib_json executable.' )
sys.exit( 1 )
exit_code = runAllTests( args[0], use_valgrind=options.valgrind )
sys.exit( exit_code )
if __name__ == '__main__':
main()

View File

@@ -1 +1 @@
0.6.0-rc2
0.6.0-dev