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