in mysql_strings/ctype-ucs2.cc [621:782]
static longlong my_strtoll10_mb2(const CHARSET_INFO *cs, const char *nptr,
const char **endptr, int *error) {
const char *s, *end, *start, *n_end, *true_end;
uchar c;
unsigned long i, j, k;
ulonglong li;
int negative;
ulong cutoff, cutoff2, cutoff3;
my_wc_t wc;
int res;
s = nptr;
/* If fixed length string */
if (endptr) {
/*
Make sure string length is even.
Odd length indicates a bug in the caller.
Assert in debug, round in production.
*/
assert((*endptr - s) % 2 == 0);
end = s + ((*endptr - s) / 2) * 2;
for (;;) /* Skip leading spaces and tabs */
{
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
s += res;
if (wc != ' ' && wc != '\t') break;
}
} else {
/* We don't support null terminated strings in UCS2 */
goto no_conv;
}
/* Check for a sign. */
negative = 0;
if (wc == '-') {
*error = -1; /* Mark as negative number */
negative = 1;
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
s += res;
cutoff = MAX_NEGATIVE_NUMBER / LFACTOR2;
cutoff2 = (MAX_NEGATIVE_NUMBER % LFACTOR2) / 100;
cutoff3 = MAX_NEGATIVE_NUMBER % 100;
} else {
*error = 0;
if (wc == '+') {
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
s += res;
}
cutoff = ULONGLONG_MAX / LFACTOR2;
cutoff2 = ULONGLONG_MAX % LFACTOR2 / 100;
cutoff3 = ULONGLONG_MAX % 100;
}
/* Handle case where we have a lot of pre-zero */
if (wc == '0') {
i = 0;
for (;; s += res) {
if (s == end) goto end_i; /* Return 0 */
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
if (wc != '0') break;
}
while (wc == '0')
;
n_end = s + 2 * INIT_CNT;
} else {
/* Read first digit to check that it's a valid number */
if ((c = (wc - '0')) > 9) goto no_conv;
i = c;
n_end = s + 2 * (INIT_CNT - 1);
}
/* Handle first 9 digits and store them in i */
if (n_end > end) n_end = end;
for (;;) {
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)n_end);
if (res <= 0) break;
if ((c = (wc - '0')) > 9) goto end_i;
s += res;
i = i * 10 + c;
}
if (s == end) goto end_i;
/* Handle next 9 digits and store them in j */
j = 0;
start = s; /* Used to know how much to shift i */
n_end = true_end = s + 2 * INIT_CNT;
if (n_end > end) n_end = end;
do {
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
if ((c = (wc - '0')) > 9) goto end_i_and_j;
s += res;
j = j * 10 + c;
} while (s != n_end);
if (s == end) {
if (s != true_end) goto end_i_and_j;
goto end3;
}
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
if ((c = (wc - '0')) > 9) goto end3;
s += res;
/* Handle the next 1 or 2 digits and store them in k */
k = c;
if (s == end) goto end4;
res = cs->cset->mb_wc(cs, &wc, (const uchar *)s, (const uchar *)end);
if (res <= 0) goto no_conv;
if ((c = (wc - '0')) > 9) goto end4;
s += res;
k = k * 10 + c;
*endptr = s;
/* number string should have ended here */
if (s != end && (c = (wc - '0')) <= 9) goto overflow;
/* Check that we didn't get an overflow with the last digit */
if (i > cutoff ||
(i == cutoff && ((j > cutoff2 || j == cutoff2) && k > cutoff3)))
goto overflow;
li = i * LFACTOR2 + (ulonglong)j * 100 + k;
return (longlong)li;
overflow: /* *endptr is set here */
*error = MY_ERRNO_ERANGE;
return negative ? LLONG_MIN : (longlong)ULONGLONG_MAX;
end_i:
*endptr = s;
return (negative ? ((longlong) - (long)i) : (longlong)i);
end_i_and_j:
li = (ulonglong)i * lfactor[(size_t)(s - start) / 2] + j;
*endptr = s;
return (negative ? -((longlong)li) : (longlong)li);
end3:
li = (ulonglong)i * LFACTOR + (ulonglong)j;
*endptr = s;
return (negative ? -((longlong)li) : (longlong)li);
end4:
li = (ulonglong)i * LFACTOR1 + (ulonglong)j * 10 + k;
*endptr = s;
if (negative) {
if (li > MAX_NEGATIVE_NUMBER) goto overflow;
if (li == MAX_NEGATIVE_NUMBER) return LLONG_MIN;
return -((longlong)li);
}
return (longlong)li;
no_conv:
/* There was no number to convert. */
*error = MY_ERRNO_EDOM;
*endptr = nptr;
return 0;
}