Ran clang-format over all .h and .cpp files.

clang-format -i $(find . -name '*.h' -or -name '*.cpp')
This commit is contained in:
Aaron Jacobs 2014-07-01 08:48:54 +10:00
parent 1b137a3802
commit 9fa4e849a1
19 changed files with 6109 additions and 7206 deletions

View File

@ -14,7 +14,8 @@
#if JSON_USE_EXCEPTION #if JSON_USE_EXCEPTION
#include <stdexcept> #include <stdexcept>
#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw #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_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);
@ -23,10 +24,18 @@
// release bugs we write to invalid memory in order to crash hard, so that a // 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() // 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. // 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); } #define JSON_FAIL_MESSAGE(message) \
{ \
assert(false &&message); \
strcpy(reinterpret_cast<char *>(666), message); \
exit(123); \
}
#endif #endif
#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) } #define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message) \
}
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED #endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED

View File

@ -12,7 +12,8 @@
#include <cpptl/cpptl_autolink.h> #include <cpptl/cpptl_autolink.h>
#endif #endif
# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) #if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \
!defined(JSON_IN_CPPTL)
#define CPPTL_AUTOLINK_NAME "json" #define CPPTL_AUTOLINK_NAME "json"
#undef CPPTL_AUTOLINK_DLL #undef CPPTL_AUTOLINK_DLL
#ifdef JSON_DLL #ifdef JSON_DLL

View File

@ -11,15 +11,18 @@
/// If defined, indicates that json may leverage CppTL library /// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1 //# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of std::map /// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container. /// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1 //# define JSON_USE_CPPTL_SMALLMAP 1
/// If defined, indicates that Json specific container should be used /// If defined, indicates that Json specific container should be used
/// (hash table & simple deque container with customizable allocator). /// (hash table & simple deque container with customizable allocator).
/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 /// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332
//# define JSON_VALUE_USE_INTERNAL_MAP 1 //# define JSON_VALUE_USE_INTERNAL_MAP 1
/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. /// Force usage of standard new/malloc based allocator instead of memory pool
/// The memory pools allocator used optimization (initializing Value and ValueInternalLink /// based allocator.
/// The memory pools allocator used optimization (initializing Value and
/// ValueInternalLink
/// as if it was a POD) that may cause some validation tool to report errors. /// as if it was a POD) that may cause some validation tool to report errors.
/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
@ -35,7 +38,6 @@
/// Remarks: it is automatically defined in the generated amalgated header. /// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION // #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL #ifdef JSON_IN_CPPTL
#include <cpptl/config.h> #include <cpptl/config.h>
#ifndef JSON_USE_CPPTL #ifndef JSON_USE_CPPTL
@ -60,7 +62,8 @@
#define JSON_API #define JSON_API
#endif #endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled. // Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1 // #define JSON_NO_INT64 1
@ -68,8 +71,10 @@
// Microsoft Visual Studio 6 only support conversion from __int64 to double // Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64). // (no conversion from unsigned __int64).
#define JSON_USE_INT64_DOUBLE_CONVERSION 1 #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) // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// All projects I've ever seen with VS6 were using this globally (not bothering with pragma push/pop). // 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) #pragma warning(disable : 4786)
#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 #endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
@ -104,5 +109,4 @@ namespace Json {
#endif // if defined(JSON_NO_INT64) #endif // if defined(JSON_NO_INT64)
} // end namespace Json } // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED #endif // JSON_CONFIG_H_INCLUDED

View File

@ -16,17 +16,18 @@ namespace Json {
* This configuration object can be used to force the Reader or Writer * This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way. * to behave in a standard conforming way.
*/ */
class JSON_API Features class JSON_API Features {
{
public: public:
/** \brief A configuration that allows all features and assumes all strings are UTF-8. /** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed * - C & C++ comments are allowed
* - Root object can be any JSON value * - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8 * - Assumes Value strings are encoded in UTF-8
*/ */
static Features all(); static Features all();
/** \brief A configuration that is strictly compatible with the JSON specification. /** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden. * - Comments are forbidden.
* - Root object must be either an array or an object value. * - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8 * - Assumes Value strings are encoded in UTF-8
@ -40,7 +41,8 @@ namespace Json {
/// \c true if comments are allowed. Default: \c true. /// \c true if comments are allowed. Default: \c true.
bool allowComments_; bool allowComments_;
/// \c true if root must be either an array or an object value. Default: \c false. /// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_; bool strictRoot_;
/// \c true if dropped null placeholders are allowed. Default: \c false. /// \c true if dropped null placeholders are allowed. Default: \c false.

View File

@ -40,5 +40,4 @@ namespace Json {
} // namespace Json } // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED #endif // JSON_FORWARDS_H_INCLUDED

View File

@ -15,20 +15,20 @@
#include <stack> #include <stack>
#include <string> #include <string>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by... // Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4251) #pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json { namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value. /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
*Value.
* *
*/ */
class JSON_API Reader class JSON_API Reader {
{
public: public:
typedef char Char; typedef char Char;
typedef const Char *Location; typedef const Char *Location;
@ -39,8 +39,7 @@ namespace Json {
* that this is bytes, not codepoints. * that this is bytes, not codepoints.
* *
*/ */
struct StructuredError struct StructuredError {
{
size_t offset_start; size_t offset_start;
size_t offset_limit; size_t offset_limit;
std::string message; std::string message;
@ -56,69 +55,83 @@ namespace Json {
*/ */
Reader(const Features &features); Reader(const Features &features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
* \param document UTF-8 encoded string containing the document to read. * \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was * \param root [out] Contains the root value of the document if it was
* successfully parsed. * successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during * \param collectComments \c true to collect comment and allow writing them
* back during
* serialization, \c false to discard comments. * serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_ * This parameter is ignored if
* Features::allowComments_
* is \c false. * is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred. * \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/ */
bool parse( const std::string &document, bool
Value &root, parse(const std::string &document, Value &root, bool collectComments = true);
bool collectComments = true );
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. document.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
document to read.
\ Must be >= beginDoc. \ Must be >= beginDoc.
* \param root [out] Contains the root value of the document if it was * \param root [out] Contains the root value of the document if it was
* successfully parsed. * successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during * \param collectComments \c true to collect comment and allow writing them
back during
* serialization, \c false to discard comments. * serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_ * This parameter is ignored if
Features::allowComments_
* is \c false. * is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred. * \return \c true if the document was successfully parsed, \c false if an
error occurred.
*/ */
bool parse( const char *beginDoc, const char *endDoc, bool parse(const char *beginDoc,
const char *endDoc,
Value &root, Value &root,
bool collectComments = true); bool collectComments = true);
/// \brief Parse from input stream. /// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&). /// \see Json::operator>>(std::istream&, Json::Value&).
bool parse( std::istream &is, bool parse(std::istream &is, Value &root, bool collectComments = true);
Value &root,
bool collectComments = true );
/** \brief Returns a user friendly string that list errors in the parsed document. /** \brief Returns a user friendly string that list errors in the parsed
* \return Formatted error message with the list of errors with their location in * document.
* the parsed document. An empty string is returned if no error occurred * \return Formatted error message with the list of errors with their location
* in
* the parsed document. An empty string is returned if no error
* occurred
* during parsing. * during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix). * \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/ */
JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead")
std::string getFormatedErrorMessages() const; std::string getFormatedErrorMessages() const;
/** \brief Returns a user friendly string that list errors in the parsed document. /** \brief Returns a user friendly string that list errors in the parsed
* \return Formatted error message with the list of errors with their location in * document.
* the parsed document. An empty string is returned if no error occurred * \return Formatted error message with the list of errors with their location
* in
* the parsed document. An empty string is returned if no error
* occurred
* during parsing. * during parsing.
*/ */
std::string getFormattedErrorMessages() const; std::string getFormattedErrorMessages() const;
/** \brief Returns a vector of structured erros encounted while parsing. /** \brief Returns a vector of structured erros encounted while parsing.
* \return A (possibly empty) vector of StructuredError objects. Currently * \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate multiple * only one error can be returned, but the caller should tolerate
* multiple
* errors. This can occur if the parser recovers from a non-fatal * errors. This can occur if the parser recovers from a non-fatal
* parse error and then encounters additional errors. * parse error and then encounters additional errors.
*/ */
std::vector<StructuredError> getStructuredErrors() const; std::vector<StructuredError> getStructuredErrors() const;
private: private:
enum TokenType enum TokenType {
{
tokenEndOfStream = 0, tokenEndOfStream = 0,
tokenObjectBegin, tokenObjectBegin,
tokenObjectEnd, tokenObjectEnd,
@ -135,16 +148,14 @@ namespace Json {
tokenError tokenError
}; };
class Token class Token {
{
public: public:
TokenType type_; TokenType type_;
Location start_; Location start_;
Location end_; Location end_;
}; };
class ErrorInfo class ErrorInfo {
{
public: public:
Token token_; Token token_;
std::string message_; std::string message_;
@ -156,8 +167,7 @@ namespace Json {
bool expectToken(TokenType type, Token &token, const char *message); 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, bool match(Location pattern, int patternLength);
int patternLength );
bool readComment(); bool readComment();
bool readCStyleComment(); bool readCStyleComment();
bool readCppStyleComment(); bool readCppStyleComment();
@ -180,9 +190,7 @@ namespace Json {
Location &current, Location &current,
Location end, Location end,
unsigned int &unicode); unsigned int &unicode);
bool addError( const std::string &message, bool addError(const std::string &message, Token &token, Location extra = 0);
Token &token,
Location extra = 0 );
bool recoverFromError(TokenType skipUntilToken); bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const std::string &message, bool addErrorAndRecover(const std::string &message,
Token &token, Token &token,
@ -190,13 +198,10 @@ namespace Json {
void skipUntilSpace(); void skipUntilSpace();
Value &currentValue(); Value &currentValue();
Char getNextChar(); Char getNextChar();
void getLocationLineAndColumn( Location location, void
int &line, getLocationLineAndColumn(Location location, int &line, int &column) const;
int &column ) const;
std::string getLocationLineAndColumn(Location location) const; std::string getLocationLineAndColumn(Location location) const;
void addComment( Location begin, void addComment(Location begin, Location end, CommentPlacement placement);
Location end,
CommentPlacement placement );
void skipCommentTokens(Token &token); void skipCommentTokens(Token &token);
typedef std::stack<Value *> Nodes; typedef std::stack<Value *> Nodes;
@ -245,5 +250,4 @@ namespace Json {
#pragma warning(pop) #pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_READER_H_INCLUDED #endif // CPPTL_JSON_READER_H_INCLUDED

View File

@ -21,21 +21,20 @@
#include <cpptl/forwards.h> #include <cpptl/forwards.h>
#endif #endif
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by... // Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4251) #pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
/** \brief JSON (JavaScript Object Notation). /** \brief JSON (JavaScript Object Notation).
*/ */
namespace Json { namespace Json {
/** \brief Type of the value held by a Value object. /** \brief Type of the value held by a Value object.
*/ */
enum ValueType enum ValueType {
{
nullValue = 0, ///< 'null' value nullValue = 0, ///< 'null' value
intValue, ///< signed integer value intValue, ///< signed integer value
uintValue, ///< unsigned integer value uintValue, ///< unsigned integer value
@ -46,11 +45,11 @@ namespace Json {
objectValue ///< object value (collection of name/value pairs). objectValue ///< object value (collection of name/value pairs).
}; };
enum CommentPlacement enum CommentPlacement {
{
commentBefore = 0, ///< a comment placed on the line before a value commentBefore = 0, ///< a comment placed on the line before a value
commentAfterOnSameLine, ///< a comment just after a value on the same line commentAfterOnSameLine, ///< a comment just after a value on the same line
commentAfter, ///< a comment on the line after a value (only make sense for root value) commentAfter, ///< a comment on the line after a value (only make sense for
///root value)
numberOfCommentPlacement numberOfCommentPlacement
}; };
@ -73,23 +72,13 @@ namespace Json {
* object[code] = 1234; * object[code] = 1234;
* \endcode * \endcode
*/ */
class JSON_API StaticString class JSON_API StaticString {
{
public: public:
explicit StaticString( const char *czstring ) explicit StaticString(const char *czstring) : str_(czstring) {}
: str_( czstring )
{
}
operator const char *() const operator const char *() const { return str_; }
{
return str_;
}
const char *c_str() const const char *c_str() const { return str_; }
{
return str_;
}
private: private:
const char *str_; const char *str_;
@ -110,20 +99,21 @@ namespace Json {
* The type of the held value is represented by a #ValueType and * The type of the held value is represented by a #ValueType and
* can be obtained using type(). * can be obtained using type().
* *
* values of an #objectValue or #arrayValue can be accessed using operator[]() methods. * values of an #objectValue or #arrayValue can be accessed using operator[]()
*methods.
* Non const methods will automatically create the a #nullValue element * Non const methods will automatically create the a #nullValue element
* if it does not exist. * if it does not exist.
* The sequence of an #arrayValue will be automatically resize and initialized * The sequence of an #arrayValue will be automatically resize and initialized
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
* *
* The get() methods can be used to obtanis default value in the case the required element * The get() methods can be used to obtanis default value in the case the
*required element
* does not exist. * does not exist.
* *
* It is possible to iterate over the list of a #objectValue values using * It is possible to iterate over the list of a #objectValue values using
* the getMemberNames() method. * the getMemberNames() method.
*/ */
class JSON_API Value class JSON_API Value {
{
friend class ValueIteratorBase; friend class ValueIteratorBase;
#ifdef JSON_VALUE_USE_INTERNAL_MAP #ifdef JSON_VALUE_USE_INTERNAL_MAP
friend class ValueInternalLink; friend class ValueInternalLink;
@ -170,11 +160,9 @@ namespace Json {
private: private:
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
#ifndef JSON_VALUE_USE_INTERNAL_MAP #ifndef JSON_VALUE_USE_INTERNAL_MAP
class CZString class CZString {
{
public: public:
enum DuplicationPolicy enum DuplicationPolicy {
{
noDuplication = 0, noDuplication = 0,
duplicate, duplicate,
duplicateOnCopy duplicateOnCopy
@ -189,6 +177,7 @@ namespace Json {
ArrayIndex index() const; ArrayIndex index() const;
const char *c_str() const; const char *c_str() const;
bool isStaticString() const; bool isStaticString() const;
private: private:
void swap(CZString &other); void swap(CZString &other);
const char *cstr_; const char *cstr_;
@ -322,14 +311,16 @@ namespace Json {
void resize(ArrayIndex size); void resize(ArrayIndex size);
/// Access an array element (zero based index ). /// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are inserted /// If the array contains less than index element, then null value are
/// inserted
/// in the array so that its size is index+1. /// in the array so that its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish /// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
Value &operator[](ArrayIndex index); Value &operator[](ArrayIndex index);
/// Access an array element (zero based index ). /// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are inserted /// If the array contains less than index element, then null value are
/// inserted
/// in the array so that its size is index+1. /// in the array so that its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish /// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
@ -345,10 +336,10 @@ namespace Json {
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
const Value &operator[](int index) const; const Value &operator[](int index) const;
/// If the array contains at least index+1 elements, returns the element value, /// If the array contains at least index+1 elements, returns the element
/// value,
/// otherwise returns defaultValue. /// otherwise returns defaultValue.
Value get( ArrayIndex index, Value get(ArrayIndex index, const Value &defaultValue) const;
const Value &defaultValue ) const;
/// Return true if index < size(). /// Return true if index < size().
bool isValidIndex(ArrayIndex index) const; bool isValidIndex(ArrayIndex index) const;
/// \brief Append value to array at the end. /// \brief Append value to array at the end.
@ -358,13 +349,16 @@ namespace Json {
/// Access an object value by name, create a null member if it does not exist. /// Access an object value by name, create a null member if it does not exist.
Value &operator[](const char *key); Value &operator[](const char *key);
/// Access an object value by name, returns null if there is no member with that name. /// Access an object value by name, returns null if there is no member with
/// that name.
const Value &operator[](const char *key) const; const Value &operator[](const char *key) const;
/// Access an object value by name, create a null member if it does not exist. /// Access an object value by name, create a null member if it does not exist.
Value &operator[](const std::string &key); Value &operator[](const std::string &key);
/// Access an object value by name, returns null if there is no member with that name. /// Access an object value by name, returns null if there is no member with
/// that name.
const Value &operator[](const std::string &key) const; const Value &operator[](const std::string &key) const;
/** \brief Access an object value by name, create a null member if it does not exist. /** \brief Access an object value by name, create a null member if it does not
exist.
* If the object as no entry for that name, then the member name used to store * If the object as no entry for that name, then the member name used to store
* the new entry is not duplicated. * the new entry is not duplicated.
@ -379,19 +373,17 @@ namespace Json {
#ifdef JSON_USE_CPPTL #ifdef JSON_USE_CPPTL
/// Access an object value by name, create a null member if it does not exist. /// Access an object value by name, create a null member if it does not exist.
Value &operator[](const CppTL::ConstString &key); Value &operator[](const CppTL::ConstString &key);
/// Access an object value by name, returns null if there is no member with that name. /// Access an object value by name, returns null if there is no member with
/// that name.
const Value &operator[](const CppTL::ConstString &key) const; const Value &operator[](const CppTL::ConstString &key) const;
#endif #endif
/// Return the member named key if it exist, defaultValue otherwise. /// Return the member named key if it exist, defaultValue otherwise.
Value get( const char *key, Value get(const char *key, const Value &defaultValue) const;
const Value &defaultValue ) const;
/// Return the member named key if it exist, defaultValue otherwise. /// Return the member named key if it exist, defaultValue otherwise.
Value get( const std::string &key, Value get(const std::string &key, const Value &defaultValue) const;
const Value &defaultValue ) const;
#ifdef JSON_USE_CPPTL #ifdef JSON_USE_CPPTL
/// Return the member named key if it exist, defaultValue otherwise. /// Return the member named key if it exist, defaultValue otherwise.
Value get( const CppTL::ConstString &key, Value get(const CppTL::ConstString &key, const Value &defaultValue) const;
const Value &defaultValue ) const;
#endif #endif
/// \brief Remove and return the named member. /// \brief Remove and return the named member.
/// ///
@ -425,11 +417,9 @@ namespace Json {
//# endif //# endif
/// Comments must be //... or /* ... */ /// Comments must be //... or /* ... */
void setComment( const char *comment, void setComment(const char *comment, CommentPlacement placement);
CommentPlacement placement );
/// Comments must be //... or /* ... */ /// Comments must be //... or /* ... */
void setComment( const std::string &comment, void setComment(const std::string &comment, CommentPlacement placement);
CommentPlacement placement );
bool hasComment(CommentPlacement placement) const; bool hasComment(CommentPlacement placement) const;
/// Include delimiters and embedded newlines. /// Include delimiters and embedded newlines.
std::string getComment(CommentPlacement placement) const; std::string getComment(CommentPlacement placement) const;
@ -450,34 +440,22 @@ namespace Json {
size_t getOffsetLimit() const; size_t getOffsetLimit() const;
private: private:
Value &resolveReference( const char *key, Value &resolveReference(const char *key, bool isStatic);
bool isStatic );
#ifdef JSON_VALUE_USE_INTERNAL_MAP #ifdef JSON_VALUE_USE_INTERNAL_MAP
inline bool isItemAvailable() const inline bool isItemAvailable() const { return itemIsUsed_ == 0; }
{
return itemIsUsed_ == 0;
}
inline void setItemUsed( bool isUsed = true ) inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; }
{
itemIsUsed_ = isUsed ? 1 : 0;
}
inline bool isMemberNameStatic() const inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; }
{
return memberNameIsStatic_ == 0;
}
inline void setMemberNameIsStatic( bool isStatic ) inline void setMemberNameIsStatic(bool isStatic) {
{
memberNameIsStatic_ = isStatic ? 1 : 0; memberNameIsStatic_ = isStatic ? 1 : 0;
} }
#endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP #endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP
private: private:
struct CommentInfo struct CommentInfo {
{
CommentInfo(); CommentInfo();
~CommentInfo(); ~CommentInfo();
@ -495,8 +473,7 @@ namespace Json {
// } // }
//}; //};
union ValueHolder union ValueHolder {
{
LargestInt int_; LargestInt int_;
LargestUInt uint_; LargestUInt uint_;
double real_; double real_;
@ -523,11 +500,10 @@ namespace Json {
size_t limit_; size_t limit_;
}; };
/** \brief Experimental and untested: represents an element of the "path" to
/** \brief Experimental and untested: represents an element of the "path" to access a node. * access a node.
*/ */
class JSON_API PathArgument class JSON_API PathArgument {
{
public: public:
friend class Path; friend class Path;
@ -537,8 +513,7 @@ namespace Json {
PathArgument(const std::string &key); PathArgument(const std::string &key);
private: private:
enum Kind enum Kind {
{
kindNone = 0, kindNone = 0,
kindIndex, kindIndex,
kindKey kindKey
@ -559,8 +534,7 @@ namespace Json {
* - ".%" => member name is provided as parameter * - ".%" => member name is provided as parameter
* - ".[%]" => index is provied as parameter * - ".[%]" => index is provied as parameter
*/ */
class JSON_API Path class JSON_API Path {
{
public: public:
Path(const std::string &path, Path(const std::string &path,
const PathArgument &a1 = PathArgument(), const PathArgument &a1 = PathArgument(),
@ -570,32 +544,29 @@ namespace Json {
const PathArgument &a5 = PathArgument()); const PathArgument &a5 = PathArgument());
const Value &resolve(const Value &root) const; const Value &resolve(const Value &root) const;
Value resolve( const Value &root, Value resolve(const Value &root, const Value &defaultValue) const;
const Value &defaultValue ) const; /// Creates the "path" to access the specified node and returns a reference on
/// Creates the "path" to access the specified node and returns a reference on the node. /// the node.
Value &make(Value &root) const; Value &make(Value &root) const;
private: private:
typedef std::vector<const PathArgument *> InArgs; typedef std::vector<const PathArgument *> InArgs;
typedef std::vector<PathArgument> Args; typedef std::vector<PathArgument> Args;
void makePath( const std::string &path, void makePath(const std::string &path, const InArgs &in);
const InArgs &in );
void addPathInArg(const std::string &path, void addPathInArg(const std::string &path,
const InArgs &in, const InArgs &in,
InArgs::const_iterator &itInArg, InArgs::const_iterator &itInArg,
PathArgument::Kind kind); PathArgument::Kind kind);
void invalidPath( const std::string &path, void invalidPath(const std::string &path, int location);
int location );
Args args_; Args args_;
}; };
#ifdef JSON_VALUE_USE_INTERNAL_MAP #ifdef JSON_VALUE_USE_INTERNAL_MAP
/** \brief Allocator to customize Value internal map. /** \brief Allocator to customize Value internal map.
* Below is an example of a simple implementation (default implementation actually * Below is an example of a simple implementation (default implementation
actually
* use memory pool for speed). * use memory pool for speed).
* \code * \code
class DefaultValueMapAllocator : public ValueMapAllocator class DefaultValueMapAllocator : public ValueMapAllocator
@ -638,8 +609,7 @@ namespace Json {
}; };
* \endcode * \endcode
*/ */
class JSON_API ValueMapAllocator class JSON_API ValueMapAllocator {
{
public: public:
virtual ~ValueMapAllocator(); virtual ~ValueMapAllocator();
virtual ValueInternalMap *newMap() = 0; virtual ValueInternalMap *newMap() = 0;
@ -654,10 +624,11 @@ namespace Json {
/** \brief ValueInternalMap hash-map bucket chain link (for internal use only). /** \brief ValueInternalMap hash-map bucket chain link (for internal use only).
* \internal previous_ & next_ allows for bidirectional traversal. * \internal previous_ & next_ allows for bidirectional traversal.
*/ */
class JSON_API ValueInternalLink class JSON_API ValueInternalLink {
{
public: public:
enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. enum {
itemPerLink = 6
}; // sizeof(ValueInternalLink) = 128 on 32 bits architecture.
enum InternalFlags { enum InternalFlags {
flagAvailable = 0, flagAvailable = 0,
flagUsed = 1 flagUsed = 1
@ -673,37 +644,34 @@ namespace Json {
ValueInternalLink *next_; ValueInternalLink *next_;
}; };
/** \brief A linked page based hash-table implementation used internally by
/** \brief A linked page based hash-table implementation used internally by Value. *Value.
* \internal ValueInternalMap is a tradional bucket based hash-table, with a linked * \internal ValueInternalMap is a tradional bucket based hash-table, with a
*linked
* list in each bucket to handle collision. There is an addional twist in that * list in each bucket to handle collision. There is an addional twist in that
* each node of the collision linked list is a page containing a fixed amount of * each node of the collision linked list is a page containing a fixed amount of
* value. This provides a better compromise between memory usage and speed. * value. This provides a better compromise between memory usage and speed.
* *
* Each bucket is made up of a chained list of ValueInternalLink. The last * Each bucket is made up of a chained list of ValueInternalLink. The last
* link of a given bucket can be found in the 'previous_' field of the following bucket. * link of a given bucket can be found in the 'previous_' field of the following
* The last link of the last bucket is stored in tailLink_ as it has no following bucket. *bucket.
* Only the last link of a bucket may contains 'available' item. The last link always * The last link of the last bucket is stored in tailLink_ as it has no
*following bucket.
* Only the last link of a bucket may contains 'available' item. The last link
*always
* contains at least one element unless is it the bucket one very first link. * contains at least one element unless is it the bucket one very first link.
*/ */
class JSON_API ValueInternalMap class JSON_API ValueInternalMap {
{
friend class ValueIteratorBase; friend class ValueIteratorBase;
friend class Value; friend class Value;
public: public:
typedef unsigned int HashKey; typedef unsigned int HashKey;
typedef unsigned int BucketIndex; typedef unsigned int BucketIndex;
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
struct IteratorState struct IteratorState {
{ IteratorState() : map_(0), link_(0), itemIndex_(0), bucketIndex_(0) {}
IteratorState()
: map_(0)
, link_(0)
, itemIndex_(0)
, bucketIndex_(0)
{
}
ValueInternalMap *map_; ValueInternalMap *map_;
ValueInternalLink *link_; ValueInternalLink *link_;
BucketIndex itemIndex_; BucketIndex itemIndex_;
@ -730,8 +698,7 @@ namespace Json {
Value *find(const char *key); Value *find(const char *key);
Value &resolveReference( const char *key, Value &resolveReference(const char *key, bool isStatic);
bool isStatic );
void remove(const char *key); void remove(const char *key);
@ -746,9 +713,7 @@ namespace Json {
ValueInternalLink *link, ValueInternalLink *link,
BucketIndex index); BucketIndex index);
Value &unsafeAdd( const char *key, Value &unsafeAdd(const char *key, bool isStatic, HashKey hashedKey);
bool isStatic,
HashKey hashedKey );
HashKey hash(const char *key) const; HashKey hash(const char *key) const;
@ -775,33 +740,33 @@ namespace Json {
/** \brief A simplified deque implementation used internally by Value. /** \brief A simplified deque implementation used internally by Value.
* \internal * \internal
* It is based on a list of fixed "page", each page contains a fixed number of items. * It is based on a list of fixed "page", each page contains a fixed number of
* Instead of using a linked-list, a array of pointer is used for fast item look-up. *items.
* Instead of using a linked-list, a array of pointer is used for fast item
*look-up.
* Look-up for an element is as follow: * Look-up for an element is as follow:
* - compute page index: pageIndex = itemIndex / itemsPerPage * - compute page index: pageIndex = itemIndex / itemsPerPage
* - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage]
* *
* Insertion is amortized constant time (only the array containing the index of pointers * Insertion is amortized constant time (only the array containing the index of
*pointers
* need to be reallocated when items are appended). * need to be reallocated when items are appended).
*/ */
class JSON_API ValueInternalArray class JSON_API ValueInternalArray {
{
friend class Value; friend class Value;
friend class ValueIteratorBase; friend class ValueIteratorBase;
public: public:
enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. enum {
itemsPerPage = 8
}; // should be a power of 2 for fast divide and modulo.
typedef Value::ArrayIndex ArrayIndex; typedef Value::ArrayIndex ArrayIndex;
typedef unsigned int PageIndex; typedef unsigned int PageIndex;
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
struct IteratorState // Must be a POD struct IteratorState // Must be a POD
{ {
IteratorState() IteratorState() : array_(0), currentPageIndex_(0), currentItemIndex_(0) {}
: array_(0)
, currentPageIndex_(0)
, currentItemIndex_(0)
{
}
ValueInternalArray *array_; ValueInternalArray *array_;
Value **currentPageIndex_; Value **currentPageIndex_;
unsigned int currentItemIndex_; unsigned int currentItemIndex_;
@ -844,7 +809,8 @@ namespace Json {
PageIndex pageCount_; PageIndex pageCount_;
}; };
/** \brief Experimental: do not use. Allocator to customize Value internal array. /** \brief Experimental: do not use. Allocator to customize Value internal
array.
* Below is an example of a simple implementation (actual implementation use * Below is an example of a simple implementation (actual implementation use
* memory pool). * memory pool).
\code \code
@ -871,8 +837,10 @@ public: // overridden from ValueArrayAllocator
} }
virtual void reallocateArrayPageIndex( Value **&indexes, virtual void reallocateArrayPageIndex( Value **&indexes,
ValueInternalArray::PageIndex &indexCount, ValueInternalArray::PageIndex
ValueInternalArray::PageIndex minNewIndexCount ) &indexCount,
ValueInternalArray::PageIndex
minNewIndexCount )
{ {
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
if ( minNewIndexCount > newIndexCount ) if ( minNewIndexCount > newIndexCount )
@ -892,7 +860,8 @@ public: // overridden from ValueArrayAllocator
virtual Value *allocateArrayPage() virtual Value *allocateArrayPage()
{ {
return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); return static_cast<Value *>( malloc( sizeof(Value) *
ValueInternalArray::itemsPerPage ) );
} }
virtual void releaseArrayPage( Value *value ) virtual void releaseArrayPage( Value *value )
@ -903,8 +872,7 @@ public: // overridden from ValueArrayAllocator
}; };
\endcode \endcode
*/ */
class JSON_API ValueArrayAllocator class JSON_API ValueArrayAllocator {
{
public: public:
virtual ~ValueArrayAllocator(); virtual ~ValueArrayAllocator();
virtual ValueInternalArray *newArray() = 0; virtual ValueInternalArray *newArray() = 0;
@ -918,25 +886,26 @@ public: // overridden from ValueArrayAllocator
* \param indexCount [input] current number of pages in the index. * \param indexCount [input] current number of pages in the index.
* [output] number of page the reallocated index can handle. * [output] number of page the reallocated index can handle.
* \b MUST be >= \a minNewIndexCount. * \b MUST be >= \a minNewIndexCount.
* \param minNewIndexCount Minimum number of page the new index must be able to * \param minNewIndexCount Minimum number of page the new index must be able
* to
* handle. * handle.
*/ */
virtual void reallocateArrayPageIndex( Value **&indexes, virtual void
reallocateArrayPageIndex(Value **&indexes,
ValueInternalArray::PageIndex &indexCount, ValueInternalArray::PageIndex &indexCount,
ValueInternalArray::PageIndex minNewIndexCount) = 0; ValueInternalArray::PageIndex minNewIndexCount) = 0;
virtual void releaseArrayPageIndex( Value **indexes, virtual void
releaseArrayPageIndex(Value **indexes,
ValueInternalArray::PageIndex indexCount) = 0; ValueInternalArray::PageIndex indexCount) = 0;
virtual Value *allocateArrayPage() = 0; virtual Value *allocateArrayPage() = 0;
virtual void releaseArrayPage(Value *value) = 0; virtual void releaseArrayPage(Value *value) = 0;
}; };
#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
/** \brief base class for Value iterators. /** \brief base class for Value iterators.
* *
*/ */
class JSON_API ValueIteratorBase class JSON_API ValueIteratorBase {
{
public: public:
typedef std::bidirectional_iterator_tag iterator_category; typedef std::bidirectional_iterator_tag iterator_category;
typedef unsigned int size_t; typedef unsigned int size_t;
@ -951,28 +920,23 @@ public: // overridden from ValueArrayAllocator
ValueIteratorBase(const ValueInternalMap::IteratorState &state); ValueIteratorBase(const ValueInternalMap::IteratorState &state);
#endif #endif
bool operator ==( const SelfType &other ) const bool operator==(const SelfType &other) const { return isEqual(other); }
{
return isEqual( other );
}
bool operator !=( const SelfType &other ) const bool operator!=(const SelfType &other) const { return !isEqual(other); }
{
return !isEqual( other );
}
difference_type operator -( const SelfType &other ) const difference_type operator-(const SelfType &other) const {
{
return computeDistance(other); return computeDistance(other);
} }
/// Return either the index or the member name of the referenced value as a Value. /// Return either the index or the member name of the referenced value as a
/// Value.
Value key() const; Value key() const;
/// Return the index of the referenced Value. -1 if it is not an arrayValue. /// Return the index of the referenced Value. -1 if it is not an arrayValue.
UInt index() const; UInt index() const;
/// Return the member name of the referenced Value. "" if it is not an objectValue. /// Return the member name of the referenced Value. "" if it is not an
/// objectValue.
const char *memberName() const; const char *memberName() const;
protected: protected:
@ -994,8 +958,7 @@ public: // overridden from ValueArrayAllocator
// Indicates that iterator is for a null value. // Indicates that iterator is for a null value.
bool isNull_; bool isNull_;
#else #else
union union {
{
ValueInternalArray::IteratorState array_; ValueInternalArray::IteratorState array_;
ValueInternalMap::IteratorState map_; ValueInternalMap::IteratorState map_;
} iterator_; } iterator_;
@ -1006,9 +969,9 @@ public: // overridden from ValueArrayAllocator
/** \brief const iterator for object and array value. /** \brief const iterator for object and array value.
* *
*/ */
class JSON_API ValueConstIterator : public ValueIteratorBase class JSON_API ValueConstIterator : public ValueIteratorBase {
{
friend class Value; friend class Value;
public: public:
typedef const Value value_type; typedef const Value value_type;
typedef unsigned int size_t; typedef unsigned int size_t;
@ -1018,6 +981,7 @@ public: // overridden from ValueArrayAllocator
typedef ValueConstIterator SelfType; typedef ValueConstIterator SelfType;
ValueConstIterator(); ValueConstIterator();
private: private:
/*! \internal Use by Value to create an iterator. /*! \internal Use by Value to create an iterator.
*/ */
@ -1030,44 +994,36 @@ public: // overridden from ValueArrayAllocator
public: public:
SelfType &operator=(const ValueIteratorBase &other); SelfType &operator=(const ValueIteratorBase &other);
SelfType operator++( int ) SelfType operator++(int) {
{
SelfType temp(*this); SelfType temp(*this);
++*this; ++*this;
return temp; return temp;
} }
SelfType operator--( int ) SelfType operator--(int) {
{
SelfType temp(*this); SelfType temp(*this);
--*this; --*this;
return temp; return temp;
} }
SelfType &operator--() SelfType &operator--() {
{
decrement(); decrement();
return *this; return *this;
} }
SelfType &operator++() SelfType &operator++() {
{
increment(); increment();
return *this; return *this;
} }
reference operator *() const reference operator*() const { return deref(); }
{
return deref();
}
}; };
/** \brief Iterator for object and array value. /** \brief Iterator for object and array value.
*/ */
class JSON_API ValueIterator : public ValueIteratorBase class JSON_API ValueIterator : public ValueIteratorBase {
{
friend class Value; friend class Value;
public: public:
typedef Value value_type; typedef Value value_type;
typedef unsigned int size_t; typedef unsigned int size_t;
@ -1079,6 +1035,7 @@ public: // overridden from ValueArrayAllocator
ValueIterator(); ValueIterator();
ValueIterator(const ValueConstIterator &other); ValueIterator(const ValueConstIterator &other);
ValueIterator(const ValueIterator &other); ValueIterator(const ValueIterator &other);
private: private:
/*! \internal Use by Value to create an iterator. /*! \internal Use by Value to create an iterator.
*/ */
@ -1089,48 +1046,37 @@ public: // overridden from ValueArrayAllocator
ValueIterator(const ValueInternalMap::IteratorState &state); ValueIterator(const ValueInternalMap::IteratorState &state);
#endif #endif
public: public:
SelfType &operator=(const SelfType &other); SelfType &operator=(const SelfType &other);
SelfType operator++( int ) SelfType operator++(int) {
{
SelfType temp(*this); SelfType temp(*this);
++*this; ++*this;
return temp; return temp;
} }
SelfType operator--( int ) SelfType operator--(int) {
{
SelfType temp(*this); SelfType temp(*this);
--*this; --*this;
return temp; return temp;
} }
SelfType &operator--() SelfType &operator--() {
{
decrement(); decrement();
return *this; return *this;
} }
SelfType &operator++() SelfType &operator++() {
{
increment(); increment();
return *this; return *this;
} }
reference operator *() const reference operator*() const { return deref(); }
{
return deref();
}
}; };
} // namespace Json } // namespace Json
#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)
#endif // CPPTL_JSON_H_INCLUDED #endif // CPPTL_JSON_H_INCLUDED

View File

@ -9,6 +9,8 @@
#define JSONCPP_VERSION_MINOR 6 #define JSONCPP_VERSION_MINOR 6
#define JSONCPP_VERSION_PATCH 0 #define JSONCPP_VERSION_PATCH 0
#define JSONCPP_VERSION_QUALIFIER -dev #define JSONCPP_VERSION_QUALIFIER -dev
# 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))
#endif // JSON_VERSION_H_INCLUDED #endif // JSON_VERSION_H_INCLUDED

View File

@ -12,35 +12,35 @@
#include <vector> #include <vector>
#include <string> #include <string>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to be used by... // Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4251) #pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json { namespace Json {
class Value; class Value;
/** \brief Abstract class for writers. /** \brief Abstract class for writers.
*/ */
class JSON_API Writer class JSON_API Writer {
{
public: public:
virtual ~Writer(); virtual ~Writer();
virtual std::string write(const Value &root) = 0; virtual std::string write(const Value &root) = 0;
}; };
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly). /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
*without formatting (not human friendly).
* *
* The JSON document is written in a single line. It is not intended for 'human' consumption, * The JSON document is written in a single line. It is not intended for 'human'
*consumption,
* but may be usefull to support feature such as RPC where bandwith is limited. * but may be usefull to support feature such as RPC where bandwith is limited.
* \sa Reader, Value * \sa Reader, Value
*/ */
class JSON_API FastWriter : public Writer class JSON_API FastWriter : public Writer {
{
public: public:
FastWriter(); FastWriter();
virtual ~FastWriter() {} virtual ~FastWriter() {}
@ -65,26 +65,30 @@ namespace Json {
bool dropNullPlaceholders_; bool dropNullPlaceholders_;
}; };
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way. /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
*human friendly way.
* *
* The rules for line break and indent are as follow: * The rules for line break and indent are as follow:
* - Object value: * - Object value:
* - if empty then print {} without indent and line break * - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line * - if not empty the print '{', line break & indent, print one value per
*line
* and then unindent and line break and print '}'. * and then unindent and line break and print '}'.
* - Array value: * - Array value:
* - if empty then print [] without indent and line break * - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types, * - if the array contains no object value, empty array or some other value
* and all the values fit on one lines, then print the array on a single line. *types,
* and all the values fit on one lines, then print the array on a single
*line.
* - otherwise, it the values do not fit on one line, or the array contains * - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line. * object or non empty array, then print one value per line.
* *
* If the Value have comments then they are outputed according to their #CommentPlacement. * If the Value have comments then they are outputed according to their
*#CommentPlacement.
* *
* \sa Reader, Value, Value::setComment() * \sa Reader, Value, Value::setComment()
*/ */
class JSON_API StyledWriter: public Writer class JSON_API StyledWriter : public Writer {
{
public: public:
StyledWriter(); StyledWriter();
virtual ~StyledWriter() {} virtual ~StyledWriter() {}
@ -120,28 +124,32 @@ namespace Json {
bool addChildValues_; bool addChildValues_;
}; };
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way, /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
human friendly way,
to a stream rather than to a string. to a stream rather than to a string.
* *
* The rules for line break and indent are as follow: * The rules for line break and indent are as follow:
* - Object value: * - Object value:
* - if empty then print {} without indent and line break * - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line * - if not empty the print '{', line break & indent, print one value per
line
* and then unindent and line break and print '}'. * and then unindent and line break and print '}'.
* - Array value: * - Array value:
* - if empty then print [] without indent and line break * - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types, * - if the array contains no object value, empty array or some other value
* and all the values fit on one lines, then print the array on a single line. types,
* and all the values fit on one lines, then print the array on a single
line.
* - otherwise, it the values do not fit on one line, or the array contains * - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line. * object or non empty array, then print one value per line.
* *
* If the Value have comments then they are outputed according to their #CommentPlacement. * If the Value have comments then they are outputed according to their
#CommentPlacement.
* *
* \param indentation Each level will be indented by this amount extra. * \param indentation Each level will be indented by this amount extra.
* \sa Reader, Value, Value::setComment() * \sa Reader, Value, Value::setComment()
*/ */
class JSON_API StyledStreamWriter class JSON_API StyledStreamWriter {
{
public: public:
StyledStreamWriter(std::string indentation = "\t"); StyledStreamWriter(std::string indentation = "\t");
~StyledStreamWriter() {} ~StyledStreamWriter() {}
@ -150,7 +158,8 @@ namespace Json {
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.) * \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize. * \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not return a value. * \note There is no point in deriving from Writer, since write() should not
* return a value.
*/ */
void write(std::ostream &out, const Value &root); void write(std::ostream &out, const Value &root);
@ -194,10 +203,8 @@ namespace Json {
} // namespace Json } // namespace Json
#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)
#endif // JSON_WRITER_H_INCLUDED #endif // JSON_WRITER_H_INCLUDED

View File

@ -6,7 +6,6 @@
/* This executable is used for testing parser/writer using real JSON files. /* This executable is used for testing parser/writer using real JSON files.
*/ */
#include <json/json.h> #include <json/json.h>
#include <algorithm> // sort #include <algorithm> // sort
#include <stdio.h> #include <stdio.h>
@ -15,9 +14,7 @@
#pragma warning(disable : 4996) // disable fopen deprecation warning #pragma warning(disable : 4996) // disable fopen deprecation warning
#endif #endif
static std::string static std::string normalizeFloatingPointStr(double value) {
normalizeFloatingPointStr( double value )
{
char buffer[32]; char buffer[32];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
sprintf_s(buffer, sizeof(buffer), "%.16g", value); sprintf_s(buffer, sizeof(buffer), "%.16g", value);
@ -27,14 +24,16 @@ normalizeFloatingPointStr( double value )
buffer[sizeof(buffer) - 1] = 0; buffer[sizeof(buffer) - 1] = 0;
std::string s(buffer); std::string s(buffer);
std::string::size_type index = s.find_last_of("eE"); std::string::size_type index = s.find_last_of("eE");
if ( index != std::string::npos ) if (index != std::string::npos) {
{ std::string::size_type hasSign =
std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0; (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
std::string::size_type exponentStartIndex = index + 1 + hasSign; std::string::size_type exponentStartIndex = index + 1 + hasSign;
std::string normalized = s.substr(0, exponentStartIndex); std::string normalized = s.substr(0, exponentStartIndex);
std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex ); std::string::size_type indexDigit =
s.find_first_not_of('0', exponentStartIndex);
std::string exponent = "0"; std::string exponent = "0";
if ( indexDigit != std::string::npos ) // There is an exponent different from 0 if (indexDigit !=
std::string::npos) // There is an exponent different from 0
{ {
exponent = s.substr(indexDigit); exponent = s.substr(indexDigit);
} }
@ -43,10 +42,7 @@ normalizeFloatingPointStr( double value )
return s; return s;
} }
static std::string readInputTestFile(const char *path) {
static std::string
readInputTestFile( const char *path )
{
FILE *file = fopen(path, "rb"); FILE *file = fopen(path, "rb");
if (!file) if (!file)
return std::string(""); return std::string("");
@ -64,21 +60,28 @@ readInputTestFile( const char *path )
} }
static void static void
printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." ) printValueTree(FILE *fout, Json::Value &value, const std::string &path = ".") {
{ switch (value.type()) {
switch ( value.type() )
{
case Json::nullValue: case Json::nullValue:
fprintf(fout, "%s=null\n", path.c_str()); fprintf(fout, "%s=null\n", path.c_str());
break; break;
case Json::intValue: case Json::intValue:
fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() ); fprintf(fout,
"%s=%s\n",
path.c_str(),
Json::valueToString(value.asLargestInt()).c_str());
break; break;
case Json::uintValue: case Json::uintValue:
fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() ); fprintf(fout,
"%s=%s\n",
path.c_str(),
Json::valueToString(value.asLargestUInt()).c_str());
break; break;
case Json::realValue: case Json::realValue:
fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() ); fprintf(fout,
"%s=%s\n",
path.c_str(),
normalizeFloatingPointStr(value.asDouble()).c_str());
break; break;
case Json::stringValue: case Json::stringValue:
fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str()); fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
@ -86,12 +89,10 @@ printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
case Json::booleanValue: case Json::booleanValue:
fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false"); fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
break; break;
case Json::arrayValue: case Json::arrayValue: {
{
fprintf(fout, "%s=[]\n", path.c_str()); fprintf(fout, "%s=[]\n", path.c_str());
int size = value.size(); int size = value.size();
for ( int index =0; index < size; ++index ) for (int index = 0; index < size; ++index) {
{
static char buffer[16]; static char buffer[16];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
sprintf_s(buffer, sizeof(buffer), "[%d]", index); sprintf_s(buffer, sizeof(buffer), "[%d]", index);
@ -100,52 +101,42 @@ printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
#endif #endif
printValueTree(fout, value[index], path + buffer); printValueTree(fout, value[index], path + buffer);
} }
} } break;
break; case Json::objectValue: {
case Json::objectValue:
{
fprintf(fout, "%s={}\n", path.c_str()); fprintf(fout, "%s={}\n", path.c_str());
Json::Value::Members members(value.getMemberNames()); Json::Value::Members members(value.getMemberNames());
std::sort(members.begin(), members.end()); std::sort(members.begin(), members.end());
std::string suffix = *(path.end() - 1) == '.' ? "" : "."; std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
for (Json::Value::Members::iterator it = members.begin(); for (Json::Value::Members::iterator it = members.begin();
it != members.end(); it != members.end();
++it ) ++it) {
{
const std::string &name = *it; const std::string &name = *it;
printValueTree(fout, value[name], path + suffix + name); printValueTree(fout, value[name], path + suffix + name);
} }
} } break;
break;
default: default:
break; break;
} }
} }
static int parseAndSaveValueTree(const std::string &input,
static int
parseAndSaveValueTree( const std::string &input,
const std::string &actual, const std::string &actual,
const std::string &kind, const std::string &kind,
Json::Value &root, Json::Value &root,
const Json::Features &features, const Json::Features &features,
bool parseOnly ) bool parseOnly) {
{
Json::Reader reader(features); Json::Reader reader(features);
bool parsingSuccessful = reader.parse(input, root); bool parsingSuccessful = reader.parse(input, root);
if ( !parsingSuccessful ) if (!parsingSuccessful) {
{
printf("Failed to parse %s file: \n%s\n", printf("Failed to parse %s file: \n%s\n",
kind.c_str(), kind.c_str(),
reader.getFormattedErrorMessages().c_str()); reader.getFormattedErrorMessages().c_str());
return 1; return 1;
} }
if ( !parseOnly ) if (!parseOnly) {
{
FILE *factual = fopen(actual.c_str(), "wt"); FILE *factual = fopen(actual.c_str(), "wt");
if ( !factual ) if (!factual) {
{
printf("Failed to create %s actual file.\n", kind.c_str()); printf("Failed to create %s actual file.\n", kind.c_str());
return 2; return 2;
} }
@ -155,19 +146,15 @@ parseAndSaveValueTree( const std::string &input,
return 0; return 0;
} }
static int rewriteValueTree(const std::string &rewritePath,
static int
rewriteValueTree( const std::string &rewritePath,
const Json::Value &root, const Json::Value &root,
std::string &rewrite ) std::string &rewrite) {
{
// Json::FastWriter writer; // Json::FastWriter writer;
// writer.enableYAMLCompatibility(); // writer.enableYAMLCompatibility();
Json::StyledWriter writer; Json::StyledWriter writer;
rewrite = writer.write(root); rewrite = writer.write(root);
FILE *fout = fopen(rewritePath.c_str(), "wt"); FILE *fout = fopen(rewritePath.c_str(), "wt");
if ( !fout ) if (!fout) {
{
printf("Failed to create rewrite file: %s\n", rewritePath.c_str()); printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
return 2; return 2;
} }
@ -176,11 +163,8 @@ rewriteValueTree( const std::string &rewritePath,
return 0; return 0;
} }
static std::string removeSuffix(const std::string &path,
static std::string const std::string &extension) {
removeSuffix( const std::string &path,
const std::string &extension )
{
if (extension.length() >= path.length()) if (extension.length() >= path.length())
return std::string(""); return std::string("");
std::string suffix = path.substr(path.length() - extension.length()); std::string suffix = path.substr(path.length() - extension.length());
@ -189,10 +173,7 @@ removeSuffix( const std::string &path,
return path.substr(0, path.length() - extension.length()); return path.substr(0, path.length() - extension.length());
} }
static void printConfig() {
static void
printConfig()
{
// Print the configuration used to compile JsonCpp // Print the configuration used to compile JsonCpp
#if defined(JSON_NO_INT64) #if defined(JSON_NO_INT64)
printf("JSON_NO_INT64=1\n"); printf("JSON_NO_INT64=1\n");
@ -201,42 +182,34 @@ printConfig()
#endif #endif
} }
static int printUsage(const char *argv[]) {
static int
printUsage( const char *argv[] )
{
printf("Usage: %s [--strict] input-json-file", argv[0]); printf("Usage: %s [--strict] input-json-file", argv[0]);
return 3; return 3;
} }
int parseCommandLine(int argc,
int const char *argv[],
parseCommandLine( int argc, const char *argv[], Json::Features &features,
Json::Features &features, std::string &path, std::string &path,
bool &parseOnly ) bool &parseOnly) {
{
parseOnly = false; parseOnly = false;
if ( argc < 2 ) if (argc < 2) {
{
return printUsage(argv); return printUsage(argv);
} }
int index = 1; int index = 1;
if ( std::string(argv[1]) == "--json-checker" ) if (std::string(argv[1]) == "--json-checker") {
{
features = Json::Features::strictMode(); features = Json::Features::strictMode();
parseOnly = true; parseOnly = true;
++index; ++index;
} }
if ( std::string(argv[1]) == "--json-config" ) if (std::string(argv[1]) == "--json-config") {
{
printConfig(); printConfig();
return 3; return 3;
} }
if ( index == argc || index + 1 < argc ) if (index == argc || index + 1 < argc) {
{
return printUsage(argv); return printUsage(argv);
} }
@ -244,31 +217,26 @@ parseCommandLine( int argc, const char *argv[],
return 0; return 0;
} }
int main(int argc, const char *argv[]) {
int main( int argc, const char *argv[] )
{
std::string path; std::string path;
Json::Features features; Json::Features features;
bool parseOnly; bool parseOnly;
int exitCode = parseCommandLine(argc, argv, features, path, parseOnly); int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
if ( exitCode != 0 ) if (exitCode != 0) {
{
return exitCode; return exitCode;
} }
try try {
{
std::string input = readInputTestFile(path.c_str()); std::string input = readInputTestFile(path.c_str());
if ( input.empty() ) if (input.empty()) {
{
printf("Failed to read input or empty input: %s\n", path.c_str()); printf("Failed to read input or empty input: %s\n", path.c_str());
return 3; return 3;
} }
std::string basePath = removeSuffix(argv[1], ".json"); std::string basePath = removeSuffix(argv[1], ".json");
if ( !parseOnly && basePath.empty() ) if (!parseOnly && basePath.empty()) {
{ printf("Bad input path. Path does not end with '.expected':\n%s\n",
printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() ); path.c_str());
return 3; return 3;
} }
@ -277,21 +245,23 @@ int main( int argc, const char *argv[] )
std::string rewriteActualPath = basePath + ".actual-rewrite"; std::string rewriteActualPath = basePath + ".actual-rewrite";
Json::Value root; Json::Value root;
exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly ); exitCode = parseAndSaveValueTree(
if ( exitCode == 0 && !parseOnly ) input, actualPath, "input", root, features, parseOnly);
{ if (exitCode == 0 && !parseOnly) {
std::string rewrite; std::string rewrite;
exitCode = rewriteValueTree(rewritePath, root, rewrite); exitCode = rewriteValueTree(rewritePath, root, rewrite);
if ( exitCode == 0 ) if (exitCode == 0) {
{
Json::Value rewriteRoot; Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath, exitCode = parseAndSaveValueTree(rewrite,
"rewrite", rewriteRoot, features, parseOnly ); rewriteActualPath,
"rewrite",
rewriteRoot,
features,
parseOnly);
} }
} }
} }
catch ( const std::exception &e ) catch (const std::exception &e) {
{
printf("Unhandled exception:\n%s\n", e.what()); printf("Unhandled exception:\n%s\n", e.what());
exitCode = 1; exitCode = 1;
} }

View File

@ -18,33 +18,33 @@ namespace Json {
* This memory allocator allocates memory for a batch of object (specified by * This memory allocator allocates memory for a batch of object (specified by
* the page size, the number of object in each page). * the page size, the number of object in each page).
* *
* It does not allow the destruction of a single object. All the allocated objects * It does not allow the destruction of a single object. All the allocated
* can be destroyed at once. The memory can be either released or reused for future *objects
* can be destroyed at once. The memory can be either released or reused for
*future
* allocation. * allocation.
* *
* The in-place new operator must be used to construct the object using the pointer * The in-place new operator must be used to construct the object using the
*pointer
* returned by allocate. * returned by allocate.
*/ */
template<typename AllocatedType template <typename AllocatedType, const unsigned int objectPerAllocation>
,const unsigned int objectPerAllocation> class BatchAllocator {
class BatchAllocator
{
public: public:
BatchAllocator(unsigned int objectsPerPage = 255) BatchAllocator(unsigned int objectsPerPage = 255)
: freeHead_( 0 ) : freeHead_(0), objectsPerPage_(objectsPerPage) {
, objectsPerPage_( objectsPerPage ) // printf( "Size: %d => %s\n", sizeof(AllocatedType),
{ // typeid(AllocatedType).name() );
// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); assert(sizeof(AllocatedType) * objectPerAllocation >=
assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. sizeof(AllocatedType *)); // We must be able to store a slist in the
// object free space.
assert(objectsPerPage >= 16); assert(objectsPerPage >= 16);
batches_ = allocateBatch(0); // allocated a dummy page batches_ = allocateBatch(0); // allocated a dummy page
currentBatch_ = batches_; currentBatch_ = batches_;
} }
~BatchAllocator() ~BatchAllocator() {
{ for (BatchInfo *batch = batches_; batch;) {
for ( BatchInfo *batch = batches_; batch; )
{
BatchInfo *nextBatch = batch->next_; BatchInfo *nextBatch = batch->next_;
free(batch); free(batch);
batch = nextBatch; batch = nextBatch;
@ -52,17 +52,16 @@ public:
} }
/// allocate space for an array of objectPerAllocation object. /// allocate space for an array of objectPerAllocation object.
/// @warning it is the responsability of the caller to call objects constructors. /// @warning it is the responsability of the caller to call objects
AllocatedType *allocate() /// constructors.
{ AllocatedType *allocate() {
if (freeHead_) // returns node from free list. if (freeHead_) // returns node from free list.
{ {
AllocatedType *object = freeHead_; AllocatedType *object = freeHead_;
freeHead_ = *(AllocatedType **)object; freeHead_ = *(AllocatedType **)object;
return object; return object;
} }
if ( currentBatch_->used_ == currentBatch_->end_ ) if (currentBatch_->used_ == currentBatch_->end_) {
{
currentBatch_ = currentBatch_->next_; currentBatch_ = currentBatch_->next_;
while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_) while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_)
currentBatch_ = currentBatch_->next_; currentBatch_ = currentBatch_->next_;
@ -80,17 +79,16 @@ public:
} }
/// Release the object. /// Release the object.
/// @warning it is the responsability of the caller to actually destruct the object. /// @warning it is the responsability of the caller to actually destruct the
void release( AllocatedType *object ) /// object.
{ void release(AllocatedType *object) {
assert(object != 0); assert(object != 0);
*(AllocatedType **)object = freeHead_; *(AllocatedType **)object = freeHead_;
freeHead_ = object; freeHead_ = object;
} }
private: private:
struct BatchInfo struct BatchInfo {
{
BatchInfo *next_; BatchInfo *next_;
AllocatedType *used_; AllocatedType *used_;
AllocatedType *end_; AllocatedType *end_;
@ -101,10 +99,10 @@ private:
BatchAllocator(const BatchAllocator &); BatchAllocator(const BatchAllocator &);
void operator=(const BatchAllocator &); void operator=(const BatchAllocator &);
static BatchInfo *allocateBatch( unsigned int objectsPerPage ) static BatchInfo *allocateBatch(unsigned int objectsPerPage) {
{ const unsigned int mallocSize =
const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation sizeof(BatchInfo) - sizeof(AllocatedType) * objectPerAllocation +
+ sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
BatchInfo *batch = static_cast<BatchInfo *>(malloc(mallocSize)); BatchInfo *batch = static_cast<BatchInfo *>(malloc(mallocSize));
batch->next_ = 0; batch->next_ = 0;
batch->used_ = batch->buffer_; batch->used_ = batch->buffer_;
@ -119,7 +117,6 @@ private:
unsigned int objectsPerPage_; unsigned int objectsPerPage_;
}; };
} // namespace Json } // namespace Json
#endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION #endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION

View File

@ -16,7 +16,8 @@
#include <istream> #include <istream>
#if defined(_MSC_VER) && _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. #pragma warning(disable \
: 4996) // disable warning about strdup being deprecated.
#endif #endif
namespace Json { namespace Json {
@ -25,24 +26,12 @@ namespace Json {
// //////////////////////////////// // ////////////////////////////////
Features::Features() Features::Features()
: allowComments_( true ) : allowComments_(true), strictRoot_(false),
, strictRoot_( false ) allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
, allowDroppedNullPlaceholders_ ( false )
, allowNumericKeys_ ( false )
{
}
Features Features::all() { return Features(); }
Features Features Features::strictMode() {
Features::all()
{
return Features();
}
Features
Features::strictMode()
{
Features features; Features features;
features.allowComments_ = false; features.allowComments_ = false;
features.strictRoot_ = true; features.strictRoot_ = true;
@ -54,81 +43,52 @@ Features::strictMode()
// Implementation of class Reader // Implementation of class Reader
// //////////////////////////////// // ////////////////////////////////
static inline bool in(Reader::Char c,
static inline bool Reader::Char c1,
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) Reader::Char c2,
{ Reader::Char c3,
Reader::Char c4) {
return c == c1 || c == c2 || c == c3 || c == c4; return c == c1 || c == c2 || c == c3 || c == c4;
} }
static inline bool static inline bool in(Reader::Char c,
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) 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; 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')
return true; return true;
return false; return false;
} }
// Class Reader // Class Reader
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Reader::Reader() Reader::Reader()
: errors_(), : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
document_(), lastValue_(), commentsBefore_(), features_(Features::all()),
begin_(), collectComments_() {}
end_(),
current_(),
lastValueEnd_(),
lastValue_(),
commentsBefore_(),
features_( Features::all() ),
collectComments_()
{
}
Reader::Reader(const Features &features) Reader::Reader(const Features &features)
: errors_(), : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
document_(), lastValue_(), commentsBefore_(), features_(features), collectComments_() {
begin_(),
end_(),
current_(),
lastValueEnd_(),
lastValue_(),
commentsBefore_(),
features_( features ),
collectComments_()
{
} }
bool bool
Reader::parse( const std::string &document, Reader::parse(const std::string &document, Value &root, bool collectComments) {
Value &root,
bool collectComments )
{
document_ = document; document_ = document;
const char *begin = document_.c_str(); const char *begin = document_.c_str();
const char *end = begin + document_.length(); const char *end = begin + document_.length();
return parse(begin, end, root, collectComments); return parse(begin, end, root, collectComments);
} }
bool Reader::parse(std::istream &sin, Value &root, bool collectComments) {
bool
Reader::parse( std::istream& sin,
Value &root,
bool collectComments )
{
// std::istream_iterator<char> begin(sin); // std::istream_iterator<char> begin(sin);
// std::istream_iterator<char> end; // std::istream_iterator<char> end;
// Those would allow streamed input from a file, if parse() were a // Those would allow streamed input from a file, if parse() were a
@ -141,13 +101,11 @@ Reader::parse( std::istream& sin,
return parse(doc, root, collectComments); return parse(doc, root, collectComments);
} }
bool bool Reader::parse(const char *beginDoc,
Reader::parse( const char *beginDoc, const char *endDoc, const char *endDoc,
Value &root, Value &root,
bool collectComments ) bool collectComments) {
{ if (!features_.allowComments_) {
if ( !features_.allowComments_ )
{
collectComments = false; collectComments = false;
} }
@ -168,15 +126,15 @@ Reader::parse( const char *beginDoc, const char *endDoc,
skipCommentTokens(token); skipCommentTokens(token);
if (collectComments_ && !commentsBefore_.empty()) if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter); root.setComment(commentsBefore_, commentAfter);
if ( features_.strictRoot_ ) if (features_.strictRoot_) {
{ if (!root.isArray() && !root.isObject()) {
if ( !root.isArray() && !root.isObject() ) // Set error location to start of doc, ideally should be first token found
{ // in doc
// Set error location to start of doc, ideally should be first token found in doc
token.type_ = tokenError; token.type_ = tokenError;
token.start_ = beginDoc; token.start_ = beginDoc;
token.end_ = endDoc; token.end_ = endDoc;
addError( "A valid JSON document must be either an array or an object value.", addError(
"A valid JSON document must be either an array or an object value.",
token); token);
return false; return false;
} }
@ -184,24 +142,17 @@ Reader::parse( const char *beginDoc, const char *endDoc,
return successful; return successful;
} }
bool Reader::readValue() {
bool
Reader::readValue()
{
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
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 characters at the end of the comments
size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n");
if (lastNonNewline != std::string::npos) if (lastNonNewline != std::string::npos) {
{
commentsBefore_.erase(lastNonNewline + 1); commentsBefore_.erase(lastNonNewline + 1);
} } else {
else
{
commentsBefore_.clear(); commentsBefore_.clear();
} }
@ -209,9 +160,7 @@ Reader::readValue()
commentsBefore_ = ""; commentsBefore_ = "";
} }
switch (token.type_) {
switch ( token.type_ )
{
case tokenObjectBegin: case tokenObjectBegin:
successful = readObject(token); successful = readObject(token);
currentValue().setOffsetLimit(current_ - begin_); currentValue().setOffsetLimit(current_ - begin_);
@ -242,8 +191,7 @@ Reader::readValue()
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_--;
@ -259,8 +207,7 @@ Reader::readValue()
return addError("Syntax error: value, object or array expected.", token); return addError("Syntax error: value, object or array expected.", token);
} }
if ( collectComments_ ) if (collectComments_) {
{
lastValueEnd_ = current_; lastValueEnd_ = current_;
lastValue_ = &currentValue(); lastValue_ = &currentValue();
} }
@ -268,44 +215,29 @@ Reader::readValue()
return successful; return successful;
} }
void Reader::skipCommentTokens(Token &token) {
void if (features_.allowComments_) {
Reader::skipCommentTokens( Token &token ) do {
{
if ( features_.allowComments_ )
{
do
{
readToken(token); readToken(token);
} } while (token.type_ == tokenComment);
while ( token.type_ == tokenComment ); } else {
}
else
{
readToken(token); readToken(token);
} }
} }
bool Reader::expectToken(TokenType type, Token &token, const char *message) {
bool
Reader::expectToken( TokenType type, Token &token, const char *message )
{
readToken(token); readToken(token);
if (token.type_ != type) if (token.type_ != type)
return addError(message, token); return addError(message, token);
return true; return true;
} }
bool Reader::readToken(Token &token) {
bool
Reader::readToken( Token &token )
{
skipSpaces(); skipSpaces();
token.start_ = current_; token.start_ = current_;
Char c = getNextChar(); Char c = getNextChar();
bool ok = true; bool ok = true;
switch ( c ) switch (c) {
{
case '{': case '{':
token.type_ = tokenObjectBegin; token.type_ = tokenObjectBegin;
break; break;
@ -371,12 +303,8 @@ Reader::readToken( Token &token )
return true; return true;
} }
void Reader::skipSpaces() {
void while (current_ != end_) {
Reader::skipSpaces()
{
while ( current_ != end_ )
{
Char c = *current_; Char c = *current_;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++current_; ++current_;
@ -385,11 +313,7 @@ Reader::skipSpaces()
} }
} }
bool Reader::match(Location pattern, int patternLength) {
bool
Reader::match( Location pattern,
int patternLength )
{
if (end_ - current_ < patternLength) if (end_ - current_ < patternLength)
return false; return false;
int index = patternLength; int index = patternLength;
@ -400,10 +324,7 @@ Reader::match( Location pattern,
return true; return true;
} }
bool Reader::readComment() {
bool
Reader::readComment()
{
Location commentBegin = current_ - 1; Location commentBegin = current_ - 1;
Char c = getNextChar(); Char c = getNextChar();
bool successful = false; bool successful = false;
@ -414,11 +335,9 @@ Reader::readComment()
if (!successful) if (!successful)
return false; return false;
if ( collectComments_ ) if (collectComments_) {
{
CommentPlacement placement = commentBefore; CommentPlacement placement = commentBefore;
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
{
if (c != '*' || !containsNewLine(commentBegin, current_)) if (c != '*' || !containsNewLine(commentBegin, current_))
placement = commentAfterOnSameLine; placement = commentAfterOnSameLine;
} }
@ -428,32 +347,21 @@ Reader::readComment()
return true; return true;
} }
void void
Reader::addComment( Location begin, Reader::addComment(Location begin, Location end, CommentPlacement placement) {
Location end,
CommentPlacement placement )
{
assert(collectComments_); assert(collectComments_);
if ( placement == commentAfterOnSameLine ) if (placement == commentAfterOnSameLine) {
{
assert(lastValue_ != 0); assert(lastValue_ != 0);
lastValue_->setComment(std::string(begin, end), placement); lastValue_->setComment(std::string(begin, end), placement);
} } else {
else
{
if (!commentsBefore_.empty()) if (!commentsBefore_.empty())
commentsBefore_ += "\n"; commentsBefore_ += "\n";
commentsBefore_ += std::string(begin, end); commentsBefore_ += std::string(begin, end);
} }
} }
bool Reader::readCStyleComment() {
bool while (current_ != end_) {
Reader::readCStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar(); Char c = getNextChar();
if (c == '*' && *current_ == '/') if (c == '*' && *current_ == '/')
break; break;
@ -461,12 +369,8 @@ Reader::readCStyleComment()
return getNextChar() == '/'; return getNextChar() == '/';
} }
bool Reader::readCppStyleComment() {
bool while (current_ != end_) {
Reader::readCppStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar(); Char c = getNextChar();
if (c == '\r' || c == '\n') if (c == '\r' || c == '\n')
break; break;
@ -474,12 +378,8 @@ Reader::readCppStyleComment()
return true; return true;
} }
void Reader::readNumber() {
void while (current_ != end_) {
Reader::readNumber()
{
while ( current_ != end_ )
{
if (!(*current_ >= '0' && *current_ <= '9') && if (!(*current_ >= '0' && *current_ <= '9') &&
!in(*current_, '.', 'e', 'E', '+', '-')) !in(*current_, '.', 'e', 'E', '+', '-'))
break; break;
@ -487,12 +387,9 @@ Reader::readNumber()
} }
} }
bool bool Reader::readString() {
Reader::readString()
{
Char c = 0; Char c = 0;
while ( current_ != end_ ) while (current_ != end_) {
{
c = getNextChar(); c = getNextChar();
if (c == '\\') if (c == '\\')
getNextChar(); getNextChar();
@ -502,16 +399,12 @@ Reader::readString()
return c == '"'; return c == '"';
} }
bool Reader::readObject(Token &tokenStart) {
bool
Reader::readObject( Token &tokenStart )
{
Token tokenName; Token tokenName;
std::string name; std::string name;
currentValue() = Value(objectValue); currentValue() = Value(objectValue);
currentValue().setOffsetStart(tokenStart.start_ - begin_); currentValue().setOffsetStart(tokenStart.start_ - begin_);
while ( readToken( tokenName ) ) while (readToken(tokenName)) {
{
bool initialTokenOk = true; bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk) while (tokenName.type_ == tokenComment && initialTokenOk)
initialTokenOk = readToken(tokenName); initialTokenOk = readToken(tokenName);
@ -520,30 +413,22 @@ Reader::readObject( Token &tokenStart )
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
return true; return true;
name = ""; name = "";
if ( tokenName.type_ == tokenString ) if (tokenName.type_ == tokenString) {
{
if (!decodeString(tokenName, name)) if (!decodeString(tokenName, name))
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
} } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
else if ( tokenName.type_ == tokenNumber &&
features_.allowNumericKeys_ )
{
Value numberName; Value numberName;
if (!decodeNumber(tokenName, numberName)) if (!decodeNumber(tokenName, numberName))
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
name = numberName.asString(); name = numberName.asString();
} } else {
else
{
break; break;
} }
Token colon; Token colon;
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
{ return addErrorAndRecover(
return addErrorAndRecover( "Missing ':' after object member name", "Missing ':' after object member name", colon, tokenObjectEnd);
colon,
tokenObjectEnd );
} }
Value &value = currentValue()[name]; Value &value = currentValue()[name];
nodes_.push(&value); nodes_.push(&value);
@ -553,31 +438,23 @@ Reader::readObject( Token &tokenStart )
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
Token comma; Token comma;
if ( !readToken( comma ) if (!readToken(comma) ||
|| ( comma.type_ != tokenObjectEnd && (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) {
comma.type_ != tokenComment ) ) return addErrorAndRecover(
{ "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
return addErrorAndRecover( "Missing ',' or '}' in object declaration",
comma,
tokenObjectEnd );
} }
bool finalizeTokenOk = true; bool finalizeTokenOk = true;
while ( comma.type_ == tokenComment && while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk )
finalizeTokenOk = readToken(comma); finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd) if (comma.type_ == tokenObjectEnd)
return true; return true;
} }
return addErrorAndRecover( "Missing '}' or object member name", return addErrorAndRecover(
tokenName, "Missing '}' or object member name", tokenName, tokenObjectEnd);
tokenObjectEnd );
} }
bool Reader::readArray(Token &tokenStart) {
bool
Reader::readArray( Token &tokenStart )
{
currentValue() = Value(arrayValue); currentValue() = Value(arrayValue);
currentValue().setOffsetStart(tokenStart.start_ - begin_); currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces(); skipSpaces();
@ -588,8 +465,7 @@ Reader::readArray( Token &tokenStart )
return true; return true;
} }
int index = 0; int index = 0;
for (;;) for (;;) {
{
Value &value = currentValue()[index++]; Value &value = currentValue()[index++];
nodes_.push(&value); nodes_.push(&value);
bool ok = readValue(); bool ok = readValue();
@ -600,17 +476,14 @@ Reader::readArray( Token &tokenStart )
Token token; Token token;
// Accept Comment after last item in the array. // Accept Comment after last item in the array.
ok = readToken(token); ok = readToken(token);
while ( token.type_ == tokenComment && ok ) while (token.type_ == tokenComment && ok) {
{
ok = readToken(token); ok = readToken(token);
} }
bool badTokenType = ( token.type_ != tokenArraySeparator && bool badTokenType =
token.type_ != tokenArrayEnd ); (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
if ( !ok || badTokenType ) if (!ok || badTokenType) {
{ return addErrorAndRecover(
return addErrorAndRecover( "Missing ',' or ']' in array declaration", "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
token,
tokenArrayEnd );
} }
if (token.type_ == tokenArrayEnd) if (token.type_ == tokenArrayEnd)
break; break;
@ -618,10 +491,7 @@ Reader::readArray( Token &tokenStart )
return true; return true;
} }
bool Reader::decodeNumber(Token &token) {
bool
Reader::decodeNumber( Token &token )
{
Value decoded; Value decoded;
if (!decodeNumber(token, decoded)) if (!decodeNumber(token, decoded))
return false; return false;
@ -631,16 +501,11 @@ Reader::decodeNumber( Token &token )
return true; return true;
} }
bool Reader::decodeNumber(Token &token, Value &decoded) {
bool
Reader::decodeNumber( Token &token, Value &decoded )
{
bool isDouble = false; bool isDouble = false;
for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) for (Location inspect = token.start_; inspect != token.end_; ++inspect) {
{ isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') ||
isDouble = isDouble (*inspect == '-' && inspect != token.start_);
|| in( *inspect, '.', 'e', 'E', '+' )
|| ( *inspect == '-' && inspect != token.start_ );
} }
if (isDouble) if (isDouble)
return decodeDouble(token, decoded); return decodeDouble(token, decoded);
@ -651,26 +516,25 @@ Reader::decodeNumber( Token &token, Value &decoded )
bool isNegative = *current == '-'; bool isNegative = *current == '-';
if (isNegative) if (isNegative)
++current; ++current;
Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) Value::LargestUInt maxIntegerValue =
isNegative ? Value::LargestUInt(-Value::minLargestInt)
: Value::maxLargestUInt; : Value::maxLargestUInt;
Value::LargestUInt threshold = maxIntegerValue / 10; Value::LargestUInt threshold = maxIntegerValue / 10;
Value::LargestUInt value = 0; Value::LargestUInt value = 0;
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_ ) + "' is not a number.", token ); return addError("'" + std::string(token.start_, token.end_) +
"' 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
// a) we've only just touched the limit, b) this is the last digit, and // 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. // c) it's small enough to fit in that rounding delta, we're okay.
// Otherwise treat this number as a double to avoid overflow. // Otherwise treat this number as a double to avoid overflow.
if (value > threshold || if (value > threshold || current != token.end_ ||
current != token.end_ || digit > maxIntegerValue % 10) {
digit > maxIntegerValue % 10)
{
return decodeDouble(token, decoded); return decodeDouble(token, decoded);
} }
} }
@ -685,10 +549,7 @@ Reader::decodeNumber( Token &token, Value &decoded )
return true; return true;
} }
bool Reader::decodeDouble(Token &token) {
bool
Reader::decodeDouble( Token &token )
{
Value decoded; Value decoded;
if (!decodeDouble(token, decoded)) if (!decodeDouble(token, decoded))
return false; return false;
@ -698,10 +559,7 @@ Reader::decodeDouble( Token &token )
return true; return true;
} }
bool Reader::decodeDouble(Token &token, Value &decoded) {
bool
Reader::decodeDouble( Token &token, Value &decoded )
{
double value = 0; double value = 0;
const int bufferSize = 32; const int bufferSize = 32;
int count; int count;
@ -719,29 +577,25 @@ Reader::decodeDouble( Token &token, Value &decoded )
// http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
char format[] = "%lf"; char format[] = "%lf";
if ( length <= bufferSize ) if (length <= bufferSize) {
{
Char buffer[bufferSize + 1]; Char buffer[bufferSize + 1];
memcpy(buffer, token.start_, length); memcpy(buffer, token.start_, length);
buffer[length] = 0; buffer[length] = 0;
count = sscanf(buffer, format, &value); count = sscanf(buffer, format, &value);
} } else {
else
{
std::string buffer(token.start_, token.end_); std::string buffer(token.start_, token.end_);
count = sscanf(buffer.c_str(), format, &value); count = sscanf(buffer.c_str(), format, &value);
} }
if (count != 1) if (count != 1)
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); return addError("'" + std::string(token.start_, token.end_) +
"' is not a number.",
token);
decoded = value; decoded = value;
return true; return true;
} }
bool Reader::decodeString(Token &token) {
bool
Reader::decodeString( Token &token )
{
std::string decoded; std::string decoded;
if (!decodeString(token, decoded)) if (!decodeString(token, decoded))
return false; return false;
@ -751,94 +605,99 @@ Reader::decodeString( Token &token )
return true; return true;
} }
bool Reader::decodeString(Token &token, std::string &decoded) {
bool
Reader::decodeString( Token &token, std::string &decoded )
{
decoded.reserve(token.end_ - token.start_ - 2); decoded.reserve(token.end_ - token.start_ - 2);
Location current = token.start_ + 1; // skip '"' Location current = token.start_ + 1; // skip '"'
Location end = token.end_ - 1; // do not include '"' Location end = token.end_ - 1; // do not include '"'
while ( current != end ) while (current != end) {
{
Char c = *current++; Char c = *current++;
if (c == '"') if (c == '"')
break; break;
else if ( c == '\\' ) else if (c == '\\') {
{
if (current == end) if (current == end)
return addError("Empty escape sequence in string", token, current); return addError("Empty escape sequence in string", token, current);
Char escape = *current++; Char escape = *current++;
switch ( escape ) switch (escape) {
{ case '"':
case '"': decoded += '"'; break; decoded += '"';
case '/': decoded += '/'; break; break;
case '\\': decoded += '\\'; break; case '/':
case 'b': decoded += '\b'; break; decoded += '/';
case 'f': decoded += '\f'; break; break;
case 'n': decoded += '\n'; break; case '\\':
case 'r': decoded += '\r'; break; decoded += '\\';
case 't': decoded += '\t'; break; break;
case 'u': case 'b':
{ decoded += '\b';
break;
case 'f':
decoded += '\f';
break;
case 'n':
decoded += '\n';
break;
case 'r':
decoded += '\r';
break;
case 't':
decoded += '\t';
break;
case 'u': {
unsigned int unicode; unsigned int unicode;
if (!decodeUnicodeCodePoint(token, current, end, unicode)) if (!decodeUnicodeCodePoint(token, current, end, unicode))
return false; return false;
decoded += codePointToUTF8(unicode); decoded += codePointToUTF8(unicode);
} } break;
break;
default: default:
return addError("Bad escape sequence in string", token, current); return addError("Bad escape sequence in string", token, current);
} }
} } else {
else
{
decoded += c; decoded += c;
} }
} }
return true; return true;
} }
bool bool Reader::decodeUnicodeCodePoint(Token &token,
Reader::decodeUnicodeCodePoint( Token &token,
Location &current, Location &current,
Location end, Location end,
unsigned int &unicode ) unsigned int &unicode) {
{
if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false; return false;
if (unicode >= 0xD800 && unicode <= 0xDBFF) if (unicode >= 0xD800 && unicode <= 0xDBFF) {
{
// surrogate pairs // surrogate pairs
if (end - current < 6) if (end - current < 6)
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); return addError(
"additional six characters expected to parse unicode surrogate pair.",
token,
current);
unsigned int surrogatePair; unsigned int surrogatePair;
if (*(current++) == '\\' && *(current++)== 'u') if (*(current++) == '\\' && *(current++) == 'u') {
{ if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
{
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
} } else
else
return false; return false;
} } else
else return addError("expecting another \\u token to begin the second half of "
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); "a unicode surrogate pair",
token,
current);
} }
return true; return true;
} }
bool bool Reader::decodeUnicodeEscapeSequence(Token &token,
Reader::decodeUnicodeEscapeSequence( Token &token,
Location &current, Location &current,
Location end, Location end,
unsigned int &unicode ) unsigned int &unicode) {
{
if (end - current < 4) if (end - current < 4)
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); return addError(
"Bad unicode escape sequence in string: four digits expected.",
token,
current);
unicode = 0; unicode = 0;
for ( int index =0; index < 4; ++index ) for (int index = 0; index < 4; ++index) {
{
Char c = *current++; Char c = *current++;
unicode *= 16; unicode *= 16;
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
@ -848,17 +707,16 @@ Reader::decodeUnicodeEscapeSequence( Token &token,
else if (c >= 'A' && c <= 'F') else if (c >= 'A' && c <= 'F')
unicode += c - 'A' + 10; unicode += c - 'A' + 10;
else else
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); return addError(
"Bad unicode escape sequence in string: hexadecimal digit expected.",
token,
current);
} }
return true; return true;
} }
bool bool
Reader::addError( const std::string &message, Reader::addError(const std::string &message, Token &token, Location extra) {
Token &token,
Location extra )
{
ErrorInfo info; ErrorInfo info;
info.token_ = token; info.token_ = token;
info.message_ = message; info.message_ = message;
@ -867,14 +725,10 @@ Reader::addError( const std::string &message,
return false; return false;
} }
bool Reader::recoverFromError(TokenType skipUntilToken) {
bool
Reader::recoverFromError( TokenType skipUntilToken )
{
int errorCount = int(errors_.size()); int errorCount = int(errors_.size());
Token skip; Token skip;
for (;;) for (;;) {
{
if (!readToken(skip)) if (!readToken(skip))
errors_.resize(errorCount); // discard errors caused by recovery errors_.resize(errorCount); // discard errors caused by recovery
if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
@ -884,53 +738,35 @@ Reader::recoverFromError( TokenType skipUntilToken )
return false; return false;
} }
bool Reader::addErrorAndRecover(const std::string &message,
bool
Reader::addErrorAndRecover( const std::string &message,
Token &token, Token &token,
TokenType skipUntilToken ) TokenType skipUntilToken) {
{
addError(message, token); addError(message, token);
return recoverFromError(skipUntilToken); return recoverFromError(skipUntilToken);
} }
Value &Reader::currentValue() { return *(nodes_.top()); }
Value & Reader::Char Reader::getNextChar() {
Reader::currentValue()
{
return *(nodes_.top());
}
Reader::Char
Reader::getNextChar()
{
if (current_ == end_) if (current_ == end_)
return 0; return 0;
return *current_++; return *current_++;
} }
void Reader::getLocationLineAndColumn(Location location,
void
Reader::getLocationLineAndColumn( Location location,
int &line, int &line,
int &column ) const int &column) const {
{
Location current = begin_; Location current = begin_;
Location lastLineStart = current; Location lastLineStart = current;
line = 0; line = 0;
while ( current < location && current != end_ ) while (current < location && current != end_) {
{
Char c = *current++; Char c = *current++;
if ( c == '\r' ) if (c == '\r') {
{
if (*current == '\n') if (*current == '\n')
++current; ++current;
lastLineStart = current; lastLineStart = current;
++line; ++line;
} } else if (c == '\n') {
else if ( c == '\n' )
{
lastLineStart = current; lastLineStart = current;
++line; ++line;
} }
@ -940,10 +776,7 @@ Reader::getLocationLineAndColumn( Location location,
++line; ++line;
} }
std::string Reader::getLocationLineAndColumn(Location location) const {
std::string
Reader::getLocationLineAndColumn( Location location ) const
{
int line, column; int line, column;
getLocationLineAndColumn(location, line, column); getLocationLineAndColumn(location, line, column);
char buffer[18 + 16 + 16 + 1]; char buffer[18 + 16 + 16 + 1];
@ -955,41 +788,32 @@ Reader::getLocationLineAndColumn( Location location ) const
return buffer; return buffer;
} }
// Deprecated. Preserved for backward compatibility // Deprecated. Preserved for backward compatibility
std::string std::string Reader::getFormatedErrorMessages() const {
Reader::getFormatedErrorMessages() const
{
return getFormattedErrorMessages(); return getFormattedErrorMessages();
} }
std::string Reader::getFormattedErrorMessages() const {
std::string
Reader::getFormattedErrorMessages() const
{
std::string formattedMessage; std::string formattedMessage;
for (Errors::const_iterator itError = errors_.begin(); for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end(); itError != errors_.end();
++itError ) ++itError) {
{
const ErrorInfo &error = *itError; const ErrorInfo &error = *itError;
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; formattedMessage +=
"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
formattedMessage += " " + error.message_ + "\n"; formattedMessage += " " + error.message_ + "\n";
if (error.extra_) if (error.extra_)
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; formattedMessage +=
"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
} }
return formattedMessage; return formattedMessage;
} }
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
std::vector<Reader::StructuredError>
Reader::getStructuredErrors() const
{
std::vector<Reader::StructuredError> allErrors; std::vector<Reader::StructuredError> allErrors;
for (Errors::const_iterator itError = errors_.begin(); for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end(); itError != errors_.end();
++itError ) ++itError) {
{
const ErrorInfo &error = *itError; const ErrorInfo &error = *itError;
Reader::StructuredError structured; Reader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_; structured.offset_start = error.token_.start_ - begin_;
@ -1000,14 +824,11 @@ Reader::getStructuredErrors() const
return allErrors; return allErrors;
} }
std::istream &operator>>(std::istream &sin, Value &root) {
std::istream& operator>>( std::istream &sin, Value &root )
{
Json::Reader reader; Json::Reader reader;
bool ok = reader.parse(sin, root, true); bool ok = reader.parse(sin, root, true);
if (!ok) { if (!ok) {
fprintf( fprintf(stderr,
stderr,
"Error from reader: %s", "Error from reader: %s",
reader.getFormattedErrorMessages().c_str()); reader.getFormattedErrorMessages().c_str());
@ -1016,6 +837,5 @@ std::istream& operator>>( std::istream &sin, Value &root )
return sin; return sin;
} }
} // namespace Json } // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0 // vim: et ts=3 sts=3 sw=3 tw=0

View File

@ -15,33 +15,24 @@
namespace Json { namespace Json {
/// Converts a unicode code-point to UTF-8. /// Converts a unicode code-point to UTF-8.
static inline std::string static inline std::string codePointToUTF8(unsigned int cp) {
codePointToUTF8(unsigned int cp)
{
std::string result; std::string result;
// based on description from http://en.wikipedia.org/wiki/UTF-8 // based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) if (cp <= 0x7f) {
{
result.resize(1); result.resize(1);
result[0] = static_cast<char>(cp); result[0] = static_cast<char>(cp);
} } else if (cp <= 0x7FF) {
else if (cp <= 0x7FF)
{
result.resize(2); result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp)); result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} } else if (cp <= 0xFFFF) {
else if (cp <= 0xFFFF)
{
result.resize(3); result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp)); result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6))); result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12))); result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
} } else if (cp <= 0x10FFFF) {
else if (cp <= 0x10FFFF)
{
result.resize(4); result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp)); result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
@ -52,40 +43,29 @@ codePointToUTF8(unsigned int cp)
return result; return result;
} }
/// Returns true if ch is a control character (in range [0,32[). /// Returns true if ch is a control character (in range [0,32[).
static inline bool static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
isControlCharacter(char ch)
{
return ch > 0 && ch <= 0x1F;
}
enum { enum {
/// Constant that specify the size of the buffer that must be passed to uintToString. /// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
}; };
// Defines a char buffer for use with uintToString(). // Defines a char buffer for use with uintToString().
typedef char UIntToStringBuffer[uintToStringBufferSize]; typedef char UIntToStringBuffer[uintToStringBufferSize];
/** Converts an unsigned integer to string. /** Converts an unsigned integer to string.
* @param value Unsigned interger to convert to string * @param value Unsigned interger to convert to string
* @param current Input/Output string buffer. * @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free. * Must have at least uintToStringBufferSize chars free.
*/ */
static inline void static inline void uintToString(LargestUInt value, char *&current) {
uintToString( LargestUInt value,
char *&current )
{
*--current = 0; *--current = 0;
do do {
{
*--current = char(value % 10) + '0'; *--current = char(value % 10) + '0';
value /= 10; value /= 10;
} } while (value != 0);
while ( value != 0 );
} }
} // namespace Json { } // namespace Json {

File diff suppressed because it is too large Load Diff

View File

@ -15,24 +15,21 @@
#include <iomanip> #include <iomanip>
#if defined(_MSC_VER) && _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. #pragma warning(disable \
: 4996) // disable warning about strdup being deprecated.
#endif #endif
namespace Json { namespace Json {
static bool containsControlCharacter( const char* str ) static bool containsControlCharacter(const char *str) {
{ while (*str) {
while ( *str )
{
if (isControlCharacter(*(str++))) if (isControlCharacter(*(str++)))
return true; return true;
} }
return false; return false;
} }
std::string valueToString(LargestInt value) {
std::string valueToString( LargestInt value )
{
UIntToStringBuffer buffer; UIntToStringBuffer buffer;
char *current = buffer + sizeof(buffer); char *current = buffer + sizeof(buffer);
bool isNegative = value < 0; bool isNegative = value < 0;
@ -45,9 +42,7 @@ std::string valueToString( LargestInt value )
return current; return current;
} }
std::string valueToString(LargestUInt value) {
std::string valueToString( LargestUInt value )
{
UIntToStringBuffer buffer; UIntToStringBuffer buffer;
char *current = buffer + sizeof(buffer); char *current = buffer + sizeof(buffer);
uintToString(value, current); uintToString(value, current);
@ -57,22 +52,17 @@ std::string valueToString( LargestUInt value )
#if defined(JSON_HAS_INT64) #if defined(JSON_HAS_INT64)
std::string valueToString( Int value ) std::string valueToString(Int value) {
{
return valueToString(LargestInt(value)); return valueToString(LargestInt(value));
} }
std::string valueToString(UInt value) {
std::string valueToString( UInt value )
{
return valueToString(LargestUInt(value)); return valueToString(LargestUInt(value));
} }
#endif // # if defined(JSON_HAS_INT64) #endif // # if defined(JSON_HAS_INT64)
std::string valueToString(double value) {
std::string valueToString( double value )
{
// Allocate a buffer that is more than large enough to store the 16 digits of // Allocate a buffer that is more than large enough to store the 16 digits of
// precision requested below. // precision requested below.
char buffer[32]; char buffer[32];
@ -80,7 +70,9 @@ std::string valueToString( double value )
// Print into the buffer. We need not request the alternative representation // Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distingish the // that always has a decimal point because JSON doesn't distingish the
// concepts of reals and integers. // concepts of reals and integers.
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. #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); sprintf_s(buffer, sizeof(buffer), "%.16g", value);
#else #else
snprintf(buffer, sizeof(buffer), "%.16g", value); snprintf(buffer, sizeof(buffer), "%.16g", value);
@ -89,30 +81,25 @@ std::string valueToString( double value )
return buffer; return buffer;
} }
std::string valueToString(bool value) { return value ? "true" : "false"; }
std::string valueToString( bool value ) std::string valueToQuotedString(const char *value) {
{
return value ? "true" : "false";
}
std::string valueToQuotedString( const char *value )
{
if (value == NULL) if (value == NULL)
return ""; return "";
// Not sure how to handle unicode... // Not sure how to handle unicode...
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
!containsControlCharacter(value))
return std::string("\"") + value + "\""; return std::string("\"") + value + "\"";
// We have to walk value and escape any special characters. // We have to walk value and escape any special characters.
// Appending to std::string is not efficient, but this should be rare. // Appending to std::string is not efficient, but this should be rare.
// (Note: forward slashes are *not* rare, but I am not escaping them.) // (Note: forward slashes are *not* rare, but I am not escaping them.)
std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL std::string::size_type maxsize =
strlen(value) * 2 + 3; // allescaped+quotes+NULL
std::string result; std::string result;
result.reserve(maxsize); // to avoid lots of mallocs result.reserve(maxsize); // to avoid lots of mallocs
result += "\""; result += "\"";
for (const char* c=value; *c != 0; ++c) for (const char *c = value; *c != 0; ++c) {
{ switch (*c) {
switch(*c)
{
case '\"': case '\"':
result += "\\\""; result += "\\\"";
break; break;
@ -143,14 +130,12 @@ std::string valueToQuotedString( const char *value )
// Should add a flag to allow this compatibility mode and prevent this // Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring. // sequence from occurring.
default: default:
if ( isControlCharacter( *c ) ) if (isControlCharacter(*c)) {
{
std::ostringstream oss; std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c); oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
result += oss.str(); result += oss.str();
} } else {
else
{
result += *c; result += *c;
} }
break; break;
@ -162,52 +147,30 @@ std::string valueToQuotedString( const char *value )
// Class Writer // Class Writer
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Writer::~Writer() Writer::~Writer() {}
{
}
// Class FastWriter // Class FastWriter
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
FastWriter::FastWriter() FastWriter::FastWriter()
: yamlCompatiblityEnabled_( false ), : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false) {}
dropNullPlaceholders_( false )
{
}
void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
void void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
FastWriter::enableYAMLCompatibility()
{
yamlCompatiblityEnabled_ = true;
}
std::string FastWriter::write(const Value &root) {
void
FastWriter::dropNullPlaceholders()
{
dropNullPlaceholders_ = true;
}
std::string
FastWriter::write( const Value &root )
{
document_ = ""; document_ = "";
writeValue(root); writeValue(root);
document_ += "\n"; document_ += "\n";
return document_; return document_;
} }
void FastWriter::writeValue(const Value &value) {
void switch (value.type()) {
FastWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue: case nullValue:
if (!dropNullPlaceholders_) document_ += "null"; if (!dropNullPlaceholders_)
document_ += "null";
break; break;
case intValue: case intValue:
document_ += valueToString(value.asLargestInt()); document_ += valueToString(value.asLargestInt());
@ -224,56 +187,40 @@ FastWriter::writeValue( const Value &value )
case booleanValue: case booleanValue:
document_ += valueToString(value.asBool()); document_ += valueToString(value.asBool());
break; break;
case arrayValue: case arrayValue: {
{
document_ += "["; document_ += "[";
int size = value.size(); int size = value.size();
for ( int index =0; index < size; ++index ) for (int index = 0; index < size; ++index) {
{
if (index > 0) if (index > 0)
document_ += ","; document_ += ",";
writeValue(value[index]); writeValue(value[index]);
} }
document_ += "]"; document_ += "]";
} } break;
break; case objectValue: {
case objectValue:
{
Value::Members members(value.getMemberNames()); Value::Members members(value.getMemberNames());
document_ += "{"; document_ += "{";
for ( Value::Members::iterator it = members.begin(); for (Value::Members::iterator it = members.begin(); it != members.end();
it != members.end(); ++it) {
++it )
{
const std::string &name = *it; const std::string &name = *it;
if (it != members.begin()) if (it != members.begin())
document_ += ","; document_ += ",";
document_ += valueToQuotedString(name.c_str()); document_ += valueToQuotedString(name.c_str());
document_ += yamlCompatiblityEnabled_ ? ": " document_ += yamlCompatiblityEnabled_ ? ": " : ":";
: ":";
writeValue(value[name]); writeValue(value[name]);
} }
document_ += "}"; document_ += "}";
} } break;
break;
} }
} }
// Class StyledWriter // Class StyledWriter
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
StyledWriter::StyledWriter() StyledWriter::StyledWriter()
: rightMargin_( 74 ) : rightMargin_(74), indentSize_(3), addChildValues_() {}
, indentSize_( 3 )
, addChildValues_()
{
}
std::string StyledWriter::write(const Value &root) {
std::string
StyledWriter::write( const Value &root )
{
document_ = ""; document_ = "";
addChildValues_ = false; addChildValues_ = false;
indentString_ = ""; indentString_ = "";
@ -284,12 +231,8 @@ StyledWriter::write( const Value &root )
return document_; return document_;
} }
void StyledWriter::writeValue(const Value &value) {
void switch (value.type()) {
StyledWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue: case nullValue:
pushValue("null"); pushValue("null");
break; break;
@ -311,26 +254,22 @@ StyledWriter::writeValue( const Value &value )
case arrayValue: case arrayValue:
writeArrayValue(value); writeArrayValue(value);
break; break;
case objectValue: case objectValue: {
{
Value::Members members(value.getMemberNames()); Value::Members members(value.getMemberNames());
if (members.empty()) if (members.empty())
pushValue("{}"); pushValue("{}");
else else {
{
writeWithIndent("{"); writeWithIndent("{");
indent(); indent();
Value::Members::iterator it = members.begin(); Value::Members::iterator it = members.begin();
for (;;) for (;;) {
{
const std::string &name = *it; const std::string &name = *it;
const Value &childValue = value[name]; const Value &childValue = value[name];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str())); writeWithIndent(valueToQuotedString(name.c_str()));
document_ += " : "; document_ += " : ";
writeValue(childValue); writeValue(childValue);
if ( ++it == members.end() ) if (++it == members.end()) {
{
writeCommentAfterValueOnSameLine(childValue); writeCommentAfterValueOnSameLine(childValue);
break; break;
} }
@ -340,40 +279,31 @@ StyledWriter::writeValue( const Value &value )
unindent(); unindent();
writeWithIndent("}"); writeWithIndent("}");
} }
} } break;
break;
} }
} }
void StyledWriter::writeArrayValue(const Value &value) {
void
StyledWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size(); unsigned size = value.size();
if (size == 0) if (size == 0)
pushValue("[]"); pushValue("[]");
else else {
{
bool isArrayMultiLine = isMultineArray(value); bool isArrayMultiLine = isMultineArray(value);
if ( isArrayMultiLine ) if (isArrayMultiLine) {
{
writeWithIndent("["); writeWithIndent("[");
indent(); indent();
bool hasChildValue = !childValues_.empty(); bool hasChildValue = !childValues_.empty();
unsigned index = 0; unsigned index = 0;
for (;;) for (;;) {
{
const Value &childValue = value[index]; const Value &childValue = value[index];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
if (hasChildValue) if (hasChildValue)
writeWithIndent(childValues_[index]); writeWithIndent(childValues_[index]);
else else {
{
writeIndent(); writeIndent();
writeValue(childValue); writeValue(childValue);
} }
if ( ++index == size ) if (++index == size) {
{
writeCommentAfterValueOnSameLine(childValue); writeCommentAfterValueOnSameLine(childValue);
break; break;
} }
@ -382,13 +312,11 @@ StyledWriter::writeArrayValue( const Value &value )
} }
unindent(); unindent();
writeWithIndent("]"); writeWithIndent("]");
} } else // output on a single line
else // output on a single line
{ {
assert(childValues_.size() == size); assert(childValues_.size() == size);
document_ += "[ "; document_ += "[ ";
for ( unsigned index =0; index < size; ++index ) for (unsigned index = 0; index < size; ++index) {
{
if (index > 0) if (index > 0)
document_ += ", "; document_ += ", ";
document_ += childValues_[index]; document_ += childValues_[index];
@ -398,18 +326,14 @@ StyledWriter::writeArrayValue( const Value &value )
} }
} }
bool StyledWriter::isMultineArray(const Value &value) {
bool
StyledWriter::isMultineArray( const Value &value )
{
int size = value.size(); int size = value.size();
bool isMultiLine = size * 3 >= rightMargin_; bool isMultiLine = size * 3 >= rightMargin_;
childValues_.clear(); childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index ) for (int index = 0; index < size && !isMultiLine; ++index) {
{
const Value &childValue = value[index]; const Value &childValue = value[index];
isMultiLine = isMultiLine || isMultiLine =
( (childValue.isArray() || childValue.isObject()) && isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0); childValue.size() > 0);
} }
if (!isMultiLine) // check if line length > max line length if (!isMultiLine) // check if line length > max line length
@ -417,8 +341,7 @@ StyledWriter::isMultineArray( const Value &value )
childValues_.reserve(size); childValues_.reserve(size);
addChildValues_ = true; addChildValues_ = true;
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index ) for (int index = 0; index < size && !isMultiLine; ++index) {
{
writeValue(value[index]); writeValue(value[index]);
lineLength += int(childValues_[index].length()); lineLength += int(childValues_[index].length());
isMultiLine = isMultiLine && hasCommentForValue(value[index]); isMultiLine = isMultiLine && hasCommentForValue(value[index]);
@ -429,22 +352,15 @@ StyledWriter::isMultineArray( const Value &value )
return isMultiLine; return isMultiLine;
} }
void StyledWriter::pushValue(const std::string &value) {
void
StyledWriter::pushValue( const std::string &value )
{
if (addChildValues_) if (addChildValues_)
childValues_.push_back(value); childValues_.push_back(value);
else else
document_ += value; document_ += value;
} }
void StyledWriter::writeIndent() {
void if (!document_.empty()) {
StyledWriter::writeIndent()
{
if ( !document_.empty() )
{
char last = document_[document_.length() - 1]; char last = document_[document_.length() - 1];
if (last == ' ') // already indented if (last == ' ') // already indented
return; return;
@ -454,33 +370,19 @@ StyledWriter::writeIndent()
document_ += indentString_; document_ += indentString_;
} }
void StyledWriter::writeWithIndent(const std::string &value) {
void
StyledWriter::writeWithIndent( const std::string &value )
{
writeIndent(); writeIndent();
document_ += value; document_ += value;
} }
void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
void void StyledWriter::unindent() {
StyledWriter::indent()
{
indentString_ += std::string( indentSize_, ' ' );
}
void
StyledWriter::unindent()
{
assert(int(indentString_.size()) >= indentSize_); assert(int(indentString_.size()) >= indentSize_);
indentString_.resize(indentString_.size() - indentSize_); indentString_.resize(indentString_.size() - indentSize_);
} }
void StyledWriter::writeCommentBeforeValue(const Value &root) {
void
StyledWriter::writeCommentBeforeValue( const Value &root )
{
if (!root.hasComment(commentBefore)) if (!root.hasComment(commentBefore))
return; return;
@ -488,8 +390,7 @@ StyledWriter::writeCommentBeforeValue( const Value &root )
writeIndent(); writeIndent();
std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
std::string::const_iterator iter = normalizedComment.begin(); std::string::const_iterator iter = normalizedComment.begin();
while ( iter != normalizedComment.end() ) while (iter != normalizedComment.end()) {
{
document_ += *iter; document_ += *iter;
if (*iter == '\n' && *(iter + 1) == '/') if (*iter == '\n' && *(iter + 1) == '/')
writeIndent(); writeIndent();
@ -500,70 +401,50 @@ StyledWriter::writeCommentBeforeValue( const Value &root )
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_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
if ( root.hasComment( commentAfter ) ) if (root.hasComment(commentAfter)) {
{
document_ += "\n"; document_ += "\n";
document_ += normalizeEOL(root.getComment(commentAfter)); document_ += normalizeEOL(root.getComment(commentAfter));
document_ += "\n"; document_ += "\n";
} }
} }
bool StyledWriter::hasCommentForValue(const Value &value) {
bool return value.hasComment(commentBefore) ||
StyledWriter::hasCommentForValue( const Value &value ) value.hasComment(commentAfterOnSameLine) ||
{ value.hasComment(commentAfter);
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
} }
std::string StyledWriter::normalizeEOL(const std::string &text) {
std::string
StyledWriter::normalizeEOL( const std::string &text )
{
std::string normalized; std::string normalized;
normalized.reserve(text.length()); normalized.reserve(text.length());
const char *begin = text.c_str(); const char *begin = text.c_str();
const char *end = begin + text.length(); const char *end = begin + text.length();
const char *current = begin; const char *current = begin;
while ( current != end ) while (current != end) {
{
char c = *current++; char c = *current++;
if (c == '\r') // mac or dos EOL if (c == '\r') // mac or dos EOL
{ {
if (*current == '\n') // convert dos EOL if (*current == '\n') // convert dos EOL
++current; ++current;
normalized += '\n'; normalized += '\n';
} } else // handle unix EOL & other char
else // handle unix EOL & other char
normalized += c; normalized += c;
} }
return normalized; return normalized;
} }
// Class StyledStreamWriter // Class StyledStreamWriter
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
StyledStreamWriter::StyledStreamWriter(std::string indentation) StyledStreamWriter::StyledStreamWriter(std::string indentation)
: document_(NULL) : document_(NULL), rightMargin_(74), indentation_(indentation),
, rightMargin_( 74 ) addChildValues_() {}
, indentation_( indentation )
, addChildValues_()
{
}
void StyledStreamWriter::write(std::ostream &out, const Value &root) {
void
StyledStreamWriter::write( std::ostream &out, const Value &root )
{
document_ = &out; document_ = &out;
addChildValues_ = false; addChildValues_ = false;
indentString_ = ""; indentString_ = "";
@ -574,12 +455,8 @@ StyledStreamWriter::write( std::ostream &out, const Value &root )
document_ = NULL; // Forget the stream, for safety. document_ = NULL; // Forget the stream, for safety.
} }
void StyledStreamWriter::writeValue(const Value &value) {
void switch (value.type()) {
StyledStreamWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue: case nullValue:
pushValue("null"); pushValue("null");
break; break;
@ -601,26 +478,22 @@ StyledStreamWriter::writeValue( const Value &value )
case arrayValue: case arrayValue:
writeArrayValue(value); writeArrayValue(value);
break; break;
case objectValue: case objectValue: {
{
Value::Members members(value.getMemberNames()); Value::Members members(value.getMemberNames());
if (members.empty()) if (members.empty())
pushValue("{}"); pushValue("{}");
else else {
{
writeWithIndent("{"); writeWithIndent("{");
indent(); indent();
Value::Members::iterator it = members.begin(); Value::Members::iterator it = members.begin();
for (;;) for (;;) {
{
const std::string &name = *it; const std::string &name = *it;
const Value &childValue = value[name]; const Value &childValue = value[name];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str())); writeWithIndent(valueToQuotedString(name.c_str()));
*document_ << " : "; *document_ << " : ";
writeValue(childValue); writeValue(childValue);
if ( ++it == members.end() ) if (++it == members.end()) {
{
writeCommentAfterValueOnSameLine(childValue); writeCommentAfterValueOnSameLine(childValue);
break; break;
} }
@ -630,40 +503,31 @@ StyledStreamWriter::writeValue( const Value &value )
unindent(); unindent();
writeWithIndent("}"); writeWithIndent("}");
} }
} } break;
break;
} }
} }
void StyledStreamWriter::writeArrayValue(const Value &value) {
void
StyledStreamWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size(); unsigned size = value.size();
if (size == 0) if (size == 0)
pushValue("[]"); pushValue("[]");
else else {
{
bool isArrayMultiLine = isMultineArray(value); bool isArrayMultiLine = isMultineArray(value);
if ( isArrayMultiLine ) if (isArrayMultiLine) {
{
writeWithIndent("["); writeWithIndent("[");
indent(); indent();
bool hasChildValue = !childValues_.empty(); bool hasChildValue = !childValues_.empty();
unsigned index = 0; unsigned index = 0;
for (;;) for (;;) {
{
const Value &childValue = value[index]; const Value &childValue = value[index];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
if (hasChildValue) if (hasChildValue)
writeWithIndent(childValues_[index]); writeWithIndent(childValues_[index]);
else else {
{
writeIndent(); writeIndent();
writeValue(childValue); writeValue(childValue);
} }
if ( ++index == size ) if (++index == size) {
{
writeCommentAfterValueOnSameLine(childValue); writeCommentAfterValueOnSameLine(childValue);
break; break;
} }
@ -672,13 +536,11 @@ StyledStreamWriter::writeArrayValue( const Value &value )
} }
unindent(); unindent();
writeWithIndent("]"); writeWithIndent("]");
} } else // output on a single line
else // output on a single line
{ {
assert(childValues_.size() == size); assert(childValues_.size() == size);
*document_ << "[ "; *document_ << "[ ";
for ( unsigned index =0; index < size; ++index ) for (unsigned index = 0; index < size; ++index) {
{
if (index > 0) if (index > 0)
*document_ << ", "; *document_ << ", ";
*document_ << childValues_[index]; *document_ << childValues_[index];
@ -688,18 +550,14 @@ StyledStreamWriter::writeArrayValue( const Value &value )
} }
} }
bool StyledStreamWriter::isMultineArray(const Value &value) {
bool
StyledStreamWriter::isMultineArray( const Value &value )
{
int size = value.size(); int size = value.size();
bool isMultiLine = size * 3 >= rightMargin_; bool isMultiLine = size * 3 >= rightMargin_;
childValues_.clear(); childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index ) for (int index = 0; index < size && !isMultiLine; ++index) {
{
const Value &childValue = value[index]; const Value &childValue = value[index];
isMultiLine = isMultiLine || isMultiLine =
( (childValue.isArray() || childValue.isObject()) && isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0); childValue.size() > 0);
} }
if (!isMultiLine) // check if line length > max line length if (!isMultiLine) // check if line length > max line length
@ -707,8 +565,7 @@ StyledStreamWriter::isMultineArray( const Value &value )
childValues_.reserve(size); childValues_.reserve(size);
addChildValues_ = true; addChildValues_ = true;
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index ) for (int index = 0; index < size && !isMultiLine; ++index) {
{
writeValue(value[index]); writeValue(value[index]);
lineLength += int(childValues_[index].length()); lineLength += int(childValues_[index].length());
isMultiLine = isMultiLine && hasCommentForValue(value[index]); isMultiLine = isMultiLine && hasCommentForValue(value[index]);
@ -719,20 +576,14 @@ StyledStreamWriter::isMultineArray( const Value &value )
return isMultiLine; return isMultiLine;
} }
void StyledStreamWriter::pushValue(const std::string &value) {
void
StyledStreamWriter::pushValue( const std::string &value )
{
if (addChildValues_) if (addChildValues_)
childValues_.push_back(value); childValues_.push_back(value);
else else
*document_ << value; *document_ << value;
} }
void StyledStreamWriter::writeIndent() {
void
StyledStreamWriter::writeIndent()
{
/* /*
Some comments in this method would have been nice. ;-) Some comments in this method would have been nice. ;-)
@ -748,95 +599,66 @@ StyledStreamWriter::writeIndent()
*document_ << '\n' << indentString_; *document_ << '\n' << indentString_;
} }
void StyledStreamWriter::writeWithIndent(const std::string &value) {
void
StyledStreamWriter::writeWithIndent( const std::string &value )
{
writeIndent(); writeIndent();
*document_ << value; *document_ << value;
} }
void StyledStreamWriter::indent() { indentString_ += indentation_; }
void void StyledStreamWriter::unindent() {
StyledStreamWriter::indent()
{
indentString_ += indentation_;
}
void
StyledStreamWriter::unindent()
{
assert(indentString_.size() >= indentation_.size()); assert(indentString_.size() >= indentation_.size());
indentString_.resize(indentString_.size() - indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size());
} }
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_ << normalizeEOL(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_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
if ( root.hasComment( commentAfter ) ) if (root.hasComment(commentAfter)) {
{
*document_ << "\n"; *document_ << "\n";
*document_ << normalizeEOL(root.getComment(commentAfter)); *document_ << normalizeEOL(root.getComment(commentAfter));
*document_ << "\n"; *document_ << "\n";
} }
} }
bool StyledStreamWriter::hasCommentForValue(const Value &value) {
bool return value.hasComment(commentBefore) ||
StyledStreamWriter::hasCommentForValue( const Value &value ) value.hasComment(commentAfterOnSameLine) ||
{ value.hasComment(commentAfter);
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
} }
std::string StyledStreamWriter::normalizeEOL(const std::string &text) {
std::string
StyledStreamWriter::normalizeEOL( const std::string &text )
{
std::string normalized; std::string normalized;
normalized.reserve(text.length()); normalized.reserve(text.length());
const char *begin = text.c_str(); const char *begin = text.c_str();
const char *end = begin + text.length(); const char *end = begin + text.length();
const char *current = begin; const char *current = begin;
while ( current != end ) while (current != end) {
{
char c = *current++; char c = *current++;
if (c == '\r') // mac or dos EOL if (c == '\r') // mac or dos EOL
{ {
if (*current == '\n') // convert dos EOL if (*current == '\n') // convert dos EOL
++current; ++current;
normalized += '\n'; normalized += '\n';
} } else // handle unix EOL & other char
else // handle unix EOL & other char
normalized += c; normalized += c;
} }
return normalized; 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);
return sout; return sout;
} }
} // namespace Json } // namespace Json
// vim: et ts=3 sts=3 sw=3 tw=0 // vim: et ts=3 sts=3 sw=3 tw=0

View File

@ -70,42 +70,31 @@
namespace JsonTest { namespace JsonTest {
// class TestResult // class TestResult
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
TestResult::TestResult() TestResult::TestResult()
: predicateId_( 1 ) : predicateId_(1), lastUsedPredicateId_(0), messageTarget_(0) {
, lastUsedPredicateId_( 0 )
, messageTarget_( 0 )
{
// The root predicate has id 0 // The root predicate has id 0
rootPredicateNode_.id_ = 0; rootPredicateNode_.id_ = 0;
rootPredicateNode_.next_ = 0; rootPredicateNode_.next_ = 0;
predicateStackTail_ = &rootPredicateNode_; predicateStackTail_ = &rootPredicateNode_;
} }
void TestResult::setTestName(const std::string &name) { name_ = name; }
void
TestResult::setTestName( const std::string &name )
{
name_ = name;
}
TestResult & TestResult &
TestResult::addFailure( const char *file, unsigned int line, TestResult::addFailure(const char *file, unsigned int line, const char *expr) {
const char *expr ) /// Walks the PredicateContext stack adding them to failures_ if not already
{ /// added.
/// Walks the PredicateContext stack adding them to failures_ if not already added.
unsigned int nestingLevel = 0; unsigned int nestingLevel = 0;
PredicateContext *lastNode = rootPredicateNode_.next_; PredicateContext *lastNode = rootPredicateNode_.next_;
for ( ; lastNode != 0; lastNode = lastNode->next_ ) for (; lastNode != 0; lastNode = lastNode->next_) {
{
if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext
{ {
lastUsedPredicateId_ = lastNode->id_; lastUsedPredicateId_ = lastNode->id_;
addFailureInfo( lastNode->file_, lastNode->line_, lastNode->expr_, addFailureInfo(
nestingLevel ); lastNode->file_, lastNode->line_, lastNode->expr_, nestingLevel);
// Link the PredicateContext to the failure for message target when // Link the PredicateContext to the failure for message target when
// popping the PredicateContext. // popping the PredicateContext.
lastNode->failure_ = &(failures_.back()); lastNode->failure_ = &(failures_.back());
@ -119,35 +108,28 @@ TestResult::addFailure( const char *file, unsigned int line,
return *this; return *this;
} }
void TestResult::addFailureInfo(const char *file,
void unsigned int line,
TestResult::addFailureInfo( const char *file, unsigned int line, const char *expr,
const char *expr, unsigned int nestingLevel ) unsigned int nestingLevel) {
{
Failure failure; Failure failure;
failure.file_ = file; failure.file_ = file;
failure.line_ = line; failure.line_ = line;
if ( expr ) if (expr) {
{
failure.expr_ = expr; failure.expr_ = expr;
} }
failure.nestingLevel_ = nestingLevel; failure.nestingLevel_ = nestingLevel;
failures_.push_back(failure); failures_.push_back(failure);
} }
TestResult &TestResult::popPredicateContext() {
TestResult &
TestResult::popPredicateContext()
{
PredicateContext *lastNode = &rootPredicateNode_; PredicateContext *lastNode = &rootPredicateNode_;
while ( lastNode->next_ != 0 && lastNode->next_->next_ != 0 ) while (lastNode->next_ != 0 && lastNode->next_->next_ != 0) {
{
lastNode = lastNode->next_; lastNode = lastNode->next_;
} }
// Set message target to popped failure // Set message target to popped failure
PredicateContext *tail = lastNode->next_; PredicateContext *tail = lastNode->next_;
if ( tail != 0 && tail->failure_ != 0 ) if (tail != 0 && tail->failure_ != 0) {
{
messageTarget_ = tail->failure_; messageTarget_ = tail->failure_;
} }
// Remove tail from list // Remove tail from list
@ -156,79 +138,54 @@ TestResult::popPredicateContext()
return *this; return *this;
} }
bool TestResult::failed() const { return !failures_.empty(); }
bool unsigned int TestResult::getAssertionNestingLevel() const {
TestResult::failed() const
{
return !failures_.empty();
}
unsigned int
TestResult::getAssertionNestingLevel() const
{
unsigned int level = 0; unsigned int level = 0;
const PredicateContext *lastNode = &rootPredicateNode_; const PredicateContext *lastNode = &rootPredicateNode_;
while ( lastNode->next_ != 0 ) while (lastNode->next_ != 0) {
{
lastNode = lastNode->next_; lastNode = lastNode->next_;
++level; ++level;
} }
return level; return level;
} }
void TestResult::printFailure(bool printTestName) const {
void if (failures_.empty()) {
TestResult::printFailure( bool printTestName ) const
{
if ( failures_.empty() )
{
return; return;
} }
if ( printTestName ) if (printTestName) {
{
printf("* Detail of %s test failure:\n", name_.c_str()); printf("* Detail of %s test failure:\n", name_.c_str());
} }
// Print in reverse to display the callstack in the right order // Print in reverse to display the callstack in the right order
Failures::const_iterator itEnd = failures_.end(); Failures::const_iterator itEnd = failures_.end();
for ( Failures::const_iterator it = failures_.begin(); it != itEnd; ++it ) for (Failures::const_iterator it = failures_.begin(); it != itEnd; ++it) {
{
const Failure &failure = *it; const Failure &failure = *it;
std::string indent(failure.nestingLevel_ * 2, ' '); std::string indent(failure.nestingLevel_ * 2, ' ');
if ( failure.file_ ) if (failure.file_) {
{
printf("%s%s(%d): ", indent.c_str(), failure.file_, failure.line_); printf("%s%s(%d): ", indent.c_str(), failure.file_, failure.line_);
} }
if ( !failure.expr_.empty() ) if (!failure.expr_.empty()) {
{
printf("%s\n", failure.expr_.c_str()); printf("%s\n", failure.expr_.c_str());
} } else if (failure.file_) {
else if ( failure.file_ )
{
printf("\n"); printf("\n");
} }
if ( !failure.message_.empty() ) if (!failure.message_.empty()) {
{
std::string reindented = indentText(failure.message_, indent + " "); std::string reindented = indentText(failure.message_, indent + " ");
printf("%s\n", reindented.c_str()); printf("%s\n", reindented.c_str());
} }
} }
} }
std::string TestResult::indentText(const std::string &text,
std::string const std::string &indent) {
TestResult::indentText( const std::string &text,
const std::string &indent )
{
std::string reindented; std::string reindented;
std::string::size_type lastIndex = 0; std::string::size_type lastIndex = 0;
while ( lastIndex < text.size() ) while (lastIndex < text.size()) {
{
std::string::size_type nextIndex = text.find('\n', lastIndex); std::string::size_type nextIndex = text.find('\n', lastIndex);
if ( nextIndex == std::string::npos ) if (nextIndex == std::string::npos) {
{
nextIndex = text.size() - 1; nextIndex = text.size() - 1;
} }
reindented += indent; reindented += indent;
@ -238,170 +195,119 @@ TestResult::indentText( const std::string &text,
return reindented; return reindented;
} }
TestResult &TestResult::addToLastFailure(const std::string &message) {
TestResult & if (messageTarget_ != 0) {
TestResult::addToLastFailure( const std::string &message )
{
if ( messageTarget_ != 0 )
{
messageTarget_->message_ += message; messageTarget_->message_ += message;
} }
return *this; return *this;
} }
TestResult & TestResult &TestResult::operator<<(Json::Int64 value) {
TestResult::operator << ( Json::Int64 value ) {
return addToLastFailure(Json::valueToString(value)); return addToLastFailure(Json::valueToString(value));
} }
TestResult &TestResult::operator<<(Json::UInt64 value) {
TestResult &
TestResult::operator << ( Json::UInt64 value ) {
return addToLastFailure(Json::valueToString(value)); return addToLastFailure(Json::valueToString(value));
} }
TestResult &TestResult::operator<<(bool value) {
TestResult &
TestResult::operator << ( bool value ) {
return addToLastFailure(value ? "true" : "false"); return addToLastFailure(value ? "true" : "false");
} }
// class TestCase // class TestCase
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
TestCase::TestCase() TestCase::TestCase() : result_(0) {}
: result_( 0 )
{
}
TestCase::~TestCase() {}
TestCase::~TestCase() void TestCase::run(TestResult &result) {
{
}
void
TestCase::run( TestResult &result )
{
result_ = &result; result_ = &result;
runTestCase(); runTestCase();
} }
// class Runner // class Runner
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Runner::Runner() Runner::Runner() {}
{
}
Runner &Runner::add(TestCaseFactory factory) {
Runner &
Runner::add( TestCaseFactory factory )
{
tests_.push_back(factory); tests_.push_back(factory);
return *this; return *this;
} }
unsigned int Runner::testCount() const {
unsigned int
Runner::testCount() const
{
return static_cast<unsigned int>(tests_.size()); return static_cast<unsigned int>(tests_.size());
} }
std::string Runner::testNameAt(unsigned int index) const {
std::string
Runner::testNameAt( unsigned int index ) const
{
TestCase *test = tests_[index](); TestCase *test = tests_[index]();
std::string name = test->testName(); std::string name = test->testName();
delete test; delete test;
return name; return name;
} }
void Runner::runTestAt(unsigned int index, TestResult &result) const {
void
Runner::runTestAt( unsigned int index, TestResult &result ) const
{
TestCase *test = tests_[index](); TestCase *test = tests_[index]();
result.setTestName(test->testName()); result.setTestName(test->testName());
printf("Testing %s: ", test->testName()); printf("Testing %s: ", test->testName());
fflush(stdout); fflush(stdout);
#if JSON_USE_EXCEPTION #if JSON_USE_EXCEPTION
try try {
{
#endif // if JSON_USE_EXCEPTION #endif // if JSON_USE_EXCEPTION
test->run(result); test->run(result);
#if JSON_USE_EXCEPTION #if JSON_USE_EXCEPTION
} }
catch ( const std::exception &e ) catch (const std::exception &e) {
{ result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:")
result.addFailure( __FILE__, __LINE__, << e.what();
"Unexpected exception caught:" ) << e.what();
} }
#endif // if JSON_USE_EXCEPTION #endif // if JSON_USE_EXCEPTION
delete test; delete test;
const char *status = result.failed() ? "FAILED" const char *status = result.failed() ? "FAILED" : "OK";
: "OK";
printf("%s\n", status); printf("%s\n", status);
fflush(stdout); fflush(stdout);
} }
bool Runner::runAllTest(bool printSummary) const {
bool
Runner::runAllTest( bool printSummary ) const
{
unsigned int count = testCount(); unsigned int count = testCount();
std::deque<TestResult> failures; std::deque<TestResult> failures;
for ( unsigned int index = 0; index < count; ++index ) for (unsigned int index = 0; index < count; ++index) {
{
TestResult result; TestResult result;
runTestAt(index, result); runTestAt(index, result);
if ( result.failed() ) if (result.failed()) {
{
failures.push_back(result); failures.push_back(result);
} }
} }
if ( failures.empty() ) if (failures.empty()) {
{ if (printSummary) {
if ( printSummary )
{
printf("All %d tests passed\n", count); printf("All %d tests passed\n", count);
} }
return true; return true;
} } else {
else for (unsigned int index = 0; index < failures.size(); ++index) {
{
for ( unsigned int index = 0; index < failures.size(); ++index )
{
TestResult &result = failures[index]; TestResult &result = failures[index];
result.printFailure(count > 1); result.printFailure(count > 1);
} }
if ( printSummary ) if (printSummary) {
{
unsigned int failedCount = static_cast<unsigned int>(failures.size()); unsigned int failedCount = static_cast<unsigned int>(failures.size());
unsigned int passedCount = count - failedCount; unsigned int passedCount = count - failedCount;
printf( "%d/%d tests passed (%d failure(s))\n", passedCount, count, failedCount ); printf("%d/%d tests passed (%d failure(s))\n",
passedCount,
count,
failedCount);
} }
return false; return false;
} }
} }
bool Runner::testIndex(const std::string &testName,
bool unsigned int &indexOut) const {
Runner::testIndex( const std::string &testName,
unsigned int &indexOut ) const
{
unsigned int count = testCount(); unsigned int count = testCount();
for ( unsigned int index = 0; index < count; ++index ) for (unsigned int index = 0; index < count; ++index) {
{ if (testNameAt(index) == testName) {
if ( testNameAt(index) == testName )
{
indexOut = index; indexOut = index;
return true; return true;
} }
@ -409,97 +315,67 @@ Runner::testIndex( const std::string &testName,
return false; return false;
} }
void Runner::listTests() const {
void
Runner::listTests() const
{
unsigned int count = testCount(); unsigned int count = testCount();
for ( unsigned int index = 0; index < count; ++index ) for (unsigned int index = 0; index < count; ++index) {
{
printf("%s\n", testNameAt(index).c_str()); printf("%s\n", testNameAt(index).c_str());
} }
} }
int Runner::runCommandLine(int argc, const char *argv[]) const {
int
Runner::runCommandLine( int argc, const char *argv[] ) const
{
typedef std::deque<std::string> TestNames; typedef std::deque<std::string> TestNames;
Runner subrunner; Runner subrunner;
for ( int index = 1; index < argc; ++index ) for (int index = 1; index < argc; ++index) {
{
std::string opt = argv[index]; std::string opt = argv[index];
if ( opt == "--list-tests" ) if (opt == "--list-tests") {
{
listTests(); listTests();
return 0; return 0;
} } else if (opt == "--test-auto") {
else if ( opt == "--test-auto" )
{
preventDialogOnCrash(); preventDialogOnCrash();
} } else if (opt == "--test") {
else if ( opt == "--test" )
{
++index; ++index;
if ( index < argc ) if (index < argc) {
{
unsigned int testNameIndex; unsigned int testNameIndex;
if ( testIndex( argv[index], testNameIndex ) ) if (testIndex(argv[index], testNameIndex)) {
{
subrunner.add(tests_[testNameIndex]); subrunner.add(tests_[testNameIndex]);
} } else {
else
{
fprintf(stderr, "Test '%s' does not exist!\n", argv[index]); fprintf(stderr, "Test '%s' does not exist!\n", argv[index]);
return 2; return 2;
} }
} } else {
else
{
printUsage(argv[0]); printUsage(argv[0]);
return 2; return 2;
} }
} } else {
else
{
printUsage(argv[0]); printUsage(argv[0]);
return 2; return 2;
} }
} }
bool succeeded; bool succeeded;
if ( subrunner.testCount() > 0 ) if (subrunner.testCount() > 0) {
{
succeeded = subrunner.runAllTest(subrunner.testCount() > 1); succeeded = subrunner.runAllTest(subrunner.testCount() > 1);
} } else {
else
{
succeeded = runAllTest(true); succeeded = runAllTest(true);
} }
return succeeded ? 0 return succeeded ? 0 : 1;
: 1;
} }
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
// Hook MSVCRT assertions to prevent dialog from appearing // Hook MSVCRT assertions to prevent dialog from appearing
static int static int
msvcrtSilentReportHook( int reportType, char *message, int * /*returnValue*/ ) msvcrtSilentReportHook(int reportType, char *message, int * /*returnValue*/) {
{
// The default CRT handling of error and assertion is to display // The default CRT handling of error and assertion is to display
// an error dialog to the user. // an error dialog to the user.
// Instead, when an error or an assertion occurs, we force the // Instead, when an error or an assertion occurs, we force the
// application to terminate using abort() after display // application to terminate using abort() after display
// the message on stderr. // the message on stderr.
if ( reportType == _CRT_ERROR || if (reportType == _CRT_ERROR || reportType == _CRT_ASSERT) {
reportType == _CRT_ASSERT )
{
// calling abort() cause the ReportHook to be called // calling abort() cause the ReportHook to be called
// The following is used to detect this case and let's the // The following is used to detect this case and let's the
// error handler fallback on its default behaviour ( // error handler fallback on its default behaviour (
// display a warning message) // display a warning message)
static volatile bool isAborting = false; static volatile bool isAborting = false;
if ( isAborting ) if (isAborting) {
{
return TRUE; return TRUE;
} }
isAborting = true; isAborting = true;
@ -513,10 +389,7 @@ msvcrtSilentReportHook( int reportType, char *message, int * /*returnValue*/ )
} }
#endif // if defined(_MSC_VER) #endif // if defined(_MSC_VER)
void Runner::preventDialogOnCrash() {
void
Runner::preventDialogOnCrash()
{
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
// Install a hook to prevent MSVCRT error and assertion from // Install a hook to prevent MSVCRT error and assertion from
// popping a dialog // popping a dialog
@ -531,17 +404,13 @@ Runner::preventDialogOnCrash()
#if defined(_WIN32) #if defined(_WIN32)
// Prevents the system from popping a dialog for debugging if the // Prevents the system from popping a dialog for debugging if the
// application fails due to invalid memory access. // application fails due to invalid memory access.
SetErrorMode( SEM_FAILCRITICALERRORS SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
| SEM_NOGPFAULTERRORBOX SEM_NOOPENFILEERRORBOX);
| SEM_NOOPENFILEERRORBOX );
#endif // if defined(_WIN32) #endif // if defined(_WIN32)
} }
void void Runner::printUsage(const char *appName) {
Runner::printUsage( const char *appName ) printf("Usage: %s [options]\n"
{
printf(
"Usage: %s [options]\n"
"\n" "\n"
"If --test is not specified, then all the test cases be run.\n" "If --test is not specified, then all the test cases be run.\n"
"\n" "\n"
@ -550,22 +419,20 @@ Runner::printUsage( const char *appName )
" output and exit.\n" " output and exit.\n"
"--test TESTNAME: executes the test case with the specified name.\n" "--test TESTNAME: executes the test case with the specified name.\n"
" May be repeated.\n" " May be repeated.\n"
"--test-auto: prevent dialog prompting for debugging on crash.\n" "--test-auto: prevent dialog prompting for debugging on crash.\n",
, appName ); appName);
} }
// Assertion functions // Assertion functions
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
TestResult & TestResult &checkStringEqual(TestResult &result,
checkStringEqual( TestResult &result, const std::string &expected,
const std::string &expected, const std::string &actual, const std::string &actual,
const char *file, unsigned int line, const char *expr ) const char *file,
{ unsigned int line,
if ( expected != actual ) const char *expr) {
{ if (expected != actual) {
result.addFailure(file, line, expr); result.addFailure(file, line, expr);
result << "Expected: '" << expected << "'\n"; result << "Expected: '" << expected << "'\n";
result << "Actual : '" << actual << "'"; result << "Actual : '" << actual << "'";
@ -573,6 +440,5 @@ checkStringEqual( TestResult &result,
return result; return result;
} }
} // namespace JsonTest } // namespace JsonTest
// vim: et ts=4 sts=4 sw=4 tw=0 // vim: et ts=4 sts=4 sw=4 tw=0

View File

@ -20,8 +20,6 @@
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
/** \brief Unit testing framework. /** \brief Unit testing framework.
* \warning: all assertions are non-aborting, test case execution will continue * \warning: all assertions are non-aborting, test case execution will continue
* even if an assertion namespace. * even if an assertion namespace.
@ -30,9 +28,7 @@
*/ */
namespace JsonTest { namespace JsonTest {
class Failure {
class Failure
{
public: public:
const char *file_; const char *file_;
unsigned int line_; unsigned int line_;
@ -41,12 +37,10 @@ namespace JsonTest {
unsigned int nestingLevel_; unsigned int nestingLevel_;
}; };
/// Context used to create the assertion callstack on failure. /// Context used to create the assertion callstack on failure.
/// Must be a POD to allow inline initialisation without stepping /// Must be a POD to allow inline initialisation without stepping
/// into the debugger. /// into the debugger.
struct PredicateContext struct PredicateContext {
{
typedef unsigned int Id; typedef unsigned int Id;
Id id_; Id id_;
const char *file_; const char *file_;
@ -58,8 +52,7 @@ namespace JsonTest {
Failure *failure_; Failure *failure_;
}; };
class TestResult class TestResult {
{
public: public:
TestResult(); TestResult();
@ -75,8 +68,8 @@ namespace JsonTest {
void setTestName(const std::string &name); void setTestName(const std::string &name);
/// Adds an assertion failure. /// Adds an assertion failure.
TestResult &addFailure( const char *file, unsigned int line, TestResult &
const char *expr = 0 ); addFailure(const char *file, unsigned int line, const char *expr = 0);
/// Removes the last PredicateContext added to the predicate stack /// Removes the last PredicateContext added to the predicate stack
/// chained list. /// chained list.
@ -88,8 +81,7 @@ namespace JsonTest {
void printFailure(bool printTestName) const; void printFailure(bool printTestName) const;
// Generic operator that will work with anything ostream can deal with. // Generic operator that will work with anything ostream can deal with.
template <typename T> template <typename T> TestResult &operator<<(const T &value) {
TestResult &operator << ( const T& value ) {
std::ostringstream oss; std::ostringstream oss;
oss.precision(16); oss.precision(16);
oss.setf(std::ios_base::floatfield); oss.setf(std::ios_base::floatfield);
@ -107,8 +99,10 @@ namespace JsonTest {
TestResult &addToLastFailure(const std::string &message); TestResult &addToLastFailure(const std::string &message);
unsigned int getAssertionNestingLevel() const; unsigned int getAssertionNestingLevel() const;
/// Adds a failure or a predicate context /// Adds a failure or a predicate context
void addFailureInfo( const char *file, unsigned int line, void addFailureInfo(const char *file,
const char *expr, unsigned int nestingLevel ); unsigned int line,
const char *expr,
unsigned int nestingLevel);
static std::string indentText(const std::string &text, static std::string indentText(const std::string &text,
const std::string &indent); const std::string &indent);
@ -121,9 +115,7 @@ namespace JsonTest {
Failure *messageTarget_; Failure *messageTarget_;
}; };
class TestCase {
class TestCase
{
public: public:
TestCase(); TestCase();
@ -143,8 +135,7 @@ namespace JsonTest {
/// Function pointer type for TestCase factory /// Function pointer type for TestCase factory
typedef TestCase *(*TestCaseFactory)(); typedef TestCase *(*TestCaseFactory)();
class Runner class Runner {
{
public: public:
Runner(); Runner();
@ -186,12 +177,13 @@ namespace JsonTest {
}; };
template <typename T, typename U> template <typename T, typename U>
TestResult & TestResult &checkEqual(TestResult &result,
checkEqual( TestResult &result, const T &expected, const U &actual, const T &expected,
const char *file, unsigned int line, const char *expr ) const U &actual,
{ const char *file,
if ( static_cast< U >( expected ) != actual ) unsigned int line,
{ const char *expr) {
if (static_cast<U>(expected) != actual) {
result.addFailure(file, line, expr); result.addFailure(file, line, expr);
result << "Expected: " << static_cast<U>(expected) << "\n"; result << "Expected: " << static_cast<U>(expected) << "\n";
result << "Actual : " << actual; result << "Actual : " << actual;
@ -199,31 +191,31 @@ namespace JsonTest {
return result; return result;
} }
TestResult &checkStringEqual(TestResult &result,
TestResult & const std::string &expected,
checkStringEqual( TestResult &result, const std::string &actual,
const std::string &expected, const std::string &actual, const char *file,
const char *file, unsigned int line, const char *expr ); unsigned int line,
const char *expr);
} // namespace JsonTest } // namespace JsonTest
/// \brief Asserts that the given expression is true. /// \brief Asserts that the given expression is true.
/// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y; /// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y;
/// JSONTEST_ASSERT( x == y ); /// JSONTEST_ASSERT( x == y );
#define JSONTEST_ASSERT(expr) \ #define JSONTEST_ASSERT(expr) \
if ( expr ) \ if (expr) { \
{ \ } else \
} \
else \
result_->addFailure(__FILE__, __LINE__, #expr) result_->addFailure(__FILE__, __LINE__, #expr)
/// \brief Asserts that the given predicate is true. /// \brief Asserts that the given predicate is true.
/// The predicate may do other assertions and be a member function of the fixture. /// The predicate may do other assertions and be a member function of the
/// fixture.
#define JSONTEST_ASSERT_PRED(expr) \ #define JSONTEST_ASSERT_PRED(expr) \
{ \ { \
JsonTest::PredicateContext _minitest_Context = { \ JsonTest::PredicateContext _minitest_Context = { \
result_->predicateId_, __FILE__, __LINE__, #expr }; \ result_->predicateId_, __FILE__, __LINE__, #expr \
}; \
result_->predicateStackTail_->next_ = &_minitest_Context; \ result_->predicateStackTail_->next_ = &_minitest_Context; \
result_->predicateId_ += 1; \ result_->predicateId_ += 1; \
result_->predicateStackTail_ = &_minitest_Context; \ result_->predicateStackTail_ = &_minitest_Context; \
@ -233,31 +225,32 @@ namespace JsonTest {
/// \brief Asserts that two values are equals. /// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL(expected, actual) \ #define JSONTEST_ASSERT_EQUAL(expected, actual) \
JsonTest::checkEqual( *result_, expected, actual, \ JsonTest::checkEqual(*result_, \
__FILE__, __LINE__, \ expected, \
actual, \
__FILE__, \
__LINE__, \
#expected " == " #actual) #expected " == " #actual)
/// \brief Asserts that two values are equals. /// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_STRING_EQUAL(expected, actual) \ #define JSONTEST_ASSERT_STRING_EQUAL(expected, actual) \
JsonTest::checkStringEqual(*result_, \ JsonTest::checkStringEqual(*result_, \
std::string(expected), std::string(actual), \ std::string(expected), \
__FILE__, __LINE__, \ std::string(actual), \
__FILE__, \
__LINE__, \
#expected " == " #actual) #expected " == " #actual)
/// \brief Begin a fixture test case. /// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE(FixtureType, name) \ #define JSONTEST_FIXTURE(FixtureType, name) \
class Test##FixtureType##name : public FixtureType \ class Test##FixtureType##name : public FixtureType { \
{ \
public: \ public: \
static JsonTest::TestCase *factory() \ static JsonTest::TestCase *factory() { \
{ \
return new Test##FixtureType##name(); \ return new Test##FixtureType##name(); \
} \ } \
\
public: /* overidden from TestCase */ \ public: /* overidden from TestCase */ \
virtual const char *testName() const \ virtual const char *testName() const { return #FixtureType "/" #name; } \
{ \
return #FixtureType "/" #name; \
} \
virtual void runTestCase(); \ virtual void runTestCase(); \
}; \ }; \
\ \

View File

@ -22,7 +22,6 @@ 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);
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// Json Library test cases // Json Library test cases
@ -30,19 +29,17 @@ static const float kfuint32max = float(kuint32max);
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double uint64ToDouble( Json::UInt64 value ) static inline double uint64ToDouble(Json::UInt64 value) {
{
return static_cast<double>(value); return static_cast<double>(value);
} }
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double uint64ToDouble( Json::UInt64 value ) static inline double uint64ToDouble(Json::UInt64 value) {
{ return static_cast<double>(Json::Int64(value / 2)) * 2.0 +
return static_cast<double>( Json::Int64(value/2) ) * 2.0 + Json::Int64(value & 1); Json::Int64(value & 1);
} }
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
struct ValueTest : JsonTest::TestCase struct ValueTest : JsonTest::TestCase {
{
Json::Value null_; Json::Value null_;
Json::Value emptyArray_; Json::Value emptyArray_;
Json::Value emptyObject_; Json::Value emptyObject_;
@ -59,27 +56,17 @@ struct ValueTest : JsonTest::TestCase
Json::Value true_; Json::Value true_;
Json::Value false_; Json::Value false_;
ValueTest() ValueTest()
: emptyArray_( Json::arrayValue ) : emptyArray_(Json::arrayValue), emptyObject_(Json::objectValue),
, emptyObject_( Json::objectValue ) integer_(123456789), unsignedInteger_(34567890u),
, integer_( 123456789 ) smallUnsignedInteger_(Json::Value::UInt(Json::Value::maxInt)),
, unsignedInteger_( 34567890u ) real_(1234.56789), float_(0.00390625f), emptyString_(""), string1_("a"),
, smallUnsignedInteger_( Json::Value::UInt( Json::Value::maxInt ) ) string_("sometext with space"), true_(true), false_(false) {
, real_( 1234.56789 )
, float_( 0.00390625f )
, emptyString_( "" )
, string1_( "a" )
, string_( "sometext with space" )
, true_( true )
, false_( false )
{
array1_.append(1234); array1_.append(1234);
object1_["id"] = 1234; object1_["id"] = 1234;
} }
struct IsCheck struct IsCheck {
{
/// Initialize all checks to \c false by default. /// Initialize all checks to \c false by default.
IsCheck(); IsCheck();
@ -98,7 +85,8 @@ struct ValueTest : JsonTest::TestCase
bool isNumeric_; bool isNumeric_;
}; };
void checkConstMemberCount( const Json::Value &value, unsigned int expectedCount ); void checkConstMemberCount(const Json::Value &value,
unsigned int expectedCount);
void checkMemberCount(Json::Value &value, unsigned int expectedCount); void checkMemberCount(Json::Value &value, unsigned int expectedCount);
@ -108,23 +96,23 @@ struct ValueTest : JsonTest::TestCase
void checkIsEqual(const Json::Value &x, const Json::Value &y); void checkIsEqual(const Json::Value &x, const Json::Value &y);
/// Normalize the representation of floating-point number by stripped leading 0 in exponent. /// Normalize the representation of floating-point number by stripped leading
/// 0 in exponent.
static std::string normalizeFloatingPointStr(const std::string &s); static std::string normalizeFloatingPointStr(const std::string &s);
}; };
std::string ValueTest::normalizeFloatingPointStr(const std::string &s) {
std::string
ValueTest::normalizeFloatingPointStr( const std::string &s )
{
std::string::size_type index = s.find_last_of("eE"); std::string::size_type index = s.find_last_of("eE");
if ( index != std::string::npos ) if (index != std::string::npos) {
{ std::string::size_type hasSign =
std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0; (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
std::string::size_type exponentStartIndex = index + 1 + hasSign; std::string::size_type exponentStartIndex = index + 1 + hasSign;
std::string normalized = s.substr(0, exponentStartIndex); std::string normalized = s.substr(0, exponentStartIndex);
std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex ); std::string::size_type indexDigit =
s.find_first_not_of('0', exponentStartIndex);
std::string exponent = "0"; std::string exponent = "0";
if ( indexDigit != std::string::npos ) // There is an exponent different from 0 if (indexDigit !=
std::string::npos) // There is an exponent different from 0
{ {
exponent = s.substr(indexDigit); exponent = s.substr(indexDigit);
} }
@ -133,27 +121,31 @@ ValueTest::normalizeFloatingPointStr( const std::string &s )
return s; return s;
} }
JSONTEST_FIXTURE(ValueTest, checkNormalizeFloatingPointStr) {
JSONTEST_FIXTURE( ValueTest, checkNormalizeFloatingPointStr )
{
JSONTEST_ASSERT_STRING_EQUAL("0.0", normalizeFloatingPointStr("0.0")); JSONTEST_ASSERT_STRING_EQUAL("0.0", normalizeFloatingPointStr("0.0"));
JSONTEST_ASSERT_STRING_EQUAL("0e0", normalizeFloatingPointStr("0e0")); JSONTEST_ASSERT_STRING_EQUAL("0e0", normalizeFloatingPointStr("0e0"));
JSONTEST_ASSERT_STRING_EQUAL("1234.0", normalizeFloatingPointStr("1234.0")); JSONTEST_ASSERT_STRING_EQUAL("1234.0", normalizeFloatingPointStr("1234.0"));
JSONTEST_ASSERT_STRING_EQUAL( "1234.0e0", normalizeFloatingPointStr("1234.0e0") ); JSONTEST_ASSERT_STRING_EQUAL("1234.0e0",
JSONTEST_ASSERT_STRING_EQUAL( "1234.0e+0", normalizeFloatingPointStr("1234.0e+0") ); normalizeFloatingPointStr("1234.0e0"));
JSONTEST_ASSERT_STRING_EQUAL("1234.0e+0",
normalizeFloatingPointStr("1234.0e+0"));
JSONTEST_ASSERT_STRING_EQUAL("1234e-1", normalizeFloatingPointStr("1234e-1")); JSONTEST_ASSERT_STRING_EQUAL("1234e-1", normalizeFloatingPointStr("1234e-1"));
JSONTEST_ASSERT_STRING_EQUAL("1234e10", normalizeFloatingPointStr("1234e10")); JSONTEST_ASSERT_STRING_EQUAL("1234e10", normalizeFloatingPointStr("1234e10"));
JSONTEST_ASSERT_STRING_EQUAL( "1234e10", normalizeFloatingPointStr("1234e010") ); JSONTEST_ASSERT_STRING_EQUAL("1234e10",
JSONTEST_ASSERT_STRING_EQUAL( "1234e+10", normalizeFloatingPointStr("1234e+010") ); normalizeFloatingPointStr("1234e010"));
JSONTEST_ASSERT_STRING_EQUAL( "1234e-10", normalizeFloatingPointStr("1234e-010") ); JSONTEST_ASSERT_STRING_EQUAL("1234e+10",
JSONTEST_ASSERT_STRING_EQUAL( "1234e+100", normalizeFloatingPointStr("1234e+100") ); normalizeFloatingPointStr("1234e+010"));
JSONTEST_ASSERT_STRING_EQUAL( "1234e-100", normalizeFloatingPointStr("1234e-100") ); JSONTEST_ASSERT_STRING_EQUAL("1234e-10",
JSONTEST_ASSERT_STRING_EQUAL( "1234e+1", normalizeFloatingPointStr("1234e+001") ); normalizeFloatingPointStr("1234e-010"));
JSONTEST_ASSERT_STRING_EQUAL("1234e+100",
normalizeFloatingPointStr("1234e+100"));
JSONTEST_ASSERT_STRING_EQUAL("1234e-100",
normalizeFloatingPointStr("1234e-100"));
JSONTEST_ASSERT_STRING_EQUAL("1234e+1",
normalizeFloatingPointStr("1234e+001"));
} }
JSONTEST_FIXTURE(ValueTest, memberCount) {
JSONTEST_FIXTURE( ValueTest, memberCount )
{
JSONTEST_ASSERT_PRED(checkMemberCount(emptyArray_, 0)); JSONTEST_ASSERT_PRED(checkMemberCount(emptyArray_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(emptyObject_, 0)); JSONTEST_ASSERT_PRED(checkMemberCount(emptyObject_, 0));
JSONTEST_ASSERT_PRED(checkMemberCount(array1_, 1)); JSONTEST_ASSERT_PRED(checkMemberCount(array1_, 1));
@ -168,9 +160,7 @@ JSONTEST_FIXTURE( ValueTest, memberCount )
JSONTEST_ASSERT_PRED(checkMemberCount(true_, 0)); JSONTEST_ASSERT_PRED(checkMemberCount(true_, 0));
} }
JSONTEST_FIXTURE(ValueTest, objects) {
JSONTEST_FIXTURE( ValueTest, objects )
{
// Types // Types
IsCheck checks; IsCheck checks;
checks.isObject_ = true; checks.isObject_ = true;
@ -210,9 +200,7 @@ JSONTEST_FIXTURE( ValueTest, objects )
JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]); JSONTEST_ASSERT_EQUAL(Json::Value("foo"), object1_["some other id"]);
} }
JSONTEST_FIXTURE(ValueTest, arrays) {
JSONTEST_FIXTURE( ValueTest, arrays )
{
const unsigned int index0 = 0; const unsigned int index0 = 0;
// Types // Types
@ -254,9 +242,7 @@ JSONTEST_FIXTURE( ValueTest, arrays )
JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]); JSONTEST_ASSERT_EQUAL(Json::Value(17), array1_[2]);
} }
JSONTEST_FIXTURE(ValueTest, null) {
JSONTEST_FIXTURE( ValueTest, null )
{
JSONTEST_ASSERT_EQUAL(Json::nullValue, null_.type()); JSONTEST_ASSERT_EQUAL(Json::nullValue, null_.type());
IsCheck checks; IsCheck checks;
@ -281,9 +267,7 @@ JSONTEST_FIXTURE( ValueTest, null )
JSONTEST_ASSERT_STRING_EQUAL("", null_.asString()); JSONTEST_ASSERT_STRING_EQUAL("", null_.asString());
} }
JSONTEST_FIXTURE(ValueTest, strings) {
JSONTEST_FIXTURE( ValueTest, strings )
{
JSONTEST_ASSERT_EQUAL(Json::stringValue, string1_.type()); JSONTEST_ASSERT_EQUAL(Json::stringValue, string1_.type());
IsCheck checks; IsCheck checks;
@ -312,9 +296,7 @@ JSONTEST_FIXTURE( ValueTest, strings )
JSONTEST_ASSERT_STRING_EQUAL("a", string1_.asCString()); JSONTEST_ASSERT_STRING_EQUAL("a", string1_.asCString());
} }
JSONTEST_FIXTURE(ValueTest, bools) {
JSONTEST_FIXTURE( ValueTest, bools )
{
JSONTEST_ASSERT_EQUAL(Json::booleanValue, false_.type()); JSONTEST_ASSERT_EQUAL(Json::booleanValue, false_.type());
IsCheck checks; IsCheck checks;
@ -356,9 +338,7 @@ JSONTEST_FIXTURE( ValueTest, bools )
JSONTEST_ASSERT_EQUAL(0.0, false_.asFloat()); JSONTEST_ASSERT_EQUAL(0.0, false_.asFloat());
} }
JSONTEST_FIXTURE(ValueTest, integers) {
JSONTEST_FIXTURE( ValueTest, integers )
{
IsCheck checks; IsCheck checks;
Json::Value val; Json::Value val;
@ -630,7 +610,8 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL((1 << 20), val.asDouble()); JSONTEST_ASSERT_EQUAL((1 << 20), val.asDouble());
JSONTEST_ASSERT_EQUAL((1 << 20), val.asFloat()); JSONTEST_ASSERT_EQUAL((1 << 20), val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1048576", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1048576",
normalizeFloatingPointStr(val.asString()));
// -2^20 // -2^20
val = Json::Value(-(1 << 20)); val = Json::Value(-(1 << 20));
@ -870,7 +851,8 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asDouble()); JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asDouble());
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asFloat()); JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1099511627776", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1099511627776",
normalizeFloatingPointStr(val.asString()));
// -2^40 // -2^40
val = Json::Value(-(Json::Int64(1) << 40)); val = Json::Value(-(Json::Int64(1) << 40));
@ -941,9 +923,11 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(Json::UInt64(1) << 63, val.asUInt64()); JSONTEST_ASSERT_EQUAL(Json::UInt64(1) << 63, val.asUInt64());
JSONTEST_ASSERT_EQUAL(Json::UInt64(1) << 63, val.asLargestUInt()); JSONTEST_ASSERT_EQUAL(Json::UInt64(1) << 63, val.asLargestUInt());
JSONTEST_ASSERT_EQUAL(uint64ToDouble(Json::UInt64(1) << 63), val.asDouble()); JSONTEST_ASSERT_EQUAL(uint64ToDouble(Json::UInt64(1) << 63), val.asDouble());
JSONTEST_ASSERT_EQUAL(float(uint64ToDouble(Json::UInt64(1) << 63)), val.asFloat()); JSONTEST_ASSERT_EQUAL(float(uint64ToDouble(Json::UInt64(1) << 63)),
val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("9.223372036854776e+18", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("9.223372036854776e+18",
normalizeFloatingPointStr(val.asString()));
// int64 min // int64 min
val = Json::Value(Json::Int64(kint64min)); val = Json::Value(Json::Int64(kint64min));
@ -990,7 +974,8 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(-9223372036854775808.0, val.asDouble()); JSONTEST_ASSERT_EQUAL(-9223372036854775808.0, val.asDouble());
JSONTEST_ASSERT_EQUAL(-9223372036854775808.0, val.asFloat()); JSONTEST_ASSERT_EQUAL(-9223372036854775808.0, val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("-9.223372036854776e+18", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("-9.223372036854776e+18",
normalizeFloatingPointStr(val.asString()));
// 10^19 // 10^19
const Json::UInt64 ten_to_19 = static_cast<Json::UInt64>(1e19); const Json::UInt64 ten_to_19 = static_cast<Json::UInt64>(1e19);
@ -1036,7 +1021,8 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(1e19, val.asDouble()); JSONTEST_ASSERT_EQUAL(1e19, val.asDouble());
JSONTEST_ASSERT_EQUAL(1e19, val.asFloat()); JSONTEST_ASSERT_EQUAL(1e19, val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1e+19", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1e+19",
normalizeFloatingPointStr(val.asString()));
// uint64 max // uint64 max
val = Json::Value(Json::UInt64(kuint64max)); val = Json::Value(Json::UInt64(kuint64max));
@ -1079,13 +1065,12 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(18446744073709551616.0, val.asDouble()); JSONTEST_ASSERT_EQUAL(18446744073709551616.0, val.asDouble());
JSONTEST_ASSERT_EQUAL(18446744073709551616.0, val.asFloat()); JSONTEST_ASSERT_EQUAL(18446744073709551616.0, val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1.844674407370955e+19", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1.844674407370955e+19",
normalizeFloatingPointStr(val.asString()));
#endif #endif
} }
JSONTEST_FIXTURE(ValueTest, nonIntegers) {
JSONTEST_FIXTURE( ValueTest, nonIntegers )
{
IsCheck checks; IsCheck checks;
Json::Value val; Json::Value val;
@ -1170,7 +1155,8 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(2147483647U, val.asLargestUInt()); JSONTEST_ASSERT_EQUAL(2147483647U, val.asLargestUInt());
#endif #endif
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("2147483647.5", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_EQUAL("2147483647.5",
normalizeFloatingPointStr(val.asString()));
// A bit under int32 min // A bit under int32 min
val = Json::Value(kint32min - 0.5); val = Json::Value(kint32min - 0.5);
@ -1197,7 +1183,8 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(-Json::Int64(1) << 31, val.asLargestInt()); JSONTEST_ASSERT_EQUAL(-Json::Int64(1) << 31, val.asLargestInt());
#endif #endif
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("-2147483648.5", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_EQUAL("-2147483648.5",
normalizeFloatingPointStr(val.asString()));
// A bit over uint32 max // A bit over uint32 max
val = Json::Value(kuint32max + 0.5); val = Json::Value(kuint32max + 0.5);
@ -1222,52 +1209,53 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(float(4294967295.5), val.asFloat()); JSONTEST_ASSERT_EQUAL(float(4294967295.5), val.asFloat());
#ifdef JSON_HAS_INT64 #ifdef JSON_HAS_INT64
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 32) - 1, val.asLargestInt()); JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 32) - 1, val.asLargestInt());
JSONTEST_ASSERT_EQUAL((Json::UInt64(1) << 32)-Json::UInt64(1), val.asLargestUInt()); JSONTEST_ASSERT_EQUAL((Json::UInt64(1) << 32) - Json::UInt64(1),
val.asLargestUInt());
#endif #endif
JSONTEST_ASSERT_EQUAL(true, val.asBool()); JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("4294967295.5", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_EQUAL("4294967295.5",
normalizeFloatingPointStr(val.asString()));
val = Json::Value(1.2345678901234); val = Json::Value(1.2345678901234);
JSONTEST_ASSERT_STRING_EQUAL( "1.2345678901234", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1.2345678901234",
normalizeFloatingPointStr(val.asString()));
// A 16-digit floating point number. // A 16-digit floating point number.
val = Json::Value(2199023255552000.0f); val = Json::Value(2199023255552000.0f);
JSONTEST_ASSERT_EQUAL(float(2199023255552000), val.asFloat()); JSONTEST_ASSERT_EQUAL(float(2199023255552000), val.asFloat());
JSONTEST_ASSERT_STRING_EQUAL("2199023255552000", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("2199023255552000",
normalizeFloatingPointStr(val.asString()));
// A very large floating point number. // A very large floating point number.
val = Json::Value(3.402823466385289e38); val = Json::Value(3.402823466385289e38);
JSONTEST_ASSERT_EQUAL(float(3.402823466385289e38), val.asFloat()); JSONTEST_ASSERT_EQUAL(float(3.402823466385289e38), val.asFloat());
JSONTEST_ASSERT_STRING_EQUAL("3.402823466385289e+38", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("3.402823466385289e+38",
normalizeFloatingPointStr(val.asString()));
// An even larger floating point number. // An even larger floating point number.
val = Json::Value(1.2345678e300); val = Json::Value(1.2345678e300);
JSONTEST_ASSERT_EQUAL(double(1.2345678e300), val.asDouble()); JSONTEST_ASSERT_EQUAL(double(1.2345678e300), val.asDouble());
JSONTEST_ASSERT_STRING_EQUAL("1.2345678e+300", normalizeFloatingPointStr(val.asString())); JSONTEST_ASSERT_STRING_EQUAL("1.2345678e+300",
normalizeFloatingPointStr(val.asString()));
} }
void ValueTest::checkConstMemberCount(const Json::Value &value,
void unsigned int expectedCount) {
ValueTest::checkConstMemberCount( const Json::Value &value, unsigned int expectedCount )
{
unsigned int count = 0; unsigned int count = 0;
Json::Value::const_iterator itEnd = value.end(); Json::Value::const_iterator itEnd = value.end();
for ( Json::Value::const_iterator it = value.begin(); it != itEnd; ++it ) for (Json::Value::const_iterator it = value.begin(); it != itEnd; ++it) {
{
++count; ++count;
} }
JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::const_iterator"; JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::const_iterator";
} }
void void ValueTest::checkMemberCount(Json::Value &value,
ValueTest::checkMemberCount( Json::Value &value, unsigned int expectedCount ) unsigned int expectedCount) {
{
JSONTEST_ASSERT_EQUAL(expectedCount, value.size()); JSONTEST_ASSERT_EQUAL(expectedCount, value.size());
unsigned int count = 0; unsigned int count = 0;
Json::Value::iterator itEnd = value.end(); Json::Value::iterator itEnd = value.end();
for ( Json::Value::iterator it = value.begin(); it != itEnd; ++it ) for (Json::Value::iterator it = value.begin(); it != itEnd; ++it) {
{
++count; ++count;
} }
JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::iterator"; JSONTEST_ASSERT_EQUAL(expectedCount, count) << "Json::Value::iterator";
@ -1275,27 +1263,13 @@ ValueTest::checkMemberCount( Json::Value &value, unsigned int expectedCount )
JSONTEST_ASSERT_PRED(checkConstMemberCount(value, expectedCount)); JSONTEST_ASSERT_PRED(checkConstMemberCount(value, expectedCount));
} }
ValueTest::IsCheck::IsCheck() ValueTest::IsCheck::IsCheck()
: isObject_( false ) : isObject_(false), isArray_(false), isBool_(false), isString_(false),
, isArray_( false ) isNull_(false), isInt_(false), isInt64_(false), isUInt_(false),
, isBool_( false ) isUInt64_(false), isIntegral_(false), isDouble_(false),
, isString_( false ) isNumeric_(false) {}
, isNull_( false )
, isInt_( false )
, isInt64_( false )
, isUInt_( false )
, isUInt64_( false )
, isIntegral_( false )
, isDouble_( false )
, isNumeric_( false )
{
}
void ValueTest::checkIs(const Json::Value &value, const IsCheck &check) {
void
ValueTest::checkIs( const Json::Value &value, const IsCheck &check )
{
JSONTEST_ASSERT_EQUAL(check.isObject_, value.isObject()); JSONTEST_ASSERT_EQUAL(check.isObject_, value.isObject());
JSONTEST_ASSERT_EQUAL(check.isArray_, value.isArray()); JSONTEST_ASSERT_EQUAL(check.isArray_, value.isArray());
JSONTEST_ASSERT_EQUAL(check.isBool_, value.isBool()); JSONTEST_ASSERT_EQUAL(check.isBool_, value.isBool());
@ -1316,40 +1290,31 @@ ValueTest::checkIs( const Json::Value &value, const IsCheck &check )
#endif #endif
} }
JSONTEST_FIXTURE( ValueTest, compareNull ) JSONTEST_FIXTURE(ValueTest, compareNull) {
{
JSONTEST_ASSERT_PRED(checkIsEqual(Json::Value(), Json::Value())); JSONTEST_ASSERT_PRED(checkIsEqual(Json::Value(), Json::Value()));
} }
JSONTEST_FIXTURE(ValueTest, compareInt) {
JSONTEST_FIXTURE( ValueTest, compareInt )
{
JSONTEST_ASSERT_PRED(checkIsLess(0, 10)); JSONTEST_ASSERT_PRED(checkIsLess(0, 10));
JSONTEST_ASSERT_PRED(checkIsEqual(10, 10)); JSONTEST_ASSERT_PRED(checkIsEqual(10, 10));
JSONTEST_ASSERT_PRED(checkIsEqual(-10, -10)); JSONTEST_ASSERT_PRED(checkIsEqual(-10, -10));
JSONTEST_ASSERT_PRED(checkIsLess(-10, 0)); JSONTEST_ASSERT_PRED(checkIsLess(-10, 0));
} }
JSONTEST_FIXTURE(ValueTest, compareUInt) {
JSONTEST_FIXTURE( ValueTest, compareUInt )
{
JSONTEST_ASSERT_PRED(checkIsLess(0u, 10u)); JSONTEST_ASSERT_PRED(checkIsLess(0u, 10u));
JSONTEST_ASSERT_PRED(checkIsLess(0u, Json::Value::maxUInt)); JSONTEST_ASSERT_PRED(checkIsLess(0u, Json::Value::maxUInt));
JSONTEST_ASSERT_PRED(checkIsEqual(10u, 10u)); JSONTEST_ASSERT_PRED(checkIsEqual(10u, 10u));
} }
JSONTEST_FIXTURE(ValueTest, compareDouble) {
JSONTEST_FIXTURE( ValueTest, compareDouble )
{
JSONTEST_ASSERT_PRED(checkIsLess(0.0, 10.0)); JSONTEST_ASSERT_PRED(checkIsLess(0.0, 10.0));
JSONTEST_ASSERT_PRED(checkIsEqual(10.0, 10.0)); JSONTEST_ASSERT_PRED(checkIsEqual(10.0, 10.0));
JSONTEST_ASSERT_PRED(checkIsEqual(-10.0, -10.0)); JSONTEST_ASSERT_PRED(checkIsEqual(-10.0, -10.0));
JSONTEST_ASSERT_PRED(checkIsLess(-10.0, 0.0)); JSONTEST_ASSERT_PRED(checkIsLess(-10.0, 0.0));
} }
JSONTEST_FIXTURE(ValueTest, compareString) {
JSONTEST_FIXTURE( ValueTest, compareString )
{
JSONTEST_ASSERT_PRED(checkIsLess("", " ")); JSONTEST_ASSERT_PRED(checkIsLess("", " "));
JSONTEST_ASSERT_PRED(checkIsLess("", "a")); JSONTEST_ASSERT_PRED(checkIsLess("", "a"));
JSONTEST_ASSERT_PRED(checkIsLess("abcd", "zyui")); JSONTEST_ASSERT_PRED(checkIsLess("abcd", "zyui"));
@ -1360,17 +1325,13 @@ JSONTEST_FIXTURE( ValueTest, compareString )
JSONTEST_ASSERT_PRED(checkIsEqual("ABCD", "ABCD")); JSONTEST_ASSERT_PRED(checkIsEqual("ABCD", "ABCD"));
} }
JSONTEST_FIXTURE(ValueTest, compareBoolean) {
JSONTEST_FIXTURE( ValueTest, compareBoolean )
{
JSONTEST_ASSERT_PRED(checkIsLess(false, true)); JSONTEST_ASSERT_PRED(checkIsLess(false, true));
JSONTEST_ASSERT_PRED(checkIsEqual(false, false)); JSONTEST_ASSERT_PRED(checkIsEqual(false, false));
JSONTEST_ASSERT_PRED(checkIsEqual(true, true)); JSONTEST_ASSERT_PRED(checkIsEqual(true, true));
} }
JSONTEST_FIXTURE(ValueTest, compareArray) {
JSONTEST_FIXTURE( ValueTest, compareArray )
{
// array compare size then content // array compare size then content
Json::Value emptyArray(Json::arrayValue); Json::Value emptyArray(Json::arrayValue);
Json::Value l1aArray; Json::Value l1aArray;
@ -1392,9 +1353,7 @@ JSONTEST_FIXTURE( ValueTest, compareArray )
JSONTEST_ASSERT_PRED(checkIsEqual(l2bArray, Json::Value(l2bArray))); JSONTEST_ASSERT_PRED(checkIsEqual(l2bArray, Json::Value(l2bArray)));
} }
JSONTEST_FIXTURE(ValueTest, compareObject) {
JSONTEST_FIXTURE( ValueTest, compareObject )
{
// object compare size then content // object compare size then content
Json::Value emptyObject(Json::objectValue); Json::Value emptyObject(Json::objectValue);
Json::Value l1aObject; Json::Value l1aObject;
@ -1412,23 +1371,20 @@ JSONTEST_FIXTURE( ValueTest, compareObject )
JSONTEST_ASSERT_PRED(checkIsEqual(l2aObject, Json::Value(l2aObject))); JSONTEST_ASSERT_PRED(checkIsEqual(l2aObject, Json::Value(l2aObject)));
} }
JSONTEST_FIXTURE(ValueTest, compareType) {
JSONTEST_FIXTURE( ValueTest, compareType )
{
// object of different type are ordered according to their type // object of different type are ordered according to their type
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(), Json::Value(1))); JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(), Json::Value(1)));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1), Json::Value(1u))); JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1), Json::Value(1u)));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1u), Json::Value(1.0))); JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1u), Json::Value(1.0)));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1.0), Json::Value("a"))); JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(1.0), Json::Value("a")));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value("a"), Json::Value(true))); JSONTEST_ASSERT_PRED(checkIsLess(Json::Value("a"), Json::Value(true)));
JSONTEST_ASSERT_PRED( checkIsLess( Json::Value(true), Json::Value(Json::arrayValue) ) ); JSONTEST_ASSERT_PRED(
JSONTEST_ASSERT_PRED( checkIsLess( Json::Value(Json::arrayValue), Json::Value(Json::objectValue) ) ); checkIsLess(Json::Value(true), Json::Value(Json::arrayValue)));
JSONTEST_ASSERT_PRED(checkIsLess(Json::Value(Json::arrayValue),
Json::Value(Json::objectValue)));
} }
void ValueTest::checkIsLess(const Json::Value &x, const Json::Value &y) {
void
ValueTest::checkIsLess( const Json::Value &x, const Json::Value &y )
{
JSONTEST_ASSERT(x < y); JSONTEST_ASSERT(x < y);
JSONTEST_ASSERT(y > x); JSONTEST_ASSERT(y > x);
JSONTEST_ASSERT(x <= y); JSONTEST_ASSERT(x <= y);
@ -1443,10 +1399,7 @@ ValueTest::checkIsLess( const Json::Value &x, const Json::Value &y )
JSONTEST_ASSERT(y.compare(x) >= 0); JSONTEST_ASSERT(y.compare(x) >= 0);
} }
void ValueTest::checkIsEqual(const Json::Value &x, const Json::Value &y) {
void
ValueTest::checkIsEqual( const Json::Value &x, const Json::Value &y )
{
JSONTEST_ASSERT(x == y); JSONTEST_ASSERT(x == y);
JSONTEST_ASSERT(y == x); JSONTEST_ASSERT(y == x);
JSONTEST_ASSERT(x <= y); JSONTEST_ASSERT(x <= y);
@ -1461,24 +1414,21 @@ ValueTest::checkIsEqual( const Json::Value &x, const Json::Value &y )
JSONTEST_ASSERT(y.compare(x) == 0); JSONTEST_ASSERT(y.compare(x) == 0);
} }
JSONTEST_FIXTURE(ValueTest, checkInteger) {
JSONTEST_FIXTURE( ValueTest, checkInteger )
{
#if JSON_USE_EXCEPTION #if JSON_USE_EXCEPTION
try { try {
Json::Value x = 1; Json::Value x = 1;
x["key"]; // SIGABRT? x["key"]; // SIGABRT?
// regression for https://sourceforge.net/p/jsoncpp/bugs/67/ // regression for https://sourceforge.net/p/jsoncpp/bugs/67/
JSONTEST_ASSERT(0); JSONTEST_ASSERT(0);
} catch (std::runtime_error const&) { }
catch (std::runtime_error const &) {
JSONTEST_ASSERT(1); // good JSONTEST_ASSERT(1); // good
} }
#endif // JSON_USE_EXCEPTION #endif // JSON_USE_EXCEPTION
} }
JSONTEST_FIXTURE(ValueTest, offsetAccessors) {
JSONTEST_FIXTURE( ValueTest, offsetAccessors )
{
Json::Value x; Json::Value x;
JSONTEST_ASSERT(x.getOffsetStart() == 0); JSONTEST_ASSERT(x.getOffsetStart() == 0);
JSONTEST_ASSERT(x.getOffsetLimit() == 0); JSONTEST_ASSERT(x.getOffsetLimit() == 0);
@ -1497,13 +1447,9 @@ JSONTEST_FIXTURE( ValueTest, offsetAccessors )
JSONTEST_ASSERT(y.getOffsetLimit() == 0); JSONTEST_ASSERT(y.getOffsetLimit() == 0);
} }
struct WriterTest : JsonTest::TestCase struct WriterTest : JsonTest::TestCase {};
{
};
JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
JSONTEST_FIXTURE( WriterTest, dropNullPlaceholders )
{
Json::FastWriter writer; Json::FastWriter writer;
Json::Value nullValue; Json::Value nullValue;
JSONTEST_ASSERT(writer.write(nullValue) == "null\n"); JSONTEST_ASSERT(writer.write(nullValue) == "null\n");
@ -1512,31 +1458,23 @@ JSONTEST_FIXTURE( WriterTest, dropNullPlaceholders )
JSONTEST_ASSERT(writer.write(nullValue) == "\n"); JSONTEST_ASSERT(writer.write(nullValue) == "\n");
} }
struct ReaderTest : JsonTest::TestCase {};
struct ReaderTest : JsonTest::TestCase JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
{
};
JSONTEST_FIXTURE( ReaderTest, parseWithNoErrors )
{
Json::Reader reader; Json::Reader reader;
Json::Value root; Json::Value root;
bool ok = reader.parse( bool ok = reader.parse("{ \"property\" : \"value\" }", root);
"{ \"property\" : \"value\" }",
root);
JSONTEST_ASSERT(ok); JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0); JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0);
JSONTEST_ASSERT(reader.getStructuredErrors().size() == 0); JSONTEST_ASSERT(reader.getStructuredErrors().size() == 0);
} }
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) {
JSONTEST_FIXTURE( ReaderTest, parseWithNoErrorsTestingOffsets )
{
Json::Reader reader; Json::Reader reader;
Json::Value root; Json::Value root;
bool ok = reader.parse( bool ok = reader.parse("{ \"property\" : [\"value\", \"value2\"], \"obj\" : "
"{ \"property\" : [\"value\", \"value2\"], \"obj\" : { \"nested\" : 123, \"bool\" : true}, \"null\" : null, \"false\" : false }", "{ \"nested\" : 123, \"bool\" : true}, \"null\" : "
"null, \"false\" : false }",
root); root);
JSONTEST_ASSERT(ok); JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0); JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0);
@ -1561,17 +1499,14 @@ JSONTEST_FIXTURE( ReaderTest, parseWithNoErrorsTestingOffsets )
JSONTEST_ASSERT(root.getOffsetLimit() == 110); JSONTEST_ASSERT(root.getOffsetLimit() == 110);
} }
JSONTEST_FIXTURE(ReaderTest, parseWithOneError) {
JSONTEST_FIXTURE( ReaderTest, parseWithOneError )
{
Json::Reader reader; Json::Reader reader;
Json::Value root; Json::Value root;
bool ok = reader.parse( bool ok = reader.parse("{ \"property\" :: \"value\" }", root);
"{ \"property\" :: \"value\" }",
root);
JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(!ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages() == JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 15\n Syntax error: value, object or array expected.\n" ); "* Line 1, Column 15\n Syntax error: value, object or array "
"expected.\n");
std::vector<Json::Reader::StructuredError> errors = std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors(); reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.size() == 1);
@ -1581,17 +1516,14 @@ JSONTEST_FIXTURE( ReaderTest, parseWithOneError )
"Syntax error: value, object or array expected."); "Syntax error: value, object or array expected.");
} }
JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) {
JSONTEST_FIXTURE( ReaderTest, parseChineseWithOneError )
{
Json::Reader reader; Json::Reader reader;
Json::Value root; Json::Value root;
bool ok = reader.parse( bool ok = reader.parse("{ \"pr佐藤erty\" :: \"value\" }", root);
"{ \"pr佐藤erty\" :: \"value\" }",
root);
JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(!ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages() == JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 19\n Syntax error: value, object or array expected.\n" ); "* Line 1, Column 19\n Syntax error: value, object or array "
"expected.\n");
std::vector<Json::Reader::StructuredError> errors = std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors(); reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.size() == 1);
@ -1601,29 +1533,23 @@ JSONTEST_FIXTURE( ReaderTest, parseChineseWithOneError )
"Syntax error: value, object or array expected."); "Syntax error: value, object or array expected.");
} }
JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
JSONTEST_FIXTURE( ReaderTest, parseWithDetailError )
{
Json::Reader reader; Json::Reader reader;
Json::Value root; Json::Value root;
bool ok = reader.parse( bool ok = reader.parse("{ \"property\" : \"v\\alue\" }", root);
"{ \"property\" : \"v\\alue\" }",
root);
JSONTEST_ASSERT(!ok); JSONTEST_ASSERT(!ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages() == JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 16\n Bad escape sequence in string\nSee Line 1, Column 20 for detail.\n" ); "* Line 1, Column 16\n Bad escape sequence in string\nSee "
"Line 1, Column 20 for detail.\n");
std::vector<Json::Reader::StructuredError> errors = std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors(); reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1); JSONTEST_ASSERT(errors.size() == 1);
JSONTEST_ASSERT(errors.at(0).offset_start == 15); JSONTEST_ASSERT(errors.at(0).offset_start == 15);
JSONTEST_ASSERT(errors.at(0).offset_limit == 23); JSONTEST_ASSERT(errors.at(0).offset_limit == 23);
JSONTEST_ASSERT( errors.at(0).message == JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
"Bad escape sequence in string" );
} }
int main(int argc, const char *argv[]) {
int main( int argc, const char *argv[] )
{
JsonTest::Runner runner; JsonTest::Runner runner;
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, memberCount); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, memberCount);
@ -1647,7 +1573,8 @@ int main( int argc, const char *argv[] )
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors); JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
JSONTEST_REGISTER_FIXTURE( runner, ReaderTest, parseWithNoErrorsTestingOffsets ); JSONTEST_REGISTER_FIXTURE(
runner, ReaderTest, parseWithNoErrorsTestingOffsets);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithOneError); JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithOneError);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError); JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseChineseWithOneError);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError); JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithDetailError);