in be/src/kudu/gutil/strings/numbers.cc [137:265]
bool ParseDoubleRange(const char* text, int len, const char** end,
double* from, double* to, bool* is_currency,
const DoubleRangeOptions& opts) {
const double from_default = opts.dont_modify_unbounded ? *from : -HUGE_VAL;
if (!opts.dont_modify_unbounded) {
*from = -HUGE_VAL;
*to = HUGE_VAL;
}
if (opts.allow_currency && (is_currency != nullptr))
*is_currency = false;
assert(len >= -1);
assert(opts.separators && (*opts.separators != '\0'));
// these aren't valid separators
assert(strlen(opts.separators) ==
strcspn(opts.separators, "+0123456789eE$"));
assert(opts.num_required_bounds <= 2);
// Handle easier cases of comparators (<, >) first
if (opts.allow_comparators) {
char comparator = EatAChar(&text, &len, "<>", true, false);
if (comparator) {
double* dest = (comparator == '>') ? from : to;
EatAChar(&text, &len, "=", true, false);
if (opts.allow_currency && EatAChar(&text, &len, "$", true, false))
if (is_currency != nullptr)
*is_currency = true;
if (!EatADouble(&text, &len, opts.allow_unbounded_markers, dest, nullptr,
nullptr))
return false;
*end = text;
return EatAChar(&text, &len, opts.acceptable_terminators, false,
opts.null_terminator_ok);
}
}
bool seen_dollar = (opts.allow_currency &&
EatAChar(&text, &len, "$", true, false));
// If we see a '-', two things could be happening: -<to> or
// <from>... where <from> is negative. Treat initial minus sign as a
// separator if '-' is a valid separator.
// Similarly, we prepare for the possibility of seeing a '.' at the
// end of the number, in case '.' (which really means '..') is a
// separator.
bool initial_minus_sign = false;
bool final_period = false;
bool* check_initial_minus = (strchr(opts.separators, '-') && !seen_dollar
&& (opts.num_required_bounds < 2)) ?
(&initial_minus_sign) : nullptr;
bool* check_final_period = strchr(opts.separators, '.') ? (&final_period)
: nullptr;
bool double_seen = EatADouble(&text, &len, opts.allow_unbounded_markers,
from, check_initial_minus, check_final_period);
// if 2 bounds required, must see a double (or '?' if allowed)
if ((opts.num_required_bounds == 2) && !double_seen) return false;
if (seen_dollar && !double_seen) {
--text;
if (len != -1)
++len;
seen_dollar = false;
}
// If we're here, we've read the first double and now expect a
// separator and another <double>.
char separator = EatAChar(&text, &len, opts.separators, true, false);
if (separator == '.') {
// seen one '.' as separator; must check for another; perhaps set seplen=2
if (EatAChar(&text, &len, ".", true, false)) {
if (final_period) {
// We may have three periods in a row. The first is part of the
// first number, the others are a separator. Policy: 234...567
// is "234." to "567", not "234" to ".567".
EatAChar(&text, &len, ".", true, false);
}
} else if (!EatAChar(&text, &len, opts.separators, true, false)) {
// just one '.' and no other separator; uneat the first '.' we saw
--text;
if (len != -1)
++len;
separator = '\0';
}
}
// By now, we've consumed whatever separator there may have been,
// and separator is true iff there was one.
if (!separator) {
if (final_period) // final period now considered part of first double
EatAChar(&text, &len, ".", true, false);
if (initial_minus_sign && double_seen) {
*to = *from;
*from = from_default;
} else if (opts.require_separator ||
(opts.num_required_bounds > 0 && !double_seen) ||
(opts.num_required_bounds > 1) ) {
return false;
}
} else {
if (initial_minus_sign && double_seen)
*from = -(*from);
// read second <double>
bool second_dollar_seen = (seen_dollar
|| (opts.allow_currency && !double_seen))
&& EatAChar(&text, &len, "$", true, false);
bool second_double_seen = EatADouble(
&text, &len, opts.allow_unbounded_markers, to, nullptr, nullptr);
if (opts.num_required_bounds > double_seen + second_double_seen)
return false;
if (second_dollar_seen && !second_double_seen) {
--text;
if (len != -1)
++len;
second_dollar_seen = false;
}
seen_dollar = seen_dollar || second_dollar_seen;
}
if (seen_dollar && (is_currency != nullptr))
*is_currency = true;
// We're done. But we have to check that the next char is a proper
// terminator.
*end = text;
char terminator = EatAChar(&text, &len, opts.acceptable_terminators, false,
opts.null_terminator_ok);
if (terminator == '.')
--(*end);
return terminator;
}