From 201fb2cf0d4f7fa3014d3156d8b0c4fee4bd9fd5 Mon Sep 17 00:00:00 2001 From: Baptiste Lepilleur Date: Mon, 19 Apr 2010 07:37:41 +0000 Subject: [PATCH] - Moved definition of Json::Int and Json::UInt to config.h which compiler detection logic to define them to 64 bits integer if JSON_NO_INT64 is not defined. - Added Json::ArrayIndex as an unsigned int to forwards.h - Modified Json::Value to consistently use Json::ArrayIndex. - Added int/unsigned int constructor overload to Json::Value to avoid ambiguous constructor call. - Modified jsontestrunner/main.cpp to use Json::valueToString for Value::asInt() conversion to string. - Modified Json::Reader to only overflow to double when the number is too large (previous code relied on the fact that an int fitted in a double without precision loss). - Generalized uintToString() helpers and buffer size to automatically adapt to the precision of Json::UInt. - Added specific conversion logic for UInt to double conversion on Microsoft Visual Studio 6 which only support __int64 to double conversion (unsigned __int64 conversion is not supported) - Added test for 64 bits parsing/writing. Notes: those will fail when compiled with JSON_NO_INT64 (more dev required to adapt). --- NEWS.txt | 15 +++++- include/json/config.h | 28 ++++++++++ include/json/forwards.h | 3 +- include/json/value.h | 28 +++++----- src/jsontestrunner/main.cpp | 23 +++++++- src/lib_json/json_reader.cpp | 23 ++++++-- src/lib_json/json_tool.h | 16 ++++-- src/lib_json/json_value.cpp | 64 +++++++++++++++++------ src/lib_json/json_writer.cpp | 4 +- test/data/test_integer_06_64bits.expected | 1 + test/data/test_integer_06_64bits.json | 2 + test/data/test_integer_07_64bits.expected | 1 + test/data/test_integer_07_64bits.json | 2 + test/data/test_integer_08_64bits.expected | 1 + test/data/test_integer_08_64bits.json | 2 + 15 files changed, 170 insertions(+), 43 deletions(-) create mode 100644 test/data/test_integer_06_64bits.expected create mode 100644 test/data/test_integer_06_64bits.json create mode 100644 test/data/test_integer_07_64bits.expected create mode 100644 test/data/test_integer_07_64bits.json create mode 100644 test/data/test_integer_08_64bits.expected create mode 100644 test/data/test_integer_08_64bits.json diff --git a/NEWS.txt b/NEWS.txt index b5e8cf6..50116aa 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -18,4 +18,17 @@ initialization/destruction order issues (bug #2934500). The DefaultValueAllocator has been inlined in code. - + - Added support for 64 bits integer. Json::Int and Json::UInt are + now 64 bits integers on system that support them (more precisely + they are of the size of long long, so if it is 128 bits it will + also work). + + Warning: Json::Value::asInt() and Json::Value::asUInt() now returns + long long. This changes break code that was passing the return value + to *printf() function. + + Notes: you can switch back to the 32 bits only behavior by defining the + macro JSON_NO_INT64 (se include/json/config.h). + + - The type Json::ArrayIndex is used for indexes of a JSON value array. It + is an unsigned int (typically 32 bits). diff --git a/include/json/config.h b/include/json/config.h index 5d334cb..a0fed8a 100644 --- a/include/json/config.h +++ b/include/json/config.h @@ -40,4 +40,32 @@ # define JSON_API # endif +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer +// Storages. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + + +namespace Json { +# if defined(JSON_NO_INT64) + typedef int Int; + typedef unsigned int UInt; +# else // if defined(JSON_NO_INT64) + // For Microsoft Visual use specific types as long long is not supported +# if defined(_MSC_VER) // Microsoft Visual Studio + typedef __int64 Int; + typedef unsigned __int64 UInt; +# else // if defined(_MSC_VER) // Other platforms, use long long + typedef long long int Int; + typedef unsigned long long int UInt; +# endif // if defined(_MSC_VER) +# endif // if defined(JSON_NO_INT64) +} // end namespace Json + + #endif // JSON_CONFIG_H_INCLUDED diff --git a/include/json/forwards.h b/include/json/forwards.h index 815075e..3a10a3b 100644 --- a/include/json/forwards.h +++ b/include/json/forwards.h @@ -16,8 +16,7 @@ namespace Json { class Features; // value.h - typedef int Int; - typedef unsigned int UInt; + typedef unsigned int ArrayIndex; class StaticString; class Path; class PathArgument; diff --git a/include/json/value.h b/include/json/value.h index 5d1bc81..f7b9c34 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -121,7 +121,7 @@ namespace Json { typedef ValueConstIterator const_iterator; typedef Json::UInt UInt; typedef Json::Int Int; - typedef UInt ArrayIndex; + typedef Json::ArrayIndex ArrayIndex; static const Value null; static const Int minInt; @@ -140,20 +140,20 @@ namespace Json { duplicate, duplicateOnCopy }; - CZString( int index ); + CZString( ArrayIndex index ); CZString( const char *cstr, DuplicationPolicy allocate ); CZString( const CZString &other ); ~CZString(); CZString &operator =( const CZString &other ); bool operator<( const CZString &other ) const; bool operator==( const CZString &other ) const; - int index() const; + ArrayIndex index() const; const char *c_str() const; bool isStaticString() const; private: void swap( CZString &other ); const char *cstr_; - int index_; + ArrayIndex index_; }; public: @@ -182,6 +182,10 @@ namespace Json { \endcode */ Value( ValueType type = nullValue ); +#if !defined(JSON_NO_INT64) + Value( int value ); + Value( ArrayIndex value ); +#endif // if !defined(JSON_NO_INT64) Value( Int value ); Value( UInt value ); Value( double value ); @@ -248,7 +252,7 @@ namespace Json { bool isConvertibleTo( ValueType other ) const; /// Number of values in array or object - UInt size() const; + ArrayIndex size() const; /// \brief Return true if empty array, empty object, or null; /// otherwise, false. @@ -267,24 +271,24 @@ namespace Json { /// May only be called on nullValue or arrayValue. /// \pre type() is arrayValue or nullValue /// \post type() is arrayValue - void resize( UInt size ); + void resize( ArrayIndex size ); /// Access an array element (zero based index ). /// If the array contains less than index element, then null value are inserted /// in the array so that its size is index+1. /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) - Value &operator[]( UInt index ); + Value &operator[]( ArrayIndex index ); /// Access an array element (zero based index ) /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) - const Value &operator[]( UInt index ) const; + const Value &operator[]( ArrayIndex index ) const; /// If the array contains at least index+1 elements, returns the element value, /// otherwise returns defaultValue. - Value get( UInt index, + Value get( ArrayIndex index, const Value &defaultValue ) const; /// Return true if index < size(). - bool isValidIndex( UInt index ) const; + bool isValidIndex( ArrayIndex index ) const; /// \brief Append value to array at the end. /// /// Equivalent to jsonvalue[jsonvalue.size()] = value; @@ -454,7 +458,7 @@ namespace Json { friend class Path; PathArgument(); - PathArgument( UInt index ); + PathArgument( ArrayIndex index ); PathArgument( const char *key ); PathArgument( const std::string &key ); @@ -466,7 +470,7 @@ namespace Json { kindKey }; std::string key_; - UInt index_; + ArrayIndex index_; Kind kind_; }; diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 231ee0c..3e6cd5d 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -35,10 +35,10 @@ printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." ) fprintf( fout, "%s=null\n", path.c_str() ); break; case Json::intValue: - fprintf( fout, "%s=%d\n", path.c_str(), value.asInt() ); + fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asInt() ).c_str() ); break; case Json::uintValue: - fprintf( fout, "%s=%u\n", path.c_str(), value.asUInt() ); + fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asUInt() ).c_str() ); break; case Json::realValue: fprintf( fout, "%s=%.16g\n", path.c_str(), value.asDouble() ); @@ -148,6 +148,19 @@ removeSuffix( const std::string &path, return path.substr( 0, path.length() - extension.length() ); } + +static void +printConfig() +{ + // Print the configuration used to compile JsonCpp +#if defined(JSON_NO_INT64) + printf( "JSON_NO_INT64=1\n" ); +#else + printf( "JSON_NO_INT64=0\n" ); +#endif +} + + static int printUsage( const char *argv[] ) { @@ -175,6 +188,12 @@ parseCommandLine( int argc, const char *argv[], ++index; } + if ( std::string(argv[1]) == "--json-config" ) + { + printConfig(); + return 3; + } + if ( index == argc || index + 1 < argc ) { return printUsage( argv ); diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index da8e83b..2bd38f0 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -555,21 +555,36 @@ Reader::decodeNumber( Token &token ) } if ( isDouble ) return decodeDouble( token ); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. Location current = token.start_; bool isNegative = *current == '-'; if ( isNegative ) ++current; - Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) - : Value::maxUInt) / 10; + Value::UInt maxIntegerValue = isNegative ? Value::UInt(-Value::minInt) + : Value::maxUInt; + Value::UInt threshold = maxIntegerValue / 10; + Value::UInt lastDigitThreshold = maxIntegerValue % 10; + assert( lastDigitThreshold >=0 && lastDigitThreshold <= 9 ); Value::UInt value = 0; while ( current < token.end_ ) { Char c = *current++; if ( c < '0' || c > '9' ) return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + Value::UInt digit(c - '0'); if ( value >= threshold ) - return decodeDouble( token ); - value = value * 10 + Value::UInt(c - '0'); + { + // If the current digit is not the last one, or if it is + // greater than the last digit of the maximum integer value, + // the parse the number as a double. + if ( current != token.end_ || digit > lastDigitThreshold ) + { + return decodeDouble( token ); + } + } + value = value * 10 + digit; } if ( isNegative ) currentValue() = -Value::Int( value ); diff --git a/src/lib_json/json_tool.h b/src/lib_json/json_tool.h index ca4ea4f..5ffc2de 100644 --- a/src/lib_json/json_tool.h +++ b/src/lib_json/json_tool.h @@ -56,18 +56,28 @@ isControlCharacter(char ch) } +enum { + /// Constant that specify the size of the buffer that must be passed to uintToString. + uintToStringBufferSize = 3*sizeof(UInt)+1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + + /** Converts an unsigned integer to string. * @param value Unsigned interger to convert to string - * @param current Input/Output string buffer. Must have at least 10 chars free. + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. */ static inline void -uintToString( unsigned int value, +uintToString( UInt value, char *¤t ) { *--current = 0; do { - *--current = (value % 10) + '0'; + *--current = char(value % 10) + '0'; value /= 10; } while ( value != 0 ); diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 1ccf70f..60362ad 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -119,7 +119,7 @@ Value::CommentInfo::setComment( const char *text ) // Notes: index_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString( int index ) +Value::CZString::CZString( ArrayIndex index ) : cstr_( 0 ) , index_( index ) { @@ -179,7 +179,7 @@ Value::CZString::operator==( const CZString &other ) const } -int +ArrayIndex Value::CZString::index() const { return index_; @@ -257,6 +257,30 @@ Value::Value( ValueType type ) } +#if !defined(JSON_NO_INT64) +Value::Value( ArrayIndex value ) + : type_( uintValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value( int value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + +#endif // if !defined(JSON_NO_INT64) + + Value::Value( Int value ) : type_( intValue ) , comments_( 0 ) @@ -310,7 +334,7 @@ Value::Value( const char *beginValue, #endif { value_.string_ = duplicateStringValue( beginValue, - UInt(endValue - beginValue) ); + (unsigned int)(endValue - beginValue) ); } @@ -722,9 +746,13 @@ Value::asDouble() const case nullValue: return 0.0; case intValue: - return value_.int_; + return static_cast( value_.int_ ); case uintValue: - return value_.uint_; +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return value_.real_; case booleanValue: @@ -817,7 +845,7 @@ Value::isConvertibleTo( ValueType other ) const /// Number of values in array or object -Value::UInt +ArrayIndex Value::size() const { switch ( type_ ) @@ -839,7 +867,7 @@ Value::size() const } return 0; case objectValue: - return Int( value_.map_->size() ); + return ArrayIndex( value_.map_->size() ); #else case arrayValue: return Int( value_.array_->size() ); @@ -896,21 +924,23 @@ Value::clear() } void -Value::resize( UInt newSize ) +Value::resize( ArrayIndex newSize ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) *this = Value( arrayValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP - UInt oldSize = size(); + ArrayIndex oldSize = size(); if ( newSize == 0 ) clear(); else if ( newSize > oldSize ) (*this)[ newSize - 1 ]; else { - for ( UInt index = newSize; index < oldSize; ++index ) + for ( ArrayIndex index = newSize; index < oldSize; ++index ) + { value_.map_->erase( index ); + } assert( size() == newSize ); } #else @@ -920,7 +950,7 @@ Value::resize( UInt newSize ) Value & -Value::operator[]( UInt index ) +Value::operator[]( ArrayIndex index ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) @@ -941,7 +971,7 @@ Value::operator[]( UInt index ) const Value & -Value::operator[]( UInt index ) const +Value::operator[]( ArrayIndex index ) const { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) @@ -991,7 +1021,7 @@ Value::resolveReference( const char *key, Value -Value::get( UInt index, +Value::get( ArrayIndex index, const Value &defaultValue ) const { const Value *value = &((*this)[index]); @@ -1000,7 +1030,7 @@ Value::get( UInt index, bool -Value::isValidIndex( UInt index ) const +Value::isValidIndex( ArrayIndex index ) const { return index < size(); } @@ -1463,7 +1493,7 @@ PathArgument::PathArgument() } -PathArgument::PathArgument( Value::UInt index ) +PathArgument::PathArgument( ArrayIndex index ) : index_( index ) , kind_( kindIndex ) { @@ -1519,9 +1549,9 @@ Path::makePath( const std::string &path, addPathInArg( path, in, itInArg, PathArgument::kindIndex ); else { - Value::UInt index = 0; + ArrayIndex index = 0; for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) - index = index * 10 + Value::UInt(*current - '0'); + index = index * 10 + ArrayIndex(*current - '0'); args_.push_back( index ); } if ( current == end || *current++ != ']' ) diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 3b926e6..92782db 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -26,7 +26,7 @@ static bool containsControlCharacter( const char* str ) std::string valueToString( Int value ) { - char buffer[32]; + UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); bool isNegative = value < 0; if ( isNegative ) @@ -41,7 +41,7 @@ std::string valueToString( Int value ) std::string valueToString( UInt value ) { - char buffer[32]; + UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); uintToString( value, current ); assert( current >= buffer ); diff --git a/test/data/test_integer_06_64bits.expected b/test/data/test_integer_06_64bits.expected new file mode 100644 index 0000000..bc9520a --- /dev/null +++ b/test/data/test_integer_06_64bits.expected @@ -0,0 +1 @@ +.=9223372036854775808 diff --git a/test/data/test_integer_06_64bits.json b/test/data/test_integer_06_64bits.json new file mode 100644 index 0000000..360d660 --- /dev/null +++ b/test/data/test_integer_06_64bits.json @@ -0,0 +1,2 @@ +9223372036854775808 + diff --git a/test/data/test_integer_07_64bits.expected b/test/data/test_integer_07_64bits.expected new file mode 100644 index 0000000..39eb798 --- /dev/null +++ b/test/data/test_integer_07_64bits.expected @@ -0,0 +1 @@ +.=-9223372036854775808 diff --git a/test/data/test_integer_07_64bits.json b/test/data/test_integer_07_64bits.json new file mode 100644 index 0000000..11d8513 --- /dev/null +++ b/test/data/test_integer_07_64bits.json @@ -0,0 +1,2 @@ +-9223372036854775808 + diff --git a/test/data/test_integer_08_64bits.expected b/test/data/test_integer_08_64bits.expected new file mode 100644 index 0000000..831f432 --- /dev/null +++ b/test/data/test_integer_08_64bits.expected @@ -0,0 +1 @@ +.=18446744073709551615 diff --git a/test/data/test_integer_08_64bits.json b/test/data/test_integer_08_64bits.json new file mode 100644 index 0000000..6e1fb04 --- /dev/null +++ b/test/data/test_integer_08_64bits.json @@ -0,0 +1,2 @@ +18446744073709551615 +