aboutsummaryrefslogtreecommitdiff
path: root/mysql/strings/my_strtoll10.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysql/strings/my_strtoll10.c')
-rw-r--r--mysql/strings/my_strtoll10.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/mysql/strings/my_strtoll10.c b/mysql/strings/my_strtoll10.c
new file mode 100644
index 0000000..1fb4f0c
--- /dev/null
+++ b/mysql/strings/my_strtoll10.c
@@ -0,0 +1,236 @@
+/* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+#include <my_sys.h> /* Needed for MY_ERRNO_ERANGE */
+#include <m_string.h>
+
+#define MAX_NEGATIVE_NUMBER ((ulonglong) 0x8000000000000000LL)
+#define INIT_CNT 9
+#define LFACTOR 1000000000ULL
+#define LFACTOR1 10000000000ULL
+#define LFACTOR2 100000000000ULL
+
+static unsigned long lfactor[9]=
+{
+ 1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, 100000000L
+};
+
+/*
+ Convert a string to an to unsigned long long integer value
+
+ SYNOPSYS
+ my_strtoll10()
+ nptr in pointer to the string to be converted
+ endptr in/out pointer to the end of the string/
+ pointer to the stop character
+ error out returned error code
+
+ DESCRIPTION
+ This function takes the decimal representation of integer number
+ from string nptr and converts it to an signed or unsigned
+ long long integer value.
+ Space characters and tab are ignored.
+ A sign character might precede the digit characters. The number
+ may have any number of pre-zero digits.
+
+ The function stops reading the string nptr at the first character
+ that is not a decimal digit. If endptr is not NULL then the function
+ will not read characters after *endptr.
+
+ RETURN VALUES
+ Value of string as a signed/unsigned longlong integer
+
+ if no error and endptr != NULL, it will be set to point at the character
+ after the number
+
+ The error parameter contains information how things went:
+ -1 Number was an ok negative number
+ 0 ok
+ ERANGE If the the value of the converted number exceeded the
+ maximum negative/unsigned long long integer.
+ In this case the return value is ~0 if value was
+ positive and LLONG_MIN if value was negative.
+ EDOM If the string didn't contain any digits. In this case
+ the return value is 0.
+
+ If endptr is not NULL the function will store the end pointer to
+ the stop character here.
+*/
+
+
+longlong my_strtoll10(const char *nptr, char **endptr, int *error)
+{
+ const char *s, *end, *start, *n_end, *true_end;
+ char *dummy;
+ uchar c;
+ unsigned long i, j, k;
+ ulonglong li;
+ int negative;
+ ulong cutoff, cutoff2, cutoff3;
+
+ s= nptr;
+ /* If fixed length string */
+ if (endptr)
+ {
+ end= *endptr;
+ while (s != end && (*s == ' ' || *s == '\t'))
+ s++;
+ if (s == end)
+ goto no_conv;
+ }
+ else
+ {
+ endptr= &dummy; /* Easier end test */
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!*s)
+ goto no_conv;
+ /* This number must be big to guard against a lot of pre-zeros */
+ end= s+65535; /* Can't be longer than this */
+ }
+
+ /* Check for a sign. */
+ negative= 0;
+ if (*s == '-')
+ {
+ *error= -1; /* Mark as negative number */
+ negative= 1;
+ if (++s == end)
+ goto no_conv;
+ cutoff= MAX_NEGATIVE_NUMBER / LFACTOR2;
+ cutoff2= (MAX_NEGATIVE_NUMBER % LFACTOR2) / 100;
+ cutoff3= MAX_NEGATIVE_NUMBER % 100;
+ }
+ else
+ {
+ *error= 0;
+ if (*s == '+')
+ {
+ if (++s == end)
+ goto no_conv;
+ }
+ cutoff= ULLONG_MAX / LFACTOR2;
+ cutoff2= ULLONG_MAX % LFACTOR2 / 100;
+ cutoff3= ULLONG_MAX % 100;
+ }
+
+ /* Handle case where we have a lot of pre-zero */
+ if (*s == '0')
+ {
+ i= 0;
+ do
+ {
+ if (++s == end)
+ goto end_i; /* Return 0 */
+ }
+ while (*s == '0');
+ n_end= s+ INIT_CNT;
+ }
+ else
+ {
+ /* Read first digit to check that it's a valid number */
+ if ((c= (*s-'0')) > 9)
+ goto no_conv;
+ i= c;
+ n_end= ++s+ INIT_CNT-1;
+ }
+
+ /* Handle first 9 digits and store them in i */
+ if (n_end > end)
+ n_end= end;
+ for (; s != n_end ; s++)
+ {
+ if ((c= (*s-'0')) > 9)
+ goto end_i;
+ 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 + INIT_CNT;
+ if (n_end > end)
+ n_end= end;
+ do
+ {
+ if ((c= (*s-'0')) > 9)
+ goto end_i_and_j;
+ j= j*10+c;
+ } while (++s != n_end);
+ if (s == end)
+ {
+ if (s != true_end)
+ goto end_i_and_j;
+ goto end3;
+ }
+ if ((c= (*s-'0')) > 9)
+ goto end3;
+
+ /* Handle the next 1 or 2 digits and store them in k */
+ k=c;
+ if (++s == end || (c= (*s-'0')) > 9)
+ goto end4;
+ k= k*10+c;
+ *endptr= (char*) ++s;
+
+ /* number string should have ended here */
+ if (s != end && (c= (*s-'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) ULLONG_MAX;
+
+end_i:
+ *endptr= (char*) s;
+ return (negative ? ((longlong) -(long) i) : (longlong) i);
+
+end_i_and_j:
+ li= (ulonglong) i * lfactor[(uint) (s-start)] + j;
+ *endptr= (char*) s;
+ return (negative ? -((longlong) li) : (longlong) li);
+
+end3:
+ li=(ulonglong) i*LFACTOR+ (ulonglong) j;
+ *endptr= (char*) s;
+ return (negative ? -((longlong) li) : (longlong) li);
+
+end4:
+ li=(ulonglong) i*LFACTOR1+ (ulonglong) j * 10 + k;
+ *endptr= (char*) s;
+ if (negative)
+ {
+ if (li > MAX_NEGATIVE_NUMBER)
+ goto overflow;
+ return -((longlong) li);
+ }
+ return (longlong) li;
+
+no_conv:
+ /* There was no number to convert. */
+ *error= MY_ERRNO_EDOM;
+ *endptr= (char *) nptr;
+ return 0;
+}