int string2decimal()

in mysql_strings/decimal.cc [925:1027]


int string2decimal(const char *from, decimal_t *to, const char **end) {
  const char *s = from, *s1, *endp, *end_of_string = *end;
  int i, intg, frac, error, intg1, frac1;
  dec1 x, *buf;
  sanity(to);

  error = E_DEC_BAD_NUM; /* In case of bad number */
  while (s < end_of_string && my_isspace(&my_charset_latin1, *s)) s++;
  if (s == end_of_string) goto fatal_error;

  if ((to->sign = (*s == '-')))
    s++;
  else if (*s == '+')
    s++;

  s1 = s;
  while (s < end_of_string && my_isdigit(&my_charset_latin1, *s)) s++;
  intg = (int)(s - s1);
  if (s < end_of_string && *s == '.') {
    endp = s + 1;
    while (endp < end_of_string && my_isdigit(&my_charset_latin1, *endp))
      endp++;
    frac = (int)(endp - s - 1);
  } else {
    frac = 0;
    endp = s;
  }

  *end = endp;

  if (frac + intg == 0) goto fatal_error;

  error = 0;

  intg1 = ROUND_UP(intg);
  frac1 = ROUND_UP(frac);
  FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error);
  if (unlikely(error)) {
    frac = frac1 * DIG_PER_DEC1;
    if (error == E_DEC_OVERFLOW) intg = intg1 * DIG_PER_DEC1;
  }

  /* Error is guranteed to be set here */
  to->intg = intg;
  to->frac = frac;

  buf = to->buf + intg1;
  s1 = s;

  for (x = 0, i = 0; intg; intg--) {
    x += (*--s - '0') * powers10[i];

    if (unlikely(++i == DIG_PER_DEC1)) {
      *--buf = x;
      x = 0;
      i = 0;
    }
  }
  if (i) *--buf = x;

  buf = to->buf + intg1;
  for (x = 0, i = 0; frac; frac--) {
    x = (*++s1 - '0') + x * 10;

    if (unlikely(++i == DIG_PER_DEC1)) {
      *buf++ = x;
      x = 0;
      i = 0;
    }
  }
  if (i) *buf = x * powers10[DIG_PER_DEC1 - i];

  /* Handle exponent */
  if (endp + 1 < end_of_string && (*endp == 'e' || *endp == 'E')) {
    int str_error;
    longlong exponent = my_strtoll10(endp + 1, &end_of_string, &str_error);

    if (end_of_string != endp + 1) /* If at least one digit */
    {
      *end = end_of_string;
      if (str_error > 0) {
        error = E_DEC_BAD_NUM;
        goto fatal_error;
      }
      if (exponent > INT_MAX / 2 || (str_error == 0 && exponent < 0)) {
        error = E_DEC_OVERFLOW;
        goto fatal_error;
      }
      if (exponent < INT_MIN / 2 && error != E_DEC_OVERFLOW) {
        error = E_DEC_TRUNCATED;
        goto fatal_error;
      }
      if (error != E_DEC_OVERFLOW) error = decimal_shift(to, (int)exponent);
    }
  }
  /* Avoid returning negative zero, cfr. decimal_cmp() */
  if (to->sign && decimal_is_zero(to)) to->sign = false;
  return error;

fatal_error:
  decimal_make_zero(to);
  return error;
}