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.
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 @@
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 @@
\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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 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 @@
}
-#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_.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_.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 @@
}
# endif
+
Value::Int
Value::asInt() const
{
@@ -697,10 +705,11 @@
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 @@
return 0; // unreachable;
}
+
Value::UInt
Value::asUInt() const
{
@@ -725,9 +735,11 @@
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 @@
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 @@
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( UInt value )
+std::string valueToString( LargestUInt value )
{
UIntToStringBuffer buffer;
char *current = buffer + sizeof(buffer);
@@ -53,6 +54,22 @@
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 @@
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 @@
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 @@
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() ) );