Skip to content

Commit 13a1a0b

Browse files
author
Mike Naquin
committed
Fix JSON parsing of large unsigned 64-bit integers
1 parent ae26e45 commit 13a1a0b

7 files changed

Lines changed: 152 additions & 17 deletions

File tree

Foundation/include/Poco/Token.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,17 @@ class Foundation_API Token
108108
#if defined(POCO_HAVE_INT64)
109109
virtual Int64 asInteger64() const;
110110
/// Returns a 64-bit integer representation of the token.
111+
112+
virtual UInt64 asUnsignedInteger64() const;
113+
/// Returns an unsigned 64-bit integer representation of the token.
111114
#endif
112115

113116
virtual int asInteger() const;
114117
/// Returns an integer representation of the token.
115-
118+
119+
virtual unsigned asUnsignedInteger() const;
120+
/// Returns an unsigned integer representation of the token.
121+
116122
virtual double asFloat() const;
117123
/// Returns a floating-point representation of the token.
118124

Foundation/src/Token.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ Int64 Token::asInteger64() const
8181
{
8282
return NumberParser::parse64(_value);
8383
}
84+
85+
86+
UInt64 Token::asUnsignedInteger64() const
87+
{
88+
return NumberParser::parseUnsigned64(_value);
89+
}
8490
#endif
8591

8692

@@ -90,6 +96,12 @@ int Token::asInteger() const
9096
}
9197

9298

99+
unsigned Token::asUnsignedInteger() const
100+
{
101+
return NumberParser::parseUnsigned(_value);
102+
}
103+
104+
93105
double Token::asFloat() const
94106
{
95107
return NumberParser::parseFloat(_value);

JSON/include/Poco/JSON/DefaultHandler.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,17 @@ class JSON_API DefaultHandler : public Handler
8282
virtual void value(int v);
8383
/// An integer value is read
8484

85+
virtual void value(unsigned v);
86+
/// An unsigned value is read. This will only be triggered if the
87+
/// value cannot fit into a signed int.
88+
8589
#if defined(POCO_HAVE_INT64)
8690
virtual void value(Int64 v);
8791
/// A 64-bit integer value is read
92+
93+
virtual void value(UInt64 v);
94+
/// An unsigned 64-bit integer value is read. This will only be
95+
/// triggered if the value cannot fit into a signed 64-bit integer.
8896
#endif
8997

9098
virtual void value(const std::string& s);
@@ -120,11 +128,23 @@ inline void DefaultHandler::value(int v)
120128
}
121129

122130

131+
inline void DefaultHandler::value(unsigned v)
132+
{
133+
setValue(v);
134+
}
135+
136+
123137
#if defined(POCO_HAVE_INT64)
124138
inline void DefaultHandler::value(Int64 v)
125139
{
126140
setValue(v);
127141
}
142+
143+
144+
inline void DefaultHandler::value(UInt64 v)
145+
{
146+
setValue(v);
147+
}
128148
#endif
129149

130150

JSON/include/Poco/JSON/Handler.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,18 @@ class JSON_API Handler
7171

7272
virtual void value(int v) = 0;
7373
/// An integer value is read
74-
74+
75+
virtual void value(unsigned v) = 0;
76+
/// An unsigned value is read. This will only be triggered if the
77+
/// value cannot fit into a signed int.
78+
7579
#if defined(POCO_HAVE_INT64)
7680
virtual void value(Int64 v) = 0;
7781
/// A 64-bit integer value is read
82+
83+
virtual void value(UInt64 v) = 0;
84+
/// An unsigned 64-bit integer value is read. This will only be
85+
/// triggered if the value cannot fit into a signed 64-bit integer.
7886
#endif
7987

8088
virtual void value(const std::string& value) = 0;

JSON/src/Parser.cpp

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -636,20 +636,46 @@ void Parser::readValue(const Token* token)
636636
if (_handler != NULL)
637637
{
638638
#if defined(POCO_HAVE_INT64)
639-
Int64 value = token->asInteger64();
640-
// if number is 32-bit, then handle as such
641-
if (value > std::numeric_limits<int>::max()
642-
|| value < std::numeric_limits<int>::min())
643-
{
644-
_handler->value(value);
645-
}
646-
else
647-
{
648-
_handler->value(static_cast<int>(value));
649-
}
639+
try
640+
{
641+
Int64 value = token->asInteger64();
642+
// if number is 32-bit, then handle as such
643+
if ( value > std::numeric_limits<int>::max()
644+
|| value < std::numeric_limits<int>::min() )
645+
{
646+
_handler->value(value);
647+
}
648+
else
649+
{
650+
_handler->value(static_cast<int>(value));
651+
}
652+
}
653+
// try to handle error as unsigned in case of overflow
654+
catch ( const SyntaxException& )
655+
{
656+
UInt64 value = token->asUnsignedInteger64();
657+
// if number is 32-bit, then handle as such
658+
if ( value > std::numeric_limits<unsigned>::max() )
659+
{
660+
_handler->value(value);
661+
}
662+
else
663+
{
664+
_handler->value(static_cast<unsigned>(value));
665+
}
666+
}
650667
#else
651-
int value = token->asInteger();
652-
_handle->value(value);
668+
try
669+
{
670+
int value = token->asInteger();
671+
_handle->value(value);
672+
}
673+
// try to handle error as unsigned in case of overflow
674+
catch ( const SyntaxException& )
675+
{
676+
unsigned value = token->asUnsignedInteger();
677+
_handle->value(value);
678+
}
653679
#endif
654680
}
655681
break;

JSON/testsuite/src/JSONTest.cpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,42 @@ void JSONTest::testNumberProperty()
192192
assert(value == 1969);
193193
}
194194

195+
196+
void JSONTest::testUnsignedNumberProperty()
197+
{
198+
// 4294967295 == unsigned(-1)
199+
std::string json = "{ \"test\" : 4294967295 }";
200+
Parser parser;
201+
Var result;
202+
203+
try
204+
{
205+
DefaultHandler handler;
206+
parser.setHandler(&handler);
207+
parser.parse(json);
208+
result = handler.result();
209+
}
210+
catch(JSONException& jsone)
211+
{
212+
std::cout << jsone.message() << std::endl;
213+
assert(false);
214+
}
215+
216+
assert(result.type() == typeid(Object::Ptr));
217+
218+
Object::Ptr object = result.extract<Object::Ptr>();
219+
Var test = object->get("test");
220+
assert(test.isInteger());
221+
unsigned value = test;
222+
assert(value == -1);
223+
}
224+
195225
#if defined(POCO_HAVE_INT64)
196226

197227

198228
void JSONTest::testNumber64Property()
199229
{
200-
std::string json = "{ \"test\" : 5000000000000000 }";
230+
std::string json = "{ \"test\" : -5000000000000000 }";
201231
Parser parser;
202232
Var result;
203233

@@ -220,10 +250,39 @@ void JSONTest::testNumber64Property()
220250
Var test = object->get("test");
221251
assert(test.isInteger());
222252
Poco::Int64 value = test;
223-
assert(value == 5000000000000000);
253+
assert(value == -5000000000000000);
224254
}
225255

226256

257+
void JSONTest::testUnsignedNumber64Property()
258+
{
259+
// 18446744073709551615 == UInt64(-1)
260+
std::string json = "{ \"test\" : 18446744073709551615 }";
261+
Parser parser;
262+
Var result;
263+
264+
try
265+
{
266+
DefaultHandler handler;
267+
parser.setHandler(&handler);
268+
parser.parse(json);
269+
result = handler.result();
270+
}
271+
catch(JSONException& jsone)
272+
{
273+
std::cout << jsone.message() << std::endl;
274+
assert(false);
275+
}
276+
277+
assert(result.type() == typeid(Object::Ptr));
278+
279+
Object::Ptr object = result.extract<Object::Ptr>();
280+
Var test = object->get("test");
281+
assert(test.isInteger());
282+
Poco::UInt64 value = test;
283+
assert(value == -1);
284+
}
285+
227286
#endif
228287

229288

@@ -962,8 +1021,10 @@ CppUnit::Test* JSONTest::suite()
9621021
CppUnit_addTest(pSuite, JSONTest, testTrueProperty);
9631022
CppUnit_addTest(pSuite, JSONTest, testFalseProperty);
9641023
CppUnit_addTest(pSuite, JSONTest, testNumberProperty);
1024+
CppUnit_addTest(pSuite, JSONTest, testUnsignedNumberProperty);
9651025
#if defined(POCO_HAVE_INT64)
9661026
CppUnit_addTest(pSuite, JSONTest, testNumber64Property);
1027+
CppUnit_addTest(pSuite, JSONTest, testUnsignedNumber64Property);
9671028
#endif
9681029
CppUnit_addTest(pSuite, JSONTest, testStringProperty);
9691030
CppUnit_addTest(pSuite, JSONTest, testEmptyObject);

JSON/testsuite/src/JSONTest.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ class JSONTest: public CppUnit::TestCase
5050
void testTrueProperty();
5151
void testFalseProperty();
5252
void testNumberProperty();
53+
void testUnsignedNumberProperty();
5354
#if defined(POCO_HAVE_INT64)
5455
void testNumber64Property();
56+
void testUnsignedNumber64Property();
5557
#endif
5658
void testStringProperty();
5759
void testEmptyObject();

0 commit comments

Comments
 (0)