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.
This commit is contained in:
Wladimir J. van der Laan 2015-06-04 12:03:09 +02:00 committed by Jonas Schnelli
parent 043df2b568
commit 7e98a3c642
3 changed files with 121 additions and 1 deletions

View file

@ -322,9 +322,16 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32)
BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648); BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648);
BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234); BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234);
// Invalid values // Invalid values
BOOST_CHECK(!ParseInt32("", &n));
BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseInt32("1 ", &n));
BOOST_CHECK(!ParseInt32("1a", &n)); BOOST_CHECK(!ParseInt32("1a", &n));
BOOST_CHECK(!ParseInt32("aap", &n)); BOOST_CHECK(!ParseInt32("aap", &n));
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
const char test_bytes[] = {'1', 0, '1'};
std::string teststr(test_bytes, sizeof(test_bytes));
BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs
// Overflow and underflow // Overflow and underflow
BOOST_CHECK(!ParseInt32("-2147483649", NULL)); BOOST_CHECK(!ParseInt32("-2147483649", NULL));
BOOST_CHECK(!ParseInt32("2147483648", NULL)); BOOST_CHECK(!ParseInt32("2147483648", NULL));
@ -332,6 +339,64 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32)
BOOST_CHECK(!ParseInt32("32482348723847471234", NULL)); BOOST_CHECK(!ParseInt32("32482348723847471234", NULL));
} }
BOOST_AUTO_TEST_CASE(test_ParseInt64)
{
int64_t n;
// Valid values
BOOST_CHECK(ParseInt64("1234", NULL));
BOOST_CHECK(ParseInt64("0", &n) && n == 0LL);
BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL);
BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal
BOOST_CHECK(ParseInt64("2147483647", &n) && n == 2147483647LL);
BOOST_CHECK(ParseInt64("-2147483648", &n) && n == -2147483648LL);
BOOST_CHECK(ParseInt64("9223372036854775807", &n) && n == 9223372036854775807LL);
BOOST_CHECK(ParseInt64("-9223372036854775808", &n) && n == 9223372036854775808LL);
BOOST_CHECK(ParseInt64("-1234", &n) && n == -1234LL);
// Invalid values
BOOST_CHECK(!ParseInt64("", &n));
BOOST_CHECK(!ParseInt64(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseInt64("1 ", &n));
BOOST_CHECK(!ParseInt64("1a", &n));
BOOST_CHECK(!ParseInt64("aap", &n));
BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex
const char test_bytes[] = {'1', 0, '1'};
std::string teststr(test_bytes, sizeof(test_bytes));
BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs
// Overflow and underflow
BOOST_CHECK(!ParseInt64("-9223372036854775809", NULL));
BOOST_CHECK(!ParseInt64("9223372036854775808", NULL));
BOOST_CHECK(!ParseInt64("-32482348723847471234", NULL));
BOOST_CHECK(!ParseInt64("32482348723847471234", NULL));
}
BOOST_AUTO_TEST_CASE(test_ParseDouble)
{
double n;
// Valid values
BOOST_CHECK(ParseDouble("1234", NULL));
BOOST_CHECK(ParseDouble("0", &n) && n == 0.0);
BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0);
BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal
BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0);
BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0);
BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0);
BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6);
BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6);
// Invalid values
BOOST_CHECK(!ParseDouble("", &n));
BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseDouble("1 ", &n));
BOOST_CHECK(!ParseDouble("1a", &n));
BOOST_CHECK(!ParseDouble("aap", &n));
BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex
const char test_bytes[] = {'1', 0, '1'};
std::string teststr(test_bytes, sizeof(test_bytes));
BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs
// Overflow and underflow
BOOST_CHECK(!ParseDouble("-1e10000", NULL));
BOOST_CHECK(!ParseDouble("1e10000", NULL));
}
BOOST_AUTO_TEST_CASE(test_FormatParagraph) BOOST_AUTO_TEST_CASE(test_FormatParagraph)
{ {
BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), ""); BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), "");

View file

@ -416,12 +416,25 @@ string DecodeBase32(const string& str)
return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size()); return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size());
} }
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out) bool ParseInt32(const std::string& str, int32_t *out)
{ {
if (!ParsePrechecks(str))
return false;
char *endp = NULL; char *endp = NULL;
errno = 0; // strtol will not set errno if valid errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10); long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int)n; if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different. // platforms the size of these types may be different.
@ -430,6 +443,34 @@ bool ParseInt32(const std::string& str, int32_t *out)
n <= std::numeric_limits<int32_t>::max(); n <= std::numeric_limits<int32_t>::max();
} }
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
char *endp = NULL;
errno = 0; // strtod will not set errno if valid
double n = strtod(str.c_str(), &endp);
if(out) *out = n;
return endp && *endp == 0 && !errno;
}
std::string FormatParagraph(const std::string in, size_t width, size_t indent) std::string FormatParagraph(const std::string in, size_t width, size_t indent)
{ {
std::stringstream out; std::stringstream out;

View file

@ -49,6 +49,20 @@ int atoi(const std::string& str);
*/ */
bool ParseInt32(const std::string& str, int32_t *out); bool ParseInt32(const std::string& str, int32_t *out);
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseInt64(const std::string& str, int64_t *out);
/**
* Convert string to double with strict parse error feedback.
* @returns true if the entire string could be parsed as valid double,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseDouble(const std::string& str, double *out);
template<typename T> template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
{ {