int bin2decimal()

in mysql_strings/decimal.cc [1482:1593]


int bin2decimal(const uchar *from, decimal_t *to, int precision, int scale,
                bool keep_prec) {
  int error = E_DEC_OK, intg = precision - scale, intg0 = intg / DIG_PER_DEC1,
      frac0 = scale / DIG_PER_DEC1, intg0x = intg - intg0 * DIG_PER_DEC1,
      frac0x = scale - frac0 * DIG_PER_DEC1, intg1 = intg0 + (intg0x > 0),
      frac1 = frac0 + (frac0x > 0);
  dec1 *buf = to->buf, mask = (*from & 0x80) ? 0 : -1;
  const uchar *stop;
  uchar *d_copy;
  int bin_size = decimal_bin_size_inline(precision, scale);

  sanity(to);
  d_copy = (uchar *)my_alloca(bin_size);
  memcpy(d_copy, from, bin_size);
  d_copy[0] ^= 0x80;
  from = d_copy;

  FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error);
  if (unlikely(error)) {
    if (intg1 < intg0 + (intg0x > 0)) {
      from += dig2bytes[intg0x] + sizeof(dec1) * (intg0 - intg1);
      frac0 = frac0x = intg0x = 0;
      intg0 = intg1;
    } else {
      frac0x = 0;
      frac0 = frac1;
    }
  }

  to->sign = (mask != 0);
  to->intg = intg0 * DIG_PER_DEC1 + intg0x;
  to->frac = frac0 * DIG_PER_DEC1 + frac0x;

  if (intg0x) {
    int i = dig2bytes[intg0x];
    dec1 x = 0;
    switch (i) {
      case 1:
        x = mi_sint1korr(from);
        break;
      case 2:
        x = mi_sint2korr(from);
        break;
      case 3:
        x = mi_sint3korr(from);
        break;
      case 4:
        x = mi_sint4korr(from);
        break;
      default:
        assert(0);
    }
    from += i;
    *buf = x ^ mask;
    if (((ulonglong)*buf) >= (ulonglong)powers10[intg0x + 1]) goto err;
    if (buf > to->buf || *buf != 0 || keep_prec)
      buf++;
    else
      to->intg -= intg0x;
  }
  for (stop = from + intg0 * sizeof(dec1); from < stop; from += sizeof(dec1)) {
    assert(sizeof(dec1) == 4);
    *buf = mi_sint4korr(from) ^ mask;
    if (((uint32)*buf) > DIG_MAX) goto err;
    if (buf > to->buf || *buf != 0 || keep_prec)
      buf++;
    else
      to->intg -= DIG_PER_DEC1;
  }
  assert(to->intg >= 0);
  for (stop = from + frac0 * sizeof(dec1); from < stop; from += sizeof(dec1)) {
    assert(sizeof(dec1) == 4);
    *buf = mi_sint4korr(from) ^ mask;
    if (((uint32)*buf) > DIG_MAX) goto err;
    buf++;
  }
  if (frac0x) {
    int i = dig2bytes[frac0x];
    dec1 x = 0;
    switch (i) {
      case 1:
        x = mi_sint1korr(from);
        break;
      case 2:
        x = mi_sint2korr(from);
        break;
      case 3:
        x = mi_sint3korr(from);
        break;
      case 4:
        x = mi_sint4korr(from);
        break;
      default:
        assert(0);
    }
    *buf = (x ^ mask) * powers10[DIG_PER_DEC1 - frac0x];
    if (((uint32)*buf) > DIG_MAX) goto err;
    buf++;
  }

  /*
    No digits? We have read the number zero, of unspecified precision.
    Make it a proper zero, with non-zero precision.
    Note: this is valid only if scale == 0, otherwise frac is always non-zero
  */
  if (to->intg == 0 && to->frac == 0) decimal_make_zero(to);
  return error;

err:
  decimal_make_zero(to);
  return (E_DEC_BAD_NUM);
}