1 | /* Copyright (c) 2003 TXT DataKonsult Ab |
2 | Copyright (c) 2009, 2013, Monty Program Ab. |
3 | |
4 | Redistribution and use in source and binary forms, with or without |
5 | modification, are permitted provided that the following conditions are |
6 | met: |
7 | |
8 | 1. Redistributions of source code must retain the above copyright |
9 | notice, this list of conditions and the following disclaimer. |
10 | |
11 | 2. Redistributions in binary form must the following disclaimer in |
12 | the documentation and/or other materials provided with the |
13 | distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY |
16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
18 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR |
19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
22 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
25 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include "strings_def.h" |
30 | #include <my_sys.h> /* Needed for MY_ERRNO_ERANGE */ |
31 | |
32 | #define MAX_NEGATIVE_NUMBER ((ulonglong) 0x8000000000000000ULL) |
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 | SYNOPSIS |
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 LONGLONG_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 | |
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= ULONGLONG_MAX / LFACTOR2; |
138 | cutoff2= ULONGLONG_MAX % LFACTOR2 / 100; |
139 | cutoff3= ULONGLONG_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 ? LONGLONG_MIN : (longlong) ULONGLONG_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 | |