From 842e9ac54bee5901e8d4efecaf75b69077bb5f4e Mon Sep 17 00:00:00 2001 From: Baptiste Lepilleur Date: Mon, 27 Dec 2010 17:45:23 +0000 Subject: [PATCH] Major rework of 64 integer support: 64 bits integer are only returned when explicitly request via Json::Value::asInt64(), unlike previous implementation where Json::Value::asInt() returned a 64 bits integer. This eases porting portable code and does not break compatibility with the previous release. Json::Value::asLargestInt() has also be added to ease writing portable code independent of 64 bits integer support. It is typically used to implement writers. --- NEWS.txt | 35 +++++++-- include/json/config.h | 18 +++-- include/json/value.h | 41 +++++++++-- include/json/writer.h | 4 ++ src/jsontestrunner/main.cpp | 66 +++++++++-------- src/lib_json/json_reader.cpp | 16 ++--- src/lib_json/json_tool.h | 4 +- src/lib_json/json_value.cpp | 136 +++++++++++++++++++++++++++++------ src/lib_json/json_writer.cpp | 35 ++++++--- 9 files changed, 266 insertions(+), 89 deletions(-) diff --git a/NEWS.txt b/NEWS.txt index ded333f..9924691 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -18,17 +18,38 @@ 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). + - Added support for 64 bits integer: + + Types Json::Int64 and Json::UInt64 have been added. They are aliased + to 64 bits integers on system that support them (based on __int64 on + Microsoft Visual Studio platform, and long long on other platforms). + + Types Json::LargestInt and Json::LargestUInt have been added. They are + aliased to the largest integer type supported: + either Json::Int/Json::UInt or Json::Int64/Json::UInt64 respectively. + + Json::Value::asInt() and Json::Value::asUInt() still returns plain + "int" based types, but asserts if an attempt is made to retrieve + a 64 bits value that can not represented as the return type. + + Json::Value::asInt64() and Json::Value::asUInt64() have been added + to obtain the 64 bits integer value. + + Json::Value::asLargestInt() and Json::Value::asLargestUInt() returns + the integer as a LargestInt/LargestUInt respectively. Those functions + functions are typically used when implementing writer. + + The reader attempts to read number as 64 bits integer, and fall back + to reading a double if the number is not in the range of 64 bits + integer. 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). + + Support for 64 bits integer can be disabled by defining the macro + JSON_NO_INT64 (uncomment it in json/config.h for example), though + it should have no impact on existing usage. - 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 3fe08f2..55f0583 100644 --- a/include/json/config.h +++ b/include/json/config.h @@ -46,7 +46,7 @@ # endif // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer -// Storages. +// Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 @@ -57,18 +57,24 @@ namespace Json { -# if defined(JSON_NO_INT64) typedef int Int; typedef unsigned int UInt; +# if defined(JSON_NO_INT64) + typedef int LargestInt; + typedef unsigned int LargestUInt; +# undef JSON_HAS_INT64 # 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; + typedef __int64 Int64; + typedef unsigned __int64 UInt64; # else // if defined(_MSC_VER) // Other platforms, use long long - typedef long long int Int; - typedef unsigned long long int UInt; + typedef long long int Int64; + typedef unsigned long long int UInt64; # endif // if defined(_MSC_VER) + typedef Int64 LargestInt; + typedef UInt64 LargestUInt; +# define JSON_HAS_INT64 # endif // if defined(JSON_NO_INT64) } // end namespace Json diff --git a/include/json/value.h b/include/json/value.h index 8d0d4c1..14464e4 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -126,13 +126,36 @@ namespace Json { typedef ValueConstIterator const_iterator; typedef Json::UInt UInt; typedef Json::Int Int; +# if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; typedef Json::ArrayIndex ArrayIndex; static const Value null; - static const Int minInt; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. static const UInt maxUInt; + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; + private: #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION # ifndef JSON_VALUE_USE_INTERNAL_MAP @@ -187,12 +210,12 @@ 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 ); +#if defined(JSON_HAS_INT64) + Value( Int64 value ); + Value( UInt64 value ); +#endif // if defined(JSON_HAS_INT64) Value( double value ); Value( const char *value ); Value( const char *beginValue, const char *endValue ); @@ -240,6 +263,10 @@ namespace Json { # endif Int asInt() const; UInt asUInt() const; + Int64 asInt64() const; + UInt64 asUInt64() const; + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; float asFloat() const; double asDouble() const; bool asBool() const; @@ -448,8 +475,8 @@ namespace Json { union ValueHolder { - Int int_; - UInt uint_; + LargestInt int_; + LargestUInt uint_; double real_; bool bool_; char *string_; diff --git a/include/json/writer.h b/include/json/writer.h index 4d74f93..2ee13de 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -162,8 +162,12 @@ namespace Json { bool addChildValues_; }; +# if defined(JSON_HAS_INT64) std::string JSON_API valueToString( Int value ); std::string JSON_API valueToString( UInt value ); +# endif // if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( LargestInt value ); + std::string JSON_API valueToString( LargestUInt value ); std::string JSON_API valueToString( double value ); std::string JSON_API valueToString( bool value ); std::string JSON_API valueToQuotedString( const char *value ); diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index 67344e0..2da3ede 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -44,10 +44,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=%s\n", path.c_str(), Json::valueToString( value.asInt() ).c_str() ); + fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() ); break; case Json::uintValue: - fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asUInt() ).c_str() ); + fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() ); break; case Json::realValue: fprintf( fout, "%s=%.16g\n", path.c_str(), value.asDouble() ); @@ -224,36 +224,44 @@ int main( int argc, const char *argv[] ) return exitCode; } - std::string input = readInputTestFile( path.c_str() ); - if ( input.empty() ) + try { - printf( "Failed to read input or empty input: %s\n", path.c_str() ); - return 3; - } - - std::string basePath = removeSuffix( argv[1], ".json" ); - if ( !parseOnly && basePath.empty() ) - { - printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() ); - return 3; - } - - std::string actualPath = basePath + ".actual"; - std::string rewritePath = basePath + ".rewrite"; - std::string rewriteActualPath = basePath + ".actual-rewrite"; - - Json::Value root; - exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly ); - if ( exitCode == 0 && !parseOnly ) - { - std::string rewrite; - exitCode = rewriteValueTree( rewritePath, root, rewrite ); - if ( exitCode == 0 ) + std::string input = readInputTestFile( path.c_str() ); + if ( input.empty() ) { - Json::Value rewriteRoot; - exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath, - "rewrite", rewriteRoot, features, parseOnly ); + printf( "Failed to read input or empty input: %s\n", path.c_str() ); + return 3; } + + std::string basePath = removeSuffix( argv[1], ".json" ); + if ( !parseOnly && basePath.empty() ) + { + printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() ); + return 3; + } + + std::string actualPath = basePath + ".actual"; + std::string rewritePath = basePath + ".rewrite"; + std::string rewriteActualPath = basePath + ".actual-rewrite"; + + Json::Value root; + exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly ); + if ( exitCode == 0 && !parseOnly ) + { + std::string rewrite; + exitCode = rewriteValueTree( rewritePath, root, rewrite ); + if ( exitCode == 0 ) + { + Json::Value rewriteRoot; + exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath, + "rewrite", rewriteRoot, features, parseOnly ); + } + } + } + catch ( const std::exception &e ) + { + printf( "Unhandled exception:\n%s\n", e.what() ); + exitCode = 1; } return exitCode; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index d2c255c..60dc4c9 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -567,12 +567,12 @@ Reader::decodeNumber( Token &token ) bool isNegative = *current == '-'; if ( isNegative ) ++current; - Value::UInt maxIntegerValue = isNegative ? Value::UInt(-Value::minInt) - : Value::maxUInt; - Value::UInt threshold = maxIntegerValue / 10; - Value::UInt lastDigitThreshold = maxIntegerValue % 10; + Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 ); assert( lastDigitThreshold >=0 && lastDigitThreshold <= 9 ); - Value::UInt value = 0; + Value::LargestUInt value = 0; while ( current < token.end_ ) { Char c = *current++; @@ -592,9 +592,9 @@ Reader::decodeNumber( Token &token ) value = value * 10 + digit; } if ( isNegative ) - currentValue() = -Value::Int( value ); - else if ( value <= Value::UInt(Value::maxInt) ) - currentValue() = Value::Int( value ); + currentValue() = -Value::LargestInt( value ); + else if ( value <= Value::LargestUInt(Value::maxInt) ) + currentValue() = Value::LargestInt( value ); else currentValue() = value; return true; diff --git a/src/lib_json/json_tool.h b/src/lib_json/json_tool.h index c20639d..658031b 100644 --- a/src/lib_json/json_tool.h +++ b/src/lib_json/json_tool.h @@ -63,7 +63,7 @@ isControlCharacter(char ch) enum { /// Constant that specify the size of the buffer that must be passed to uintToString. - uintToStringBufferSize = 3*sizeof(UInt)+1 + uintToStringBufferSize = 3*sizeof(LargestUInt)+1 }; // Defines a char buffer for use with uintToString(). @@ -76,7 +76,7 @@ typedef char UIntToStringBuffer[uintToStringBufferSize]; * Must have at least uintToStringBufferSize chars free. */ static inline void -uintToString( UInt value, +uintToString( LargestUInt value, char *¤t ) { *--current = 0; diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 15a1140..218c127 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -28,6 +28,13 @@ const Value Value::null; const Int Value::minInt = Int( ~(UInt(-1)/2) ); const Int Value::maxInt = Int( UInt(-1)/2 ); const UInt Value::maxUInt = UInt(-1); +const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); +const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); +const UInt64 Value::maxUInt64 = UInt64(-1); +const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); +const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + /// Unknown size marker enum { unknown = (unsigned)-1 }; @@ -262,8 +269,8 @@ Value::Value( ValueType type ) } -#if !defined(JSON_NO_INT64) -Value::Value( ArrayIndex value ) +#if defined(JSON_HAS_INT64) +Value::Value( UInt value ) : type_( uintValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP @@ -273,19 +280,6 @@ Value::Value( ArrayIndex value ) 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 ) @@ -296,8 +290,21 @@ Value::Value( Int value ) value_.int_ = value; } +#endif // if defined(JSON_HAS_INT64) -Value::Value( UInt value ) + +Value::Value( Int64 value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + + +Value::Value( UInt64 value ) : type_( uintValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP @@ -689,6 +696,7 @@ Value::asConstString() const } # endif + Value::Int Value::asInt() const { @@ -697,10 +705,11 @@ Value::asInt() const case nullValue: return 0; case intValue: - return value_.int_; + JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" ); + return Int(value_.int_); case uintValue: - JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); - return value_.uint_; + JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" ); + return Int(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); return Int( value_.real_ ); @@ -716,6 +725,7 @@ Value::asInt() const return 0; // unreachable; } + Value::UInt Value::asUInt() const { @@ -725,9 +735,11 @@ Value::asUInt() const return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); - return value_.int_; + JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" ); + return UInt(value_.int_); case uintValue: - return value_.uint_; + JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" ); + return UInt(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); return UInt( value_.real_ ); @@ -743,6 +755,88 @@ Value::asUInt() const return 0; // unreachable; } + +# if defined(JSON_HAS_INT64) + +Value::Int64 +Value::asInt64() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" ); + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" ); + return Int( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to Int64" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +Value::UInt64 +Value::asUInt64() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" ); + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" ); + return UInt( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to UInt64" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} +# endif // if defined(JSON_HAS_INT64) + + +LargestInt +Value::asLargestInt() const +{ +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + + +LargestUInt +Value::asLargestUInt() const +{ +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + + double Value::asDouble() const { diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 7882acf..f101cbc 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -29,14 +29,15 @@ static bool containsControlCharacter( const char* str ) return false; } -std::string valueToString( Int value ) + +std::string valueToString( LargestInt value ) { UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); bool isNegative = value < 0; if ( isNegative ) value = -value; - uintToString( UInt(value), current ); + uintToString( LargestUInt(value), current ); if ( isNegative ) *--current = '-'; assert( current >= buffer ); @@ -44,7 +45,7 @@ std::string valueToString( Int value ) } -std::string valueToString( UInt value ) +std::string valueToString( LargestUInt value ) { UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); @@ -53,6 +54,22 @@ std::string valueToString( UInt value ) return current; } +#if defined(JSON_HAS_INT64) + +std::string valueToString( Int value ) +{ + return valueToString( LargestInt(value) ); +} + + +std::string valueToString( UInt value ) +{ + return valueToString( LargestUInt(value) ); +} + +#endif // # if defined(JSON_HAS_INT64) + + std::string valueToString( double value ) { char buffer[32]; @@ -203,10 +220,10 @@ FastWriter::writeValue( const Value &value ) document_ += "null"; break; case intValue: - document_ += valueToString( value.asInt() ); + document_ += valueToString( value.asLargestInt() ); break; case uintValue: - document_ += valueToString( value.asUInt() ); + document_ += valueToString( value.asLargestUInt() ); break; case realValue: document_ += valueToString( value.asDouble() ); @@ -286,10 +303,10 @@ StyledWriter::writeValue( const Value &value ) pushValue( "null" ); break; case intValue: - pushValue( valueToString( value.asInt() ) ); + pushValue( valueToString( value.asLargestInt() ) ); break; case uintValue: - pushValue( valueToString( value.asUInt() ) ); + pushValue( valueToString( value.asLargestUInt() ) ); break; case realValue: pushValue( valueToString( value.asDouble() ) ); @@ -562,10 +579,10 @@ StyledStreamWriter::writeValue( const Value &value ) pushValue( "null" ); break; case intValue: - pushValue( valueToString( value.asInt() ) ); + pushValue( valueToString( value.asLargestInt() ) ); break; case uintValue: - pushValue( valueToString( value.asUInt() ) ); + pushValue( valueToString( value.asLargestUInt() ) ); break; case realValue: pushValue( valueToString( value.asDouble() ) );