bool ParseDoubleRange()

in src/kudu/gutil/strings/numbers.cc [136:264]


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;
}