Skip to content

Commit 7e98a3c

Browse files
laanwjjonasschnelli
authored andcommitted
util: Add ParseInt64 and ParseDouble functions
Strict parsing functions for other numeric types. - ParseInt64 analogous to ParseInt32, but for 64-bit values. - ParseDouble for doubles. - Make all three Parse* functions more strict (e.g. reject whitespace on the inside) Also add tests.
1 parent 043df2b commit 7e98a3c

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

src/test/util_tests.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,16 +322,81 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32)
322322
BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648);
323323
BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234);
324324
// Invalid values
325+
BOOST_CHECK(!ParseInt32("", &n));
326+
BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside
327+
BOOST_CHECK(!ParseInt32("1 ", &n));
325328
BOOST_CHECK(!ParseInt32("1a", &n));
326329
BOOST_CHECK(!ParseInt32("aap", &n));
327330
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
331+
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
332+
const char test_bytes[] = {'1', 0, '1'};
333+
std::string teststr(test_bytes, sizeof(test_bytes));
334+
BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs
328335
// Overflow and underflow
329336
BOOST_CHECK(!ParseInt32("-2147483649", NULL));
330337
BOOST_CHECK(!ParseInt32("2147483648", NULL));
331338
BOOST_CHECK(!ParseInt32("-32482348723847471234", NULL));
332339
BOOST_CHECK(!ParseInt32("32482348723847471234", NULL));
333340
}
334341

342+
BOOST_AUTO_TEST_CASE(test_ParseInt64)
343+
{
344+
int64_t n;
345+
// Valid values
346+
BOOST_CHECK(ParseInt64("1234", NULL));
347+
BOOST_CHECK(ParseInt64("0", &n) && n == 0LL);
348+
BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL);
349+
BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal
350+
BOOST_CHECK(ParseInt64("2147483647", &n) && n == 2147483647LL);
351+
BOOST_CHECK(ParseInt64("-2147483648", &n) && n == -2147483648LL);
352+
BOOST_CHECK(ParseInt64("9223372036854775807", &n) && n == 9223372036854775807LL);
353+
BOOST_CHECK(ParseInt64("-9223372036854775808", &n) && n == 9223372036854775808LL);
354+
BOOST_CHECK(ParseInt64("-1234", &n) && n == -1234LL);
355+
// Invalid values
356+
BOOST_CHECK(!ParseInt64("", &n));
357+
BOOST_CHECK(!ParseInt64(" 1", &n)); // no padding inside
358+
BOOST_CHECK(!ParseInt64("1 ", &n));
359+
BOOST_CHECK(!ParseInt64("1a", &n));
360+
BOOST_CHECK(!ParseInt64("aap", &n));
361+
BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex
362+
const char test_bytes[] = {'1', 0, '1'};
363+
std::string teststr(test_bytes, sizeof(test_bytes));
364+
BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs
365+
// Overflow and underflow
366+
BOOST_CHECK(!ParseInt64("-9223372036854775809", NULL));
367+
BOOST_CHECK(!ParseInt64("9223372036854775808", NULL));
368+
BOOST_CHECK(!ParseInt64("-32482348723847471234", NULL));
369+
BOOST_CHECK(!ParseInt64("32482348723847471234", NULL));
370+
}
371+
372+
BOOST_AUTO_TEST_CASE(test_ParseDouble)
373+
{
374+
double n;
375+
// Valid values
376+
BOOST_CHECK(ParseDouble("1234", NULL));
377+
BOOST_CHECK(ParseDouble("0", &n) && n == 0.0);
378+
BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0);
379+
BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal
380+
BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0);
381+
BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0);
382+
BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0);
383+
BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6);
384+
BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6);
385+
// Invalid values
386+
BOOST_CHECK(!ParseDouble("", &n));
387+
BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside
388+
BOOST_CHECK(!ParseDouble("1 ", &n));
389+
BOOST_CHECK(!ParseDouble("1a", &n));
390+
BOOST_CHECK(!ParseDouble("aap", &n));
391+
BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex
392+
const char test_bytes[] = {'1', 0, '1'};
393+
std::string teststr(test_bytes, sizeof(test_bytes));
394+
BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs
395+
// Overflow and underflow
396+
BOOST_CHECK(!ParseDouble("-1e10000", NULL));
397+
BOOST_CHECK(!ParseDouble("1e10000", NULL));
398+
}
399+
335400
BOOST_AUTO_TEST_CASE(test_FormatParagraph)
336401
{
337402
BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), "");

src/utilstrencodings.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,25 @@ string DecodeBase32(const string& str)
416416
return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size());
417417
}
418418

419+
static bool ParsePrechecks(const std::string& str)
420+
{
421+
if (str.empty()) // No empty string allowed
422+
return false;
423+
if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed
424+
return false;
425+
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
426+
return false;
427+
return true;
428+
}
429+
419430
bool ParseInt32(const std::string& str, int32_t *out)
420431
{
432+
if (!ParsePrechecks(str))
433+
return false;
421434
char *endp = NULL;
422435
errno = 0; // strtol will not set errno if valid
423436
long int n = strtol(str.c_str(), &endp, 10);
424-
if(out) *out = (int)n;
437+
if(out) *out = (int32_t)n;
425438
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
426439
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
427440
// platforms the size of these types may be different.
@@ -430,6 +443,34 @@ bool ParseInt32(const std::string& str, int32_t *out)
430443
n <= std::numeric_limits<int32_t>::max();
431444
}
432445

446+
bool ParseInt64(const std::string& str, int64_t *out)
447+
{
448+
if (!ParsePrechecks(str))
449+
return false;
450+
char *endp = NULL;
451+
errno = 0; // strtoll will not set errno if valid
452+
long long int n = strtoll(str.c_str(), &endp, 10);
453+
if(out) *out = (int64_t)n;
454+
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
455+
// we still have to check that the returned value is within the range of an *int64_t*.
456+
return endp && *endp == 0 && !errno &&
457+
n >= std::numeric_limits<int64_t>::min() &&
458+
n <= std::numeric_limits<int64_t>::max();
459+
}
460+
461+
bool ParseDouble(const std::string& str, double *out)
462+
{
463+
if (!ParsePrechecks(str))
464+
return false;
465+
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
466+
return false;
467+
char *endp = NULL;
468+
errno = 0; // strtod will not set errno if valid
469+
double n = strtod(str.c_str(), &endp);
470+
if(out) *out = n;
471+
return endp && *endp == 0 && !errno;
472+
}
473+
433474
std::string FormatParagraph(const std::string in, size_t width, size_t indent)
434475
{
435476
std::stringstream out;

src/utilstrencodings.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ int atoi(const std::string& str);
4949
*/
5050
bool ParseInt32(const std::string& str, int32_t *out);
5151

52+
/**
53+
* Convert string to signed 64-bit integer with strict parse error feedback.
54+
* @returns true if the entire string could be parsed as valid integer,
55+
* false if not the entire string could be parsed or when overflow or underflow occurred.
56+
*/
57+
bool ParseInt64(const std::string& str, int64_t *out);
58+
59+
/**
60+
* Convert string to double with strict parse error feedback.
61+
* @returns true if the entire string could be parsed as valid double,
62+
* false if not the entire string could be parsed or when overflow or underflow occurred.
63+
*/
64+
bool ParseDouble(const std::string& str, double *out);
65+
5266
template<typename T>
5367
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
5468
{

0 commit comments

Comments
 (0)