Commit 8c74c806 authored by Lucian Grijincu's avatar Lucian Grijincu Committed by woo

folly: to: make exceptions more informative

Summary: print the value which failed conversion to aid debugging.

Test Plan: run code which throws in to<> and contbuild

Reviewed By: tudorb@fb.com, andrei.alexandrescu@fb.com

Subscribers: trunkagent, folly-diffs@

FB internal diff: D1786294

Signature: t1:1786294:1421795912:00cc10923990e5aac0a15eedec09deb8e8d470d1
parent ca71b787
...@@ -71,8 +71,8 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) { ...@@ -71,8 +71,8 @@ inline bool bool_str_cmp(const char** b, size_t len, const char* value) {
bool str_to_bool(StringPiece* src) { bool str_to_bool(StringPiece* src) {
auto b = src->begin(), e = src->end(); auto b = src->begin(), e = src->end();
for (;; ++b) { for (;; ++b) {
FOLLY_RANGE_CHECK(b < e, FOLLY_RANGE_CHECK_STRINGPIECE(
"No non-whitespace characters found in input string"); b < e, "No non-whitespace characters found in input string", *src);
if (!isspace(*b)) break; if (!isspace(*b)) break;
} }
...@@ -85,8 +85,8 @@ bool str_to_bool(StringPiece* src) { ...@@ -85,8 +85,8 @@ bool str_to_bool(StringPiece* src) {
StringPiece tmp(*src); StringPiece tmp(*src);
uint8_t value = to<uint8_t>(&tmp); uint8_t value = to<uint8_t>(&tmp);
// Only accept 0 or 1 // Only accept 0 or 1
FOLLY_RANGE_CHECK(value <= 1, FOLLY_RANGE_CHECK_STRINGPIECE(
"Integer overflow when parsing bool: must be 0 or 1"); value <= 1, "Integer overflow when parsing bool: must be 0 or 1", *src);
b = tmp.begin(); b = tmp.begin();
result = (value == 1); result = (value == 1);
break; break;
...@@ -126,11 +126,11 @@ bool str_to_bool(StringPiece* src) { ...@@ -126,11 +126,11 @@ bool str_to_bool(StringPiece* src) {
} else if (bool_str_cmp(&b, len, "off")) { } else if (bool_str_cmp(&b, len, "off")) {
result = false; result = false;
} else { } else {
FOLLY_RANGE_CHECK(false, "Invalid value for bool"); FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
} }
break; break;
default: default:
FOLLY_RANGE_CHECK(false, "Invalid value for bool"); FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
} }
src->assign(b, e); src->assign(b, e);
......
...@@ -46,10 +46,16 @@ ...@@ -46,10 +46,16 @@
#define FOLLY_RANGE_CHECK_STRINGIZE(x) #x #define FOLLY_RANGE_CHECK_STRINGIZE(x) #x
#define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x) #define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x)
#define FOLLY_RANGE_CHECK(condition, message) \ #define FOLLY_RANGE_CHECK(condition, message, src) \
((condition) ? (void)0 : throw std::range_error( \ ((condition) ? (void)0 : throw std::range_error( \
(std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \ (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \
+ (message)).c_str())) + (message) + ": '" + (src) + "'").c_str()))
#define FOLLY_RANGE_CHECK_BEGIN_END(condition, message, b, e) \
FOLLY_RANGE_CHECK(condition, message, std::string((b), (e) - (b)))
#define FOLLY_RANGE_CHECK_STRINGPIECE(condition, message, sp) \
FOLLY_RANGE_CHECK(condition, message, std::string((sp).data(), (sp).size()))
namespace folly { namespace folly {
...@@ -89,14 +95,14 @@ to(const Src & value) { ...@@ -89,14 +95,14 @@ to(const Src & value) {
< std::numeric_limits<Src>::max()) { < std::numeric_limits<Src>::max()) {
FOLLY_RANGE_CHECK( FOLLY_RANGE_CHECK(
(!greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)), (!greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)),
"Overflow" "Overflow", std::to_string(value)
); );
} }
/* static */ if (std::is_signed<Src>::value && /* static */ if (std::is_signed<Src>::value &&
(!std::is_signed<Tgt>::value || sizeof(Src) > sizeof(Tgt))) { (!std::is_signed<Tgt>::value || sizeof(Src) > sizeof(Tgt))) {
FOLLY_RANGE_CHECK( FOLLY_RANGE_CHECK(
(!less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)), (!less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)),
"Negative overflow" "Negative overflow", std::to_string(value)
); );
} }
return static_cast<Tgt>(value); return static_cast<Tgt>(value);
...@@ -116,9 +122,9 @@ to(const Src & value) { ...@@ -116,9 +122,9 @@ to(const Src & value) {
/* static */ if (std::numeric_limits<Tgt>::max() < /* static */ if (std::numeric_limits<Tgt>::max() <
std::numeric_limits<Src>::max()) { std::numeric_limits<Src>::max()) {
FOLLY_RANGE_CHECK(value <= std::numeric_limits<Tgt>::max(), FOLLY_RANGE_CHECK(value <= std::numeric_limits<Tgt>::max(),
"Overflow"); "Overflow", std::to_string(value));
FOLLY_RANGE_CHECK(value >= -std::numeric_limits<Tgt>::max(), FOLLY_RANGE_CHECK(value >= -std::numeric_limits<Tgt>::max(),
"Negative overflow"); "Negative overflow", std::to_string(value));
} }
return boost::implicit_cast<Tgt>(value); return boost::implicit_cast<Tgt>(value);
} }
...@@ -1075,9 +1081,10 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = { ...@@ -1075,9 +1081,10 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = {
if (*b != '0') return digits_to<Tgt>(b, e); if (*b != '0') return digits_to<Tgt>(b, e);
} }
} }
FOLLY_RANGE_CHECK(size == std::numeric_limits<Tgt>::digits10 + 1 && FOLLY_RANGE_CHECK_BEGIN_END(
size == std::numeric_limits<Tgt>::digits10 + 1 &&
strncmp(b, detail::MaxString<Tgt>::value, size) <= 0, strncmp(b, detail::MaxString<Tgt>::value, size) <= 0,
"Numeric overflow upon conversion"); "Numeric overflow upon conversion", b, e);
} }
// Here we know that the number won't overflow when // Here we know that the number won't overflow when
...@@ -1120,7 +1127,8 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = { ...@@ -1120,7 +1127,8 @@ __attribute__((__aligned__(16))) constexpr uint16_t shift1000[] = {
} }
assert(b == e); assert(b == e);
FOLLY_RANGE_CHECK(size > 0, "Found no digits to convert in input"); FOLLY_RANGE_CHECK_BEGIN_END(size > 0,
"Found no digits to convert in input", b, e);
return result; return result;
} }
...@@ -1152,18 +1160,19 @@ typename std::enable_if< ...@@ -1152,18 +1160,19 @@ typename std::enable_if<
std::is_integral<Tgt>::value && std::is_signed<Tgt>::value, std::is_integral<Tgt>::value && std::is_signed<Tgt>::value,
Tgt>::type Tgt>::type
to(const char * b, const char * e) { to(const char * b, const char * e) {
FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral"); FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral",
to<std::string>("b: ", intptr_t(b), " e: ", intptr_t(e)));
if (!isdigit(*b)) { if (!isdigit(*b)) {
if (*b == '-') { if (*b == '-') {
Tgt result = -to<typename std::make_unsigned<Tgt>::type>(b + 1, e); Tgt result = -to<typename std::make_unsigned<Tgt>::type>(b + 1, e);
FOLLY_RANGE_CHECK(result <= 0, "Negative overflow."); FOLLY_RANGE_CHECK_BEGIN_END(result <= 0, "Negative overflow.", b, e);
return result; return result;
} }
FOLLY_RANGE_CHECK(*b == '+', "Invalid lead character"); FOLLY_RANGE_CHECK_BEGIN_END(*b == '+', "Invalid lead character", b, e);
++b; ++b;
} }
Tgt result = to<typename std::make_unsigned<Tgt>::type>(b, e); Tgt result = to<typename std::make_unsigned<Tgt>::type>(b, e);
FOLLY_RANGE_CHECK(result >= 0, "Overflow."); FOLLY_RANGE_CHECK_BEGIN_END(result >= 0, "Overflow", b, e);
return result; return result;
} }
...@@ -1186,7 +1195,8 @@ to(StringPiece * src) { ...@@ -1186,7 +1195,8 @@ to(StringPiece * src) {
auto b = src->data(), past = src->data() + src->size(); auto b = src->data(), past = src->data() + src->size();
for (;; ++b) { for (;; ++b) {
FOLLY_RANGE_CHECK(b < past, "No digits found in input string"); FOLLY_RANGE_CHECK_STRINGPIECE(b < past,
"No digits found in input string", *src);
if (!isspace(*b)) break; if (!isspace(*b)) break;
} }
...@@ -1199,15 +1209,16 @@ to(StringPiece * src) { ...@@ -1199,15 +1209,16 @@ to(StringPiece * src) {
if (*m == '-') { if (*m == '-') {
negative = true; negative = true;
} else { } else {
FOLLY_RANGE_CHECK(*m == '+', "Invalid leading character in conversion" FOLLY_RANGE_CHECK_STRINGPIECE(*m == '+', "Invalid leading character in "
" to integral"); "conversion to integral", *src);
} }
++b; ++b;
++m; ++m;
} }
} }
FOLLY_RANGE_CHECK(m < past, "No digits found in input string"); FOLLY_RANGE_CHECK_STRINGPIECE(m < past, "No digits found in input string",
FOLLY_RANGE_CHECK(isdigit(*m), "Non-digit character found"); *src);
FOLLY_RANGE_CHECK_STRINGPIECE(isdigit(*m), "Non-digit character found", *src);
m = detail::findFirstNonDigit<Tgt>(m + 1, past); m = detail::findFirstNonDigit<Tgt>(m + 1, past);
Tgt result; Tgt result;
...@@ -1217,10 +1228,11 @@ to(StringPiece * src) { ...@@ -1217,10 +1228,11 @@ to(StringPiece * src) {
auto t = detail::digits_to<typename std::make_unsigned<Tgt>::type>(b, m); auto t = detail::digits_to<typename std::make_unsigned<Tgt>::type>(b, m);
if (negative) { if (negative) {
result = -t; result = -t;
FOLLY_RANGE_CHECK(is_non_positive(result), "Negative overflow"); FOLLY_RANGE_CHECK_STRINGPIECE(is_non_positive(result),
"Negative overflow", *src);
} else { } else {
result = t; result = t;
FOLLY_RANGE_CHECK(is_non_negative(result), "Overflow"); FOLLY_RANGE_CHECK_STRINGPIECE(is_non_negative(result), "Overflow", *src);
} }
} }
src->advance(m - src->data()); src->advance(m - src->data());
...@@ -1246,7 +1258,9 @@ namespace detail { ...@@ -1246,7 +1258,9 @@ namespace detail {
*/ */
inline void enforceWhitespace(const char* b, const char* e) { inline void enforceWhitespace(const char* b, const char* e) {
for (; b != e; ++b) { for (; b != e; ++b) {
FOLLY_RANGE_CHECK(isspace(*b), to<std::string>("Non-whitespace: ", *b)); FOLLY_RANGE_CHECK_BEGIN_END(isspace(*b),
to<std::string>("Non-whitespace: ", *b),
b, e);
} }
} }
...@@ -1288,7 +1302,8 @@ to(StringPiece *const src) { ...@@ -1288,7 +1302,8 @@ to(StringPiece *const src) {
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
nullptr, nullptr); nullptr, nullptr);
FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string"); FOLLY_RANGE_CHECK_STRINGPIECE(!src->empty(),
"No digits found in input string", *src);
int length; int length;
auto result = conv.StringToDouble(src->data(), auto result = conv.StringToDouble(src->data(),
...@@ -1476,8 +1491,10 @@ to(const Src & value) { ...@@ -1476,8 +1491,10 @@ to(const Src & value) {
// to avoid defining this global macro name in other files that include Conv.h. // to avoid defining this global macro name in other files that include Conv.h.
#ifndef FOLLY_CONV_INTERNAL #ifndef FOLLY_CONV_INTERNAL
#undef FOLLY_RANGE_CHECK #undef FOLLY_RANGE_CHECK
#undef FOLLY_RANGE_CHECK_STRINGIZE2 #undef FOLLY_RANGE_CHECK_BEGIN_END
#undef FOLLY_RANGE_CHECK_STRINGPIECE
#undef FOLLY_RANGE_CHECK_STRINGIZE #undef FOLLY_RANGE_CHECK_STRINGIZE
#undef FOLLY_RANGE_CHECK_STRINGIZE2
#endif #endif
#endif /* FOLLY_BASE_CONV_H_ */ #endif /* FOLLY_BASE_CONV_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment