- 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).
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 @@
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 @@
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 @@
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 @@
\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 @@
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 @@
/// 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 @@
friend class Path;
PathArgument();
- PathArgument( UInt index );
+ PathArgument( ArrayIndex index );
PathArgument( const char *key );
PathArgument( const std::string &key );
@@ -466,7 +470,7 @@
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 @@
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 @@
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 @@
++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 @@
}
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 @@
}
+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 @@
// 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 @@
}
-int
+ArrayIndex
Value::CZString::index() const
{
return index_;
@@ -257,6 +257,30 @@
}
+#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 @@
#endif
{
value_.string_ = duplicateStringValue( beginValue,
- UInt(endValue - beginValue) );
+ (unsigned int)(endValue - beginValue) );
}
@@ -722,9 +746,13 @@
case nullValue:
return 0.0;
case intValue:
- return value_.int_;
+ return static_cast<double>( value_.int_ );
case uintValue:
- return value_.uint_;
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<double>( value_.uint_ );
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<double>( 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 @@
/// Number of values in array or object
-Value::UInt
+ArrayIndex
Value::size() const
{
switch ( type_ )
@@ -839,7 +867,7 @@
}
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 @@
}
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 &
-Value::operator[]( UInt index )
+Value::operator[]( ArrayIndex index )
{
JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
if ( type_ == nullValue )
@@ -941,7 +971,7 @@
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
-Value::get( UInt index,
+Value::get( ArrayIndex index,
const Value &defaultValue ) const
{
const Value *value = &((*this)[index]);
@@ -1000,7 +1030,7 @@
bool
-Value::isValidIndex( UInt index ) const
+Value::isValidIndex( ArrayIndex index ) const
{
return index < size();
}
@@ -1463,7 +1493,7 @@
}
-PathArgument::PathArgument( Value::UInt index )
+PathArgument::PathArgument( ArrayIndex index )
: index_( index )
, kind_( kindIndex )
{
@@ -1519,9 +1549,9 @@
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 @@
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( 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
+