static int do_sub()

in mysql_strings/decimal.cc [1907:2018]


static int do_sub(const decimal_t *from1, const decimal_t *from2,
                  decimal_t *to) {
  int intg1 = ROUND_UP(from1->intg), intg2 = ROUND_UP(from2->intg),
      frac1 = ROUND_UP(from1->frac), frac2 = ROUND_UP(from2->frac);
  int frac0 = std::max(frac1, frac2), error;
  dec1 *buf1, *buf2, *buf0, *stop1, *stop2, *start1, *start2, carry = 0;

  /* let carry:=1 if from2 > from1 */
  start1 = buf1 = from1->buf;
  stop1 = buf1 + intg1;
  start2 = buf2 = from2->buf;
  stop2 = buf2 + intg2;
  if (unlikely(*buf1 == 0)) {
    while (buf1 < stop1 && *buf1 == 0) buf1++;
    start1 = buf1;
    intg1 = (int)(stop1 - buf1);
  }
  if (unlikely(*buf2 == 0)) {
    while (buf2 < stop2 && *buf2 == 0) buf2++;
    start2 = buf2;
    intg2 = (int)(stop2 - buf2);
  }
  if (intg2 > intg1)
    carry = 1;
  else if (intg2 == intg1) {
    dec1 *end1 = stop1 + (frac1 - 1);
    dec1 *end2 = stop2 + (frac2 - 1);
    while (unlikely((buf1 <= end1) && (*end1 == 0))) end1--;
    while (unlikely((buf2 <= end2) && (*end2 == 0))) end2--;
    frac1 = (int)(end1 - stop1) + 1;
    frac2 = (int)(end2 - stop2) + 1;
    while (buf1 <= end1 && buf2 <= end2 && *buf1 == *buf2) buf1++, buf2++;
    if (buf1 <= end1) {
      if (buf2 <= end2)
        carry = *buf2 > *buf1;
      else
        carry = 0;
    } else {
      if (buf2 <= end2)
        carry = 1;
      else /* short-circuit everything: from1 == from2 */
      {
        if (to == nullptr) /* decimal_cmp() */
          return 0;
        decimal_make_zero(to);
        return E_DEC_OK;
      }
    }
  }

  if (to == nullptr) /* decimal_cmp() */
    return ((bool)carry == from1->sign ? 1 : -1);

  sanity(to);

  to->sign = from1->sign;

  /* ensure that always from1 > from2 (and intg1 >= intg2) */
  if (carry) {
    std::swap(from1, from2);
    std::swap(start1, start2);
    std::swap(intg1, intg2);
    std::swap(frac1, frac2);
    to->sign = 1 - to->sign;
  }

  FIX_INTG_FRAC_ERROR(to->len, intg1, frac0, error);
  buf0 = to->buf + intg1 + frac0;

  to->frac = std::max(from1->frac, from2->frac);
  to->intg = intg1 * DIG_PER_DEC1;
  if (unlikely(error)) {
    to->frac = std::min(to->frac, frac0 * DIG_PER_DEC1);
    frac1 = std::min(frac1, frac0);
    frac2 = std::min(frac2, frac0);
    intg2 = std::min(intg2, intg1);
  }
  carry = 0;

  /* part 1 - max(frac) ... min (frac) */
  if (frac1 > frac2) {
    buf1 = start1 + intg1 + frac1;
    stop1 = start1 + intg1 + frac2;
    buf2 = start2 + intg2 + frac2;
    while (frac0-- > frac1) *--buf0 = 0;
    while (buf1 > stop1) *--buf0 = *--buf1;
  } else {
    buf1 = start1 + intg1 + frac1;
    buf2 = start2 + intg2 + frac2;
    stop2 = start2 + intg2 + frac1;
    while (frac0-- > frac2) *--buf0 = 0;
    while (buf2 > stop2) {
      SUB(*--buf0, 0, *--buf2, carry);
    }
  }

  /* part 2 - min(frac) ... intg2 */
  while (buf2 > start2) {
    SUB(*--buf0, *--buf1, *--buf2, carry);
  }

  /* part 3 - intg2 ... intg1 */
  while (carry && buf1 > start1) {
    SUB(*--buf0, *--buf1, 0, carry);
  }

  while (buf1 > start1) *--buf0 = *--buf1;

  while (buf0 > to->buf) *--buf0 = 0;

  return error;
}