1 /* Copyright (c) 2003, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 #include <my_global.h>
29 #include <my_sys.h>            /* Needed for MY_ERRNO_ERANGE */
30 #include <m_string.h>
31 
32 #define MAX_NEGATIVE_NUMBER	((ulonglong) 0x8000000000000000LL)
33 #define INIT_CNT  9
34 #define LFACTOR   1000000000ULL
35 #define LFACTOR1  10000000000ULL
36 #define LFACTOR2  100000000000ULL
37 
38 static unsigned long lfactor[9]=
39 {
40   1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, 100000000L
41 };
42 
43 /*
44   Convert a string to an to unsigned long long integer value
45 
46   SYNOPSYS
47     my_strtoll10()
48       nptr     in       pointer to the string to be converted
49       endptr   in/out   pointer to the end of the string/
50                         pointer to the stop character
51       error    out      returned error code
52 
53   DESCRIPTION
54     This function takes the decimal representation of integer number
55     from string nptr and converts it to an signed or unsigned
56     long long integer value.
57     Space characters and tab are ignored.
58     A sign character might precede the digit characters. The number
59     may have any number of pre-zero digits.
60 
61     The function stops reading the string nptr at the first character
62     that is not a decimal digit. If endptr is not NULL then the function
63     will not read characters after *endptr.
64 
65   RETURN VALUES
66     Value of string as a signed/unsigned longlong integer
67 
68     if no error and endptr != NULL, it will be set to point at the character
69     after the number
70 
71     The error parameter contains information how things went:
72     -1		Number was an ok negative number
73     0	 	ok
74     ERANGE	If the the value of the converted number exceeded the
75 	        maximum negative/unsigned long long integer.
76 		In this case the return value is ~0 if value was
77 		positive and LLONG_MIN if value was negative.
78     EDOM	If the string didn't contain any digits. In this case
79     		the return value is 0.
80 
81     If endptr is not NULL the function will store the end pointer to
82     the stop character here.
83 */
84 
85 
my_strtoll10(const char * nptr,char ** endptr,int * error)86 longlong my_strtoll10(const char *nptr, char **endptr, int *error)
87 {
88   const char *s, *end, *start, *n_end, *true_end;
89   char *dummy;
90   uchar c;
91   unsigned long i, j, k;
92   ulonglong li;
93   int negative;
94   ulong cutoff, cutoff2, cutoff3;
95 
96   s= nptr;
97   /* If fixed length string */
98   if (endptr)
99   {
100     end= *endptr;
101     while (s != end && (*s == ' ' || *s == '\t'))
102       s++;
103     if (s == end)
104       goto no_conv;
105   }
106   else
107   {
108     endptr= &dummy;				/* Easier end test */
109     while (*s == ' ' || *s == '\t')
110       s++;
111     if (!*s)
112       goto no_conv;
113     /* This number must be big to guard against a lot of pre-zeros */
114     end= s+65535;				/* Can't be longer than this */
115   }
116 
117   /* Check for a sign.	*/
118   negative= 0;
119   if (*s == '-')
120   {
121     *error= -1;					/* Mark as negative number */
122     negative= 1;
123     if (++s == end)
124       goto no_conv;
125     cutoff=  MAX_NEGATIVE_NUMBER / LFACTOR2;
126     cutoff2= (MAX_NEGATIVE_NUMBER % LFACTOR2) / 100;
127     cutoff3=  MAX_NEGATIVE_NUMBER % 100;
128   }
129   else
130   {
131     *error= 0;
132     if (*s == '+')
133     {
134       if (++s == end)
135 	goto no_conv;
136     }
137     cutoff=  ULLONG_MAX / LFACTOR2;
138     cutoff2= ULLONG_MAX % LFACTOR2 / 100;
139     cutoff3=  ULLONG_MAX % 100;
140   }
141 
142   /* Handle case where we have a lot of pre-zero */
143   if (*s == '0')
144   {
145     i= 0;
146     do
147     {
148       if (++s == end)
149 	goto end_i;				/* Return 0 */
150     }
151     while (*s == '0');
152     n_end= s+ INIT_CNT;
153   }
154   else
155   {
156     /* Read first digit to check that it's a valid number */
157     if ((c= (*s-'0')) > 9)
158       goto no_conv;
159     i= c;
160     n_end= ++s+ INIT_CNT-1;
161   }
162 
163   /* Handle first 9 digits and store them in i */
164   if (n_end > end)
165     n_end= end;
166   for (; s != n_end ; s++)
167   {
168     if ((c= (*s-'0')) > 9)
169       goto end_i;
170     i= i*10+c;
171   }
172   if (s == end)
173     goto end_i;
174 
175   /* Handle next 9 digits and store them in j */
176   j= 0;
177   start= s;				/* Used to know how much to shift i */
178   n_end= true_end= s + INIT_CNT;
179   if (n_end > end)
180     n_end= end;
181   do
182   {
183     if ((c= (*s-'0')) > 9)
184       goto end_i_and_j;
185     j= j*10+c;
186   } while (++s != n_end);
187   if (s == end)
188   {
189     if (s != true_end)
190       goto end_i_and_j;
191     goto end3;
192   }
193   if ((c= (*s-'0')) > 9)
194     goto end3;
195 
196   /* Handle the next 1 or 2 digits and store them in k */
197   k=c;
198   if (++s == end || (c= (*s-'0')) > 9)
199     goto end4;
200   k= k*10+c;
201   *endptr= (char*) ++s;
202 
203   /* number string should have ended here */
204   if (s != end && (c= (*s-'0')) <= 9)
205     goto overflow;
206 
207   /* Check that we didn't get an overflow with the last digit */
208   if (i > cutoff || (i == cutoff && (j > cutoff2 || (j == cutoff2 &&
209                                      k > cutoff3))))
210     goto overflow;
211   li=i*LFACTOR2+ (ulonglong) j*100 + k;
212   return (longlong) li;
213 
214 overflow:					/* *endptr is set here */
215   *error= MY_ERRNO_ERANGE;
216   return negative ? LLONG_MIN : (longlong) ULLONG_MAX;
217 
218 end_i:
219   *endptr= (char*) s;
220   return (negative ? ((longlong) -(long) i) : (longlong) i);
221 
222 end_i_and_j:
223   li= (ulonglong) i * lfactor[(uint) (s-start)] + j;
224   *endptr= (char*) s;
225   return (negative ? -((longlong) li) : (longlong) li);
226 
227 end3:
228   li=(ulonglong) i*LFACTOR+ (ulonglong) j;
229   *endptr= (char*) s;
230   return (negative ? -((longlong) li) : (longlong) li);
231 
232 end4:
233   li=(ulonglong) i*LFACTOR1+ (ulonglong) j * 10 + k;
234   *endptr= (char*) s;
235   if (negative)
236   {
237    if (li > MAX_NEGATIVE_NUMBER)
238      goto overflow;
239    return -((longlong) li);
240   }
241   return (longlong) li;
242 
243 no_conv:
244   /* There was no number to convert.  */
245   *error= MY_ERRNO_EDOM;
246   *endptr= (char *) nptr;
247   return 0;
248 }
249