Compare commits

..

45 Commits
1.1.0 ... 1.3.0

Author SHA1 Message Date
Christopher Dunn
ee8b58f82f Merge pull request #135 from cdunn2001/removeMember
Deprecate old `removeMember()`. Add new.

[Deprecated methods will be removed at the next major version bump](http://apr.apache.org/versioning.html#binary).
2015-01-22 19:26:46 -06:00
Christopher Dunn
9132aa94b1 1.3.0
http://apr.apache.org/versioning.html#binary
2015-01-22 19:25:44 -06:00
Christopher Dunn
76746b09fc deprecate old removeMember() 2015-01-22 19:25:44 -06:00
Christopher Dunn
70b795bd45 Merge pull request #133 from cdunn2001/travis-11
upgrade -std=c++ version
2015-01-22 19:21:24 -06:00
Christopher Dunn
26842530f2 upgrade -std=c++ version
Travis CI does not yet support gcc-4.8, needed for c++11, so we
will try c++0x for now.
2015-01-22 19:12:23 -06:00
Christopher Dunn
e3f24286c1 Merge pull request #130 from connormanning/master
Build without warnings with -pedantic enabled.
2015-01-22 11:48:58 -06:00
Connor Manning
00b8ce81db Build without warnings with -pedantic enabled. 2015-01-22 10:48:45 -06:00
Christopher Dunn
40810fe326 Merge pull request #127 from cdunn2001/removeIndex
`Value::removeIndex()`

See issue #28.
2015-01-21 16:08:25 -06:00
Christopher Dunn
59167d8627 more changes per cr 2015-01-21 16:05:08 -06:00
Christopher Dunn
05c1b8344d drop this-> (team preference) 2015-01-21 15:43:48 -06:00
Christopher Dunn
e893625e88 test removeIndex/Member() 2015-01-20 17:04:03 -06:00
Christopher Dunn
e87e41cdb0 from Itzik S; see issue #28
with minor corrections
2015-01-20 17:03:58 -06:00
Christopher Dunn
9de2c2d84d partial 2015-01-20 17:02:48 -06:00
Christopher Dunn
7956ccd61e allow stream ops for JSON_FAIL_MESSAGE
http://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Tips%20and%20tricks%20using%20the%20preprocessor%20%28part%20two%29.pdf
2015-01-20 16:25:26 -06:00
datadiode
9454e687a3 Specialize std::swap() for Json::Value in a C++ standard compliant way
originally from pull #119
2015-01-20 15:25:41 -06:00
Christopher Dunn
46a925ba4a fix compiler warning for a test 2015-01-20 15:19:22 -06:00
Christopher Dunn
c407f1407f test-data for #103
passes
2015-01-20 15:16:46 -06:00
Christopher Dunn
ec251df6b7 Merge pull request #125 from cdunn2001/issue-116
1.2.1

Fix issue #116, DOS line-endings. Never output \r.
2015-01-20 15:14:50 -06:00
Christopher Dunn
51c0afab22 1.2.1 <- 1.2.0
This can affect existing round-trip tests, but we never made any
guarantees about whitespace constancy.
2015-01-20 15:12:49 -06:00
Mark Zeren
e39fb0083c Normalize comment EOLs while reading instead of while writing
Tests are currently failing when git cloning on Windows with autocrlf = true. In
that setup multiline comments contain \r\n EOLs. The test code assumes that
comments contain \n EOLs and opens the .actual files (etc.) with "wt" which
converts \n to \r\n. Thus we end up with \r\r\n EOLs in the output, which
triggers a test failure.

Instead we should cannonicalize comments while reading so that they contain only
\n EOLs. This approach simplifies other parts of the reader and writer logic,
and requires no changes to the test. It is a breaking change, but probably the
Right Thing going forward.

This change also fixes dereferencing past the end of the comment string in
StyledWriter::writeCommentBeforeValue.

Tests should be added with appropriate .gitattributes for the input files to
ensure that we run tests for DOS, Mac, and Unix EOL files on all platforms. For
now this change is enough to unblock Windows builds.

issue #116
2015-01-20 13:45:44 -06:00
Christopher Dunn
ec727e2f6b -Wall for Clang/GCC 2015-01-20 13:45:28 -06:00
Christopher Dunn
4ce4bb8404 Merge pull request #124 from cdunn2001/assign-with-comments
1.2.0

 `operator=()` (which already performed a deep-copy) now includes comments. This change is probably harmless in all practical cases. But just in case, we bump the minor version.

Address #47.
2015-01-20 12:49:51 -06:00
Christopher Dunn
2cd0f4ec21 1.2.0 <- 1.1.1
`operator=()` (which already performed a deep-copy) now includes
comments. The change is probably harmless in all practical cases.
2015-01-20 12:44:51 -06:00
Christopher Dunn
836f0fb863 fix comments before several types
tests pass
2015-01-20 12:23:44 -06:00
Christopher Dunn
37644abd77 test comment before several types
* array
* double
* string
* true
* false
* null
2015-01-20 12:23:18 -06:00
Christopher Dunn
66eb72f121 use SwapPayload() to retain comments
All tests pass, but we might be missing coverage.

issue #47
2015-01-20 12:07:01 -06:00
Christopher Dunn
94b0297dc5 Revert "consider these as binary, so git will not alter line-endings"
This reverts commit 8f3aa220db.

We will find a better fix for #116. In the meantime, we want to see
diffs for changes to test-data.
2015-01-20 12:06:12 -06:00
Christopher Dunn
55db3c3cb2 Merge pull request #118 from datadiode/47_fix_value_swap
swap comments too

* Changed `operator=` to exclude start/limit, which should never have been added.
* Changed `swap` to include comments. Hmm. That affects efficiency (but *not* for `operator=`) and probably nothing else in practice.

- issue #47
2015-01-18 11:31:47 -06:00
datadiode
c07ef37904 https://github.com/open-source-parsers/jsoncpp/issues/47 2015-01-18 10:05:25 +01:00
Christopher Dunn
62ab94ddd3 Merge pull request #117 from datadiode/integration
Simplify Reader::decodeNumber() / Remove unused functions
2015-01-17 08:10:59 -06:00
datadiode
09d352ac13 Remove unused functions 2015-01-17 13:26:23 +01:00
datadiode
50753bb808 Simplify Reader::decodeNumber() 2015-01-17 13:21:42 +01:00
Christopher Dunn
8f3aa220db consider these as binary, so git will not alter line-endings
issue #116
2015-01-16 16:29:07 -06:00
Christopher Dunn
73e127892e Merge branch 'fix-fail31' 2015-01-16 15:10:56 -06:00
Christopher Dunn
4997dfb8af 1.1.1 <- 1.1.0
slight change to fail on a bad float
2015-01-16 15:09:54 -06:00
datadiode
c1441ef5e0 stricter float parsing
fixes `test/jsonchecker/fail31.json`
(issue #113)
2015-01-16 15:05:12 -06:00
Christopher Dunn
e0bfb45000 Merge branch 'py3/2' 2015-01-16 14:53:22 -06:00
Christopher Dunn
4bc311503c just in case 2015-01-16 14:53:04 -06:00
Christopher Dunn
cd140b5141 py2 and py3 2015-01-16 14:52:56 -06:00
datadiode
01aee4a0dc Fix Python test scripts for Python 3 and Windows 2015-01-16 09:57:42 -06:00
Christopher Dunn
59a01652ab Merge pull request #114 from Gachapen/fix_cmake_output_dir
CMake: Remove set(CMAKE_*_OUTPUT_DIRECTORY)
2015-01-15 20:17:34 -06:00
Magnus Bjerke Vik
8371a4337c CMake: Remove set(CMAKE_*_OUTPUT_DIRECTORY)
With set(CMAKE_*_OUTPUT_DIRECTORY) when using jsoncpp as a sub project,
the parent project's executables and libraries will also be outputed to
jsoncpp's directory. By removing this, it is up to the parent projects
to decide where to put their and jsoncpp's executables and libraries.
2015-01-15 20:16:54 -06:00
Christopher Dunn
dc2e1c98b9 Merge pull request #111 from open-source-parsers/quotes-spaces-fixed
Quotes spaces fixed
2015-01-09 22:36:40 -06:00
Christopher Dunn
d98b5f4230 quote spaces in commands for Windows
See comments at:
    1a4dc3a888
2015-01-09 22:32:10 -06:00
Christopher Dunn
4ca9d25ccc Revert "Merge pull request #108 from open-source-parsers/quote-spaces"
This reverts commit dfc5f879c1, reversing
changes made to 0f6884f771.
2015-01-09 22:28:20 -06:00
17 changed files with 279 additions and 173 deletions

2
.gitignore vendored
View File

@@ -10,4 +10,4 @@
/libs/ /libs/
/doc/doxyfile /doc/doxyfile
/dist/ /dist/
/include/json/version.h #/include/json/version.h

View File

@@ -31,16 +31,6 @@ SET(PACKAGE_INSTALL_DIR lib${LIB_SUFFIX}/cmake
CACHE PATH "Install dir for cmake package config files") CACHE PATH "Install dir for cmake package config files")
MARK_AS_ADVANCED( RUNTIME_INSTALL_DIR ARCHIVE_INSTALL_DIR INCLUDE_INSTALL_DIR PACKAGE_INSTALL_DIR ) MARK_AS_ADVANCED( RUNTIME_INSTALL_DIR ARCHIVE_INSTALL_DIR INCLUDE_INSTALL_DIR PACKAGE_INSTALL_DIR )
# 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} # Set variable named ${VAR_NAME} to value ${VALUE}
FUNCTION(set_using_dynamic_name VAR_NAME VALUE) FUNCTION(set_using_dynamic_name VAR_NAME VALUE)
SET( "${VAR_NAME}" "${VALUE}" PARENT_SCOPE) SET( "${VAR_NAME}" "${VALUE}" PARENT_SCOPE)
@@ -93,6 +83,14 @@ if ( MSVC )
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 ") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 ")
endif( MSVC ) endif( MSVC )
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# using regular Clang or AppleClang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# using GCC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++0x")
endif()
IF(JSONCPP_WITH_WARNING_AS_ERROR) IF(JSONCPP_WITH_WARNING_AS_ERROR)
UseCompilationWarningAsError() UseCompilationWarningAsError()
ENDIF(JSONCPP_WITH_WARNING_AS_ERROR) ENDIF(JSONCPP_WITH_WARNING_AS_ERROR)

View File

@@ -7,6 +7,7 @@
#define CPPTL_JSON_ASSERTIONS_H_INCLUDED #define CPPTL_JSON_ASSERTIONS_H_INCLUDED
#include <stdlib.h> #include <stdlib.h>
#include <sstream>
#if !defined(JSON_IS_AMALGAMATION) #if !defined(JSON_IS_AMALGAMATION)
#include "config.h" #include "config.h"
@@ -16,26 +17,26 @@
#include <stdexcept> #include <stdexcept>
#define JSON_ASSERT(condition) \ #define JSON_ASSERT(condition) \
assert(condition); // @todo <= change this into an exception throw assert(condition); // @todo <= change this into an exception throw
#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message); #define JSON_FAIL_MESSAGE(message) do{std::ostringstream oss; oss << message; throw std::runtime_error(oss.str());}while(0)
//#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message)
#else // JSON_USE_EXCEPTION #else // JSON_USE_EXCEPTION
#define JSON_ASSERT(condition) assert(condition); #define JSON_ASSERT(condition) assert(condition);
// The call to assert() will show the failure message in debug builds. In // 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 // release bugs we abort, for a core-dump or debugger.
// 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) \ #define JSON_FAIL_MESSAGE(message) \
{ \ { \
assert(false&& message); \ std::ostringstream oss; oss << message; \
strcpy(reinterpret_cast<char*>(666), message); \ assert(false && oss.str().c_str()); \
exit(123); \ abort(); \
} }
#endif #endif
#define JSON_ASSERT_MESSAGE(condition, message) \ #define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) { \ if (!(condition)) { \
JSON_FAIL_MESSAGE(message) \ JSON_FAIL_MESSAGE(message); \
} }
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED #endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED

View File

@@ -187,7 +187,6 @@ private:
typedef std::deque<ErrorInfo> Errors; typedef std::deque<ErrorInfo> Errors;
bool expectToken(TokenType type, Token& token, const char* message);
bool readToken(Token& token); bool readToken(Token& token);
void skipSpaces(); void skipSpaces();
bool match(Location pattern, int patternLength); bool match(Location pattern, int patternLength);

View File

@@ -235,25 +235,26 @@ Json::Value obj_value(Json::objectValue); // {}
Value(const CppTL::ConstString& value); Value(const CppTL::ConstString& value);
#endif #endif
Value(bool value); Value(bool value);
/// Deep copy.
Value(const Value& other); Value(const Value& other);
~Value(); ~Value();
// Deep copy, then swap(other).
Value& operator=(Value other); Value& operator=(Value other);
/// Swap values. /// Swap everything.
/// \note Currently, comments are intentionally not swapped, for
/// both logic and efficiency.
void swap(Value& other); void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
void swapPayload(Value& other);
ValueType type() const; ValueType type() const;
/// Compare payload only, not comments etc.
bool operator<(const Value& other) const; bool operator<(const Value& other) const;
bool operator<=(const Value& other) const; bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const; bool operator>=(const Value& other) const;
bool operator>(const Value& other) const; bool operator>(const Value& other) const;
bool operator==(const Value& other) const; bool operator==(const Value& other) const;
bool operator!=(const Value& other) const; bool operator!=(const Value& other) const;
int compare(const Value& other) const; int compare(const Value& other) const;
const char* asCString() const; const char* asCString() const;
@@ -391,9 +392,24 @@ Json::Value obj_value(Json::objectValue); // {}
/// \return the removed Value, or null. /// \return the removed Value, or null.
/// \pre type() is objectValue or nullValue /// \pre type() is objectValue or nullValue
/// \post type() is unchanged /// \post type() is unchanged
/// \deprecated
Value removeMember(const char* key); Value removeMember(const char* key);
/// Same as removeMember(const char*) /// Same as removeMember(const char*)
/// \deprecated
Value removeMember(const std::string& key); Value removeMember(const std::string& key);
/** \brief Remove the named map member.
Update 'removed' iff removed.
\return true iff removed (no exceptions)
*/
bool removeMember(const char* key, Value* removed);
/** \brief Remove the indexed array element.
O(n) expensive operations.
Update 'removed' iff removed.
\return true iff removed (no exceptions)
*/
bool removeIndex(ArrayIndex i, Value* removed);
/// Return true if the object has a member named key. /// Return true if the object has a member named key.
bool isMember(const char* key) const; bool isMember(const char* key) const;
@@ -1081,6 +1097,14 @@ public:
} // namespace Json } // namespace Json
namespace std {
/// Specialize std::swap() for Json::Value.
template<>
inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
}
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop) #pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)

View File

@@ -4,9 +4,9 @@
#ifndef JSON_VERSION_H_INCLUDED #ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "1.1.0" # define JSONCPP_VERSION_STRING "1.3.0"
# define JSONCPP_VERSION_MAJOR 1 # define JSONCPP_VERSION_MAJOR 1
# define JSONCPP_VERSION_MINOR 1 # define JSONCPP_VERSION_MINOR 3
# define JSONCPP_VERSION_PATCH 0 # define JSONCPP_VERSION_PATCH 0
# define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))

View File

@@ -47,23 +47,6 @@ Features Features::strictMode() {
// Implementation of class Reader // Implementation of class Reader
// //////////////////////////////// // ////////////////////////////////
static inline bool in(Reader::Char c,
Reader::Char c1,
Reader::Char c2,
Reader::Char c3,
Reader::Char c4) {
return c == c1 || c == c2 || c == c3 || c == c4;
}
static inline bool in(Reader::Char c,
Reader::Char c1,
Reader::Char c2,
Reader::Char c3,
Reader::Char c4,
Reader::Char c5) {
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
}
static bool containsNewLine(Reader::Location begin, Reader::Location end) { static bool containsNewLine(Reader::Location begin, Reader::Location end) {
for (; begin < end; ++begin) for (; begin < end; ++begin)
if (*begin == '\n' || *begin == '\r') if (*begin == '\n' || *begin == '\r')
@@ -152,14 +135,9 @@ bool Reader::readValue() {
bool successful = true; bool successful = true;
if (collectComments_ && !commentsBefore_.empty()) { if (collectComments_ && !commentsBefore_.empty()) {
// Remove newline characters at the end of the comments // Remove newline at the end of the comment
size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); if (commentsBefore_[commentsBefore_.size() - 1] == '\n')
if (lastNonNewline != std::string::npos) { commentsBefore_.resize(commentsBefore_.size() - 1);
commentsBefore_.erase(lastNonNewline + 1);
} else {
commentsBefore_.clear();
}
currentValue().setComment(commentsBefore_, commentBefore); currentValue().setComment(commentsBefore_, commentBefore);
commentsBefore_ = ""; commentsBefore_ = "";
} }
@@ -180,26 +158,36 @@ bool Reader::readValue() {
successful = decodeString(token); successful = decodeString(token);
break; break;
case tokenTrue: case tokenTrue:
currentValue() = true; {
Value v(true);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
}
break; break;
case tokenFalse: case tokenFalse:
currentValue() = false; {
Value v(false);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
}
break; break;
case tokenNull: case tokenNull:
currentValue() = Value(); {
Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
}
break; break;
case tokenArraySeparator: case tokenArraySeparator:
if (features_.allowDroppedNullPlaceholders_) { if (features_.allowDroppedNullPlaceholders_) {
// "Un-read" the current token and mark the current value as a null // "Un-read" the current token and mark the current value as a null
// token. // token.
current_--; current_--;
currentValue() = Value(); Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(current_ - begin_ - 1); currentValue().setOffsetStart(current_ - begin_ - 1);
currentValue().setOffsetLimit(current_ - begin_); currentValue().setOffsetLimit(current_ - begin_);
break; break;
@@ -229,13 +217,6 @@ void Reader::skipCommentTokens(Token& token) {
} }
} }
bool Reader::expectToken(TokenType type, Token& token, const char* message) {
readToken(token);
if (token.type_ != type)
return addError(message, token);
return true;
}
bool Reader::readToken(Token& token) { bool Reader::readToken(Token& token) {
skipSpaces(); skipSpaces();
token.start_ = current_; token.start_ = current_;
@@ -351,14 +332,34 @@ bool Reader::readComment() {
return true; return true;
} }
static std::string normalizeEOL(Reader::Location begin, Reader::Location end) {
std::string normalized;
normalized.reserve(end - begin);
Reader::Location current = begin;
while (current != end) {
char c = *current++;
if (c == '\r') {
if (current != end && *current == '\n')
// convert dos EOL
++current;
// convert Mac EOL
normalized += '\n';
} else {
normalized += c;
}
}
return normalized;
}
void void
Reader::addComment(Location begin, Location end, CommentPlacement placement) { Reader::addComment(Location begin, Location end, CommentPlacement placement) {
assert(collectComments_); assert(collectComments_);
const std::string& normalized = normalizeEOL(begin, end);
if (placement == commentAfterOnSameLine) { if (placement == commentAfterOnSameLine) {
assert(lastValue_ != 0); assert(lastValue_ != 0);
lastValue_->setComment(std::string(begin, end), placement); lastValue_->setComment(normalized, placement);
} else { } else {
commentsBefore_ += std::string(begin, end); commentsBefore_ += normalized;
} }
} }
@@ -374,18 +375,38 @@ bool Reader::readCStyleComment() {
bool Reader::readCppStyleComment() { bool Reader::readCppStyleComment() {
while (current_ != end_) { while (current_ != end_) {
Char c = getNextChar(); Char c = getNextChar();
if (c == '\r' || c == '\n') if (c == '\n')
break; break;
if (c == '\r') {
// Consume DOS EOL. It will be normalized in addComment.
if (current_ != end_ && *current_ == '\n')
getNextChar();
// Break on Moc OS 9 EOL.
break;
}
} }
return true; return true;
} }
void Reader::readNumber() { void Reader::readNumber() {
while (current_ != end_) { const char *p = current_;
if (!(*current_ >= '0' && *current_ <= '9') && char c = '0'; // stopgap for already consumed character
!in(*current_, '.', 'e', 'E', '+', '-')) // integral part
break; while (c >= '0' && c <= '9')
++current_; c = (current_ = p) < end_ ? *p++ : 0;
// fractional part
if (c == '.') {
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
}
// exponential part
if (c == 'e' || c == 'E') {
c = (current_ = p) < end_ ? *p++ : 0;
if (c == '+' || c == '-')
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
} }
} }
@@ -404,7 +425,8 @@ bool Reader::readString() {
bool Reader::readObject(Token& tokenStart) { bool Reader::readObject(Token& tokenStart) {
Token tokenName; Token tokenName;
std::string name; std::string name;
currentValue() = Value(objectValue); Value init(objectValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_); currentValue().setOffsetStart(tokenStart.start_ - begin_);
while (readToken(tokenName)) { while (readToken(tokenName)) {
bool initialTokenOk = true; bool initialTokenOk = true;
@@ -457,7 +479,8 @@ bool Reader::readObject(Token& tokenStart) {
} }
bool Reader::readArray(Token& tokenStart) { bool Reader::readArray(Token& tokenStart) {
currentValue() = Value(arrayValue); Value init(arrayValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_); currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces(); skipSpaces();
if (*current_ == ']') // empty array if (*current_ == ']') // empty array
@@ -497,20 +520,13 @@ bool Reader::decodeNumber(Token& token) {
Value decoded; Value decoded;
if (!decodeNumber(token, decoded)) if (!decodeNumber(token, decoded))
return false; return false;
currentValue() = decoded; currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
return true; return true;
} }
bool Reader::decodeNumber(Token& token, Value& decoded) { bool Reader::decodeNumber(Token& token, Value& decoded) {
bool isDouble = false;
for (Location inspect = token.start_; inspect != token.end_; ++inspect) {
isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') ||
(*inspect == '-' && inspect != token.start_);
}
if (isDouble)
return decodeDouble(token, decoded);
// Attempts to parse the number as an integer. If the number is // Attempts to parse the number as an integer. If the number is
// larger than the maximum supported value of an integer then // larger than the maximum supported value of an integer then
// we decode the number as a double. // we decode the number as a double.
@@ -518,6 +534,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) {
bool isNegative = *current == '-'; bool isNegative = *current == '-';
if (isNegative) if (isNegative)
++current; ++current;
// TODO: Help the compiler do the div and mod at compile time or get rid of them.
Value::LargestUInt maxIntegerValue = Value::LargestUInt maxIntegerValue =
isNegative ? Value::LargestUInt(-Value::minLargestInt) isNegative ? Value::LargestUInt(-Value::minLargestInt)
: Value::maxLargestUInt; : Value::maxLargestUInt;
@@ -526,9 +543,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) {
while (current < token.end_) { while (current < token.end_) {
Char c = *current++; Char c = *current++;
if (c < '0' || c > '9') if (c < '0' || c > '9')
return addError("'" + std::string(token.start_, token.end_) + return decodeDouble(token, decoded);
"' is not a number.",
token);
Value::UInt digit(c - '0'); Value::UInt digit(c - '0');
if (value >= threshold) { if (value >= threshold) {
// We've hit or exceeded the max value divided by 10 (rounded down). If // We've hit or exceeded the max value divided by 10 (rounded down). If
@@ -555,7 +570,7 @@ bool Reader::decodeDouble(Token& token) {
Value decoded; Value decoded;
if (!decodeDouble(token, decoded)) if (!decodeDouble(token, decoded))
return false; return false;
currentValue() = decoded; currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
return true; return true;
@@ -598,10 +613,11 @@ bool Reader::decodeDouble(Token& token, Value& decoded) {
} }
bool Reader::decodeString(Token& token) { bool Reader::decodeString(Token& token) {
std::string decoded; std::string decoded_string;
if (!decodeString(token, decoded)) if (!decodeString(token, decoded_string))
return false; return false;
currentValue() = decoded; Value decoded(decoded_string);
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_);
return true; return true;

View File

@@ -340,7 +340,7 @@ Value::Value(const Value& other)
case stringValue: case stringValue:
if (other.value_.string_) { if (other.value_.string_) {
value_.string_ = duplicateStringValue(other.value_.string_); value_.string_ = duplicateStringValue(other.value_.string_);
allocated_ = true; allocated_ |= true;
} else { } else {
value_.string_ = 0; value_.string_ = 0;
allocated_ = false; allocated_ = false;
@@ -410,7 +410,7 @@ Value& Value::operator=(Value other) {
return *this; return *this;
} }
void Value::swap(Value& other) { void Value::swapPayload(Value& other) {
ValueType temp = type_; ValueType temp = type_;
type_ = other.type_; type_ = other.type_;
other.type_ = temp; other.type_ = temp;
@@ -418,6 +418,11 @@ void Value::swap(Value& other) {
int temp2 = allocated_; int temp2 = allocated_;
allocated_ = other.allocated_; allocated_ = other.allocated_;
other.allocated_ = temp2; other.allocated_ = temp2;
}
void Value::swap(Value& other) {
swapPayload(other);
std::swap(comments_, other.comments_);
std::swap(start_, other.start_); std::swap(start_, other.start_);
std::swap(limit_, other.limit_); std::swap(limit_, other.limit_);
} }
@@ -984,35 +989,74 @@ Value Value::get(const std::string& key, const Value& defaultValue) const {
return get(key.c_str(), defaultValue); return get(key.c_str(), defaultValue);
} }
bool Value::removeMember(const char* key, Value* removed) {
if (type_ != objectValue) {
return false;
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString actualKey(key, CZString::noDuplication);
ObjectValues::iterator it = value_.map_->find(actualKey);
if (it == value_.map_->end())
return false;
*removed = it->second;
value_.map_->erase(it);
return true;
#else
Value* value = value_.map_->find(key);
if (value) {
*removed = *value;
value_.map_.remove(key);
return true;
} else {
return false;
}
#endif
}
Value Value::removeMember(const char* key) { Value Value::removeMember(const char* key) {
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
"in Json::Value::removeMember(): requires objectValue"); "in Json::Value::removeMember(): requires objectValue");
if (type_ == nullValue) if (type_ == nullValue)
return null; return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString actualKey(key, CZString::noDuplication); Value removed; // null
ObjectValues::iterator it = value_.map_->find(actualKey); removeMember(key, &removed);
if (it == value_.map_->end()) return removed; // still null if removeMember() did nothing
return null;
Value old(it->second);
value_.map_->erase(it);
return old;
#else
Value* value = value_.map_->find(key);
if (value) {
Value old(*value);
value_.map_.remove(key);
return old;
} else {
return null;
}
#endif
} }
Value Value::removeMember(const std::string& key) { Value Value::removeMember(const std::string& key) {
return removeMember(key.c_str()); return removeMember(key.c_str());
} }
bool Value::removeIndex(ArrayIndex index, Value* removed) {
if (type_ != arrayValue) {
return false;
}
#ifdef JSON_VALUE_USE_INTERNAL_MAP
JSON_FAIL_MESSAGE("removeIndex is not implemented for ValueInternalArray.");
return false;
#else
CZString key(index);
ObjectValues::iterator it = value_.map_->find(key);
if (it == value_.map_->end()) {
return false;
}
*removed = it->second;
ArrayIndex oldSize = size();
// shift left all items left, into the place of the "removed"
for (ArrayIndex i = index; i < (oldSize - 1); ++i){
CZString key(i);
(*value_.map_)[key] = (*this)[i + 1];
}
// erase the last one ("leftover")
CZString keyLast(oldSize - 1);
ObjectValues::iterator itLast = value_.map_->find(keyLast);
value_.map_->erase(itLast);
return true;
#endif
}
#ifdef JSON_USE_CPPTL #ifdef JSON_USE_CPPTL
Value Value::get(const CppTL::ConstString& key, Value Value::get(const CppTL::ConstString& key,
const Value& defaultValue) const { const Value& defaultValue) const {

View File

@@ -421,26 +421,27 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) {
document_ += "\n"; document_ += "\n";
writeIndent(); writeIndent();
std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); const std::string& comment = root.getComment(commentBefore);
std::string::const_iterator iter = normalizedComment.begin(); std::string::const_iterator iter = comment.begin();
while (iter != normalizedComment.end()) { while (iter != comment.end()) {
document_ += *iter; document_ += *iter;
if (*iter == '\n' && *(iter + 1) == '/') if (*iter == '\n' &&
(iter != comment.end() && *(iter + 1) == '/'))
writeIndent(); writeIndent();
++iter; ++iter;
} }
// Comments are stripped of newlines, so add one here // Comments are stripped of trailing newlines, so add one here
document_ += "\n"; document_ += "\n";
} }
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
if (root.hasComment(commentAfterOnSameLine)) if (root.hasComment(commentAfterOnSameLine))
document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); document_ += " " + root.getComment(commentAfterOnSameLine);
if (root.hasComment(commentAfter)) { if (root.hasComment(commentAfter)) {
document_ += "\n"; document_ += "\n";
document_ += normalizeEOL(root.getComment(commentAfter)); document_ += root.getComment(commentAfter);
document_ += "\n"; document_ += "\n";
} }
} }
@@ -451,25 +452,6 @@ bool StyledWriter::hasCommentForValue(const Value& value) {
value.hasComment(commentAfter); value.hasComment(commentAfter);
} }
std::string StyledWriter::normalizeEOL(const std::string& text) {
std::string normalized;
normalized.reserve(text.length());
const char* begin = text.c_str();
const char* end = begin + text.length();
const char* current = begin;
while (current != end) {
char c = *current++;
if (c == '\r') // mac or dos EOL
{
if (*current == '\n') // convert dos EOL
++current;
normalized += '\n';
} else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
// Class StyledStreamWriter // Class StyledStreamWriter
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
@@ -646,17 +628,17 @@ void StyledStreamWriter::unindent() {
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
if (!root.hasComment(commentBefore)) if (!root.hasComment(commentBefore))
return; return;
*document_ << normalizeEOL(root.getComment(commentBefore)); *document_ << root.getComment(commentBefore);
*document_ << "\n"; *document_ << "\n";
} }
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
if (root.hasComment(commentAfterOnSameLine)) if (root.hasComment(commentAfterOnSameLine))
*document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); *document_ << " " + root.getComment(commentAfterOnSameLine);
if (root.hasComment(commentAfter)) { if (root.hasComment(commentAfter)) {
*document_ << "\n"; *document_ << "\n";
*document_ << normalizeEOL(root.getComment(commentAfter)); *document_ << root.getComment(commentAfter);
*document_ << "\n"; *document_ << "\n";
} }
} }
@@ -667,25 +649,6 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) {
value.hasComment(commentAfter); value.hasComment(commentAfter);
} }
std::string StyledStreamWriter::normalizeEOL(const std::string& text) {
std::string normalized;
normalized.reserve(text.length());
const char* begin = text.c_str();
const char* end = begin + text.length();
const char* current = begin;
while (current != end) {
char c = *current++;
if (c == '\r') // mac or dos EOL
{
if (*current == '\n') // convert dos EOL
++current;
normalized += '\n';
} else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
std::ostream& operator<<(std::ostream& sout, const Value& root) { std::ostream& operator<<(std::ostream& sout, const Value& root) {
Json::StyledStreamWriter writer; Json::StyledStreamWriter writer;
writer.write(sout, root); writer.write(sout, root);

View File

@@ -17,8 +17,8 @@
#define kint64min Json::Value::minInt64 #define kint64min Json::Value::minInt64
#define kuint64max Json::Value::maxUInt64 #define kuint64max Json::Value::maxUInt64
static const double kdint64max = double(kint64max); //static const double kdint64max = double(kint64max);
static const float kfint64max = float(kint64max); //static const float kfint64max = float(kint64max);
static const float kfint32max = float(kint32max); static const float kfint32max = float(kint32max);
static const float kfuint32max = float(kuint32max); static const float kfuint32max = float(kuint32max);
@@ -198,6 +198,18 @@ JSONTEST_FIXTURE(ValueTest, objects) {
object1_["some other id"] = "foo"; object1_["some other id"] = "foo";
JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]);
JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]);
// Remove.
Json::Value got;
bool did;
did = object1_.removeMember("some other id", &got);
JSONTEST_ASSERT_EQUAL(Json::Value("foo"), got);
JSONTEST_ASSERT_EQUAL(true, did);
got = Json::Value("bar");
did = object1_.removeMember("some other id", &got);
JSONTEST_ASSERT_EQUAL(Json::Value("bar"), got);
JSONTEST_ASSERT_EQUAL(false, did);
} }
JSONTEST_FIXTURE(ValueTest, arrays) { JSONTEST_FIXTURE(ValueTest, arrays) {
@@ -240,6 +252,10 @@ JSONTEST_FIXTURE(ValueTest, arrays) {
array1_[2] = Json::Value(17); array1_[2] = Json::Value(17);
JSONTEST_ASSERT_EQUAL(Json::Value(), array1_[1]); JSONTEST_ASSERT_EQUAL(Json::Value(), array1_[1]);
JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]); JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]);
Json::Value got;
JSONTEST_ASSERT_EQUAL(true, array1_.removeIndex(2, &got));
JSONTEST_ASSERT_EQUAL(Json::Value(17), got);
JSONTEST_ASSERT_EQUAL(false, array1_.removeIndex(2, &got)); // gone now
} }
JSONTEST_FIXTURE(ValueTest, null) { JSONTEST_FIXTURE(ValueTest, null) {

View File

@@ -1,5 +1,7 @@
.={} .={}
// Comment for array
.test=[] .test=[]
// Comment within array
.test[0]={} .test[0]={}
.test[0].a="aaa" .test[0].a="aaa"
.test[1]={} .test[1]={}

View File

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

View File

@@ -11,4 +11,13 @@
// Multiline comment cpp-style // Multiline comment cpp-style
// Second line // Second line
.cpp-test.c=3 .cpp-test.c=3
.cpp-test.d=4 // Comment before double
.cpp-test.d=4.1
// Comment before string
.cpp-test.e="e-string"
// Comment before true
.cpp-test.f=true
// Comment before false
.cpp-test.g=false
// Comment before null
.cpp-test.h=null

View File

@@ -12,6 +12,15 @@
// Multiline comment cpp-style // Multiline comment cpp-style
// Second line // Second line
"c" : 3, "c" : 3,
"d" : 4 // Comment before double
"d" : 4.1,
// Comment before string
"e" : "e-string",
// Comment before true
"f" : true,
// Comment before false
"g" : false,
// Comment before null
"h" : null
} }
} }

View File

@@ -1,12 +1,30 @@
from __future__ import print_function from __future__ import print_function
from __future__ import unicode_literals
from io import open
from glob import glob from glob import glob
import sys import sys
import os import os
import pipes import os.path
import optparse import optparse
VALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes ' VALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes '
def getStatusOutput(cmd):
"""
Return int, unicode (for both Python 2 and 3).
Note: os.popen().close() would return None for 0.
"""
pipe = os.popen(cmd)
process_output = pipe.read()
try:
# We have been using os.popen(). When we read() the result
# we get 'str' (bytes) in py2, and 'str' (unicode) in py3.
# Ugh! There must be a better way to handle this.
process_output = process_output.decode('utf-8')
except AttributeError:
pass # python3
status = pipe.close()
return status, process_output
def compareOutputs( expected, actual, message ): def compareOutputs( expected, actual, message ):
expected = expected.strip().replace('\r','').split('\n') expected = expected.strip().replace('\r','').split('\n')
actual = actual.strip().replace('\r','').split('\n') actual = actual.strip().replace('\r','').split('\n')
@@ -34,7 +52,7 @@ def compareOutputs( expected, actual, message ):
def safeReadFile( path ): def safeReadFile( path ):
try: try:
return file( path, 'rt' ).read() return open( path, 'rt', encoding = 'utf-8' ).read()
except IOError as e: except IOError as e:
return '<File "%s" is missing: %s>' % (path,e) return '<File "%s" is missing: %s>' % (path,e)
@@ -54,21 +72,20 @@ def runAllTests( jsontest_executable_path, input_dir = None,
is_json_checker_test = (input_path in test_jsonchecker) or expect_failure is_json_checker_test = (input_path in test_jsonchecker) or expect_failure
print('TESTING:', input_path, end=' ') print('TESTING:', input_path, end=' ')
options = is_json_checker_test and '--json-checker' or '' options = is_json_checker_test and '--json-checker' or ''
pipe = os.popen( "%s%s %s %s" % ( cmd = '%s%s %s "%s"' % (
valgrind_path, jsontest_executable_path, options, valgrind_path, jsontest_executable_path, options,
pipes.quote(input_path))) input_path)
process_output = pipe.read() status, process_output = getStatusOutput(cmd)
status = pipe.close()
if is_json_checker_test: if is_json_checker_test:
if expect_failure: if expect_failure:
if status is None: if not status:
print('FAILED') print('FAILED')
failed_tests.append( (input_path, 'Parsing should have failed:\n%s' % failed_tests.append( (input_path, 'Parsing should have failed:\n%s' %
safeReadFile(input_path)) ) safeReadFile(input_path)) )
else: else:
print('OK') print('OK')
else: else:
if status is not None: if status:
print('FAILED') print('FAILED')
failed_tests.append( (input_path, 'Parsing failed:\n' + process_output) ) failed_tests.append( (input_path, 'Parsing failed:\n' + process_output) )
else: else:
@@ -77,13 +94,13 @@ def runAllTests( jsontest_executable_path, input_dir = None,
base_path = os.path.splitext(input_path)[0] base_path = os.path.splitext(input_path)[0]
actual_output = safeReadFile( base_path + '.actual' ) actual_output = safeReadFile( base_path + '.actual' )
actual_rewrite_output = safeReadFile( base_path + '.actual-rewrite' ) actual_rewrite_output = safeReadFile( base_path + '.actual-rewrite' )
file(base_path + '.process-output','wt').write( process_output ) open(base_path + '.process-output', 'wt', encoding = 'utf-8').write( process_output )
if status: if status:
print('parsing failed') print('parsing failed')
failed_tests.append( (input_path, 'Parsing failed:\n' + process_output) ) failed_tests.append( (input_path, 'Parsing failed:\n' + process_output) )
else: else:
expected_output_path = os.path.splitext(input_path)[0] + '.expected' expected_output_path = os.path.splitext(input_path)[0] + '.expected'
expected_output = file( expected_output_path, 'rt' ).read() expected_output = open( expected_output_path, 'rt', encoding = 'utf-8' ).read()
detail = ( compareOutputs( expected_output, actual_output, 'input' ) detail = ( compareOutputs( expected_output, actual_output, 'input' )
or compareOutputs( expected_output, actual_rewrite_output, 'rewrite' ) ) or compareOutputs( expected_output, actual_rewrite_output, 'rewrite' ) )
if detail: if detail:

View File

@@ -1,4 +1,6 @@
from __future__ import print_function from __future__ import print_function
from __future__ import unicode_literals
from io import open
from glob import glob from glob import glob
import sys import sys
import os import os
@@ -19,7 +21,11 @@ class TestProxy(object):
else: else:
cmd = [] cmd = []
cmd.extend( [self.test_exe_path, '--test-auto'] + options ) cmd.extend( [self.test_exe_path, '--test-auto'] + options )
try:
process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
except:
print(cmd)
raise
stdout = process.communicate()[0] stdout = process.communicate()[0]
if process.returncode: if process.returncode:
return False, stdout return False, stdout
@@ -31,7 +37,7 @@ def runAllTests( exe_path, use_valgrind=False ):
if not status: if not status:
print("Failed to obtain unit tests list:\n" + test_names, file=sys.stderr) print("Failed to obtain unit tests list:\n" + test_names, file=sys.stderr)
return 1 return 1
test_names = [name.strip() for name in test_names.strip().split('\n')] test_names = [name.strip() for name in test_names.decode('utf-8').strip().split('\n')]
failures = [] failures = []
for name in test_names: for name in test_names:
print('TESTING %s:' % name, end=' ') print('TESTING %s:' % name, end=' ')

View File

@@ -1 +1 @@
1.1.0 1.3.0