Source file
src/strconv/atoi.go
Documentation: strconv
1
2
3
4
5 package strconv
6
7 import "errors"
8
9
10
11
12
13 func lower(c byte) byte {
14 return c | ('x' - 'X')
15 }
16
17
18 var ErrRange = errors.New("value out of range")
19
20
21 var ErrSyntax = errors.New("invalid syntax")
22
23
24 type NumError struct {
25 Func string
26 Num string
27 Err error
28 }
29
30 func (e *NumError) Error() string {
31 return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
32 }
33
34 func (e *NumError) Unwrap() error { return e.Err }
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func cloneString(x string) string { return string([]byte(x)) }
51
52 func syntaxError(fn, str string) *NumError {
53 return &NumError{fn, cloneString(str), ErrSyntax}
54 }
55
56 func rangeError(fn, str string) *NumError {
57 return &NumError{fn, cloneString(str), ErrRange}
58 }
59
60 func baseError(fn, str string, base int) *NumError {
61 return &NumError{fn, cloneString(str), errors.New("invalid base " + Itoa(base))}
62 }
63
64 func bitSizeError(fn, str string, bitSize int) *NumError {
65 return &NumError{fn, cloneString(str), errors.New("invalid bit size " + Itoa(bitSize))}
66 }
67
68 const intSize = 32 << (^uint(0) >> 63)
69
70
71 const IntSize = intSize
72
73 const maxUint64 = 1<<64 - 1
74
75
76
77
78 func ParseUint(s string, base int, bitSize int) (uint64, error) {
79 const fnParseUint = "ParseUint"
80
81 if s == "" {
82 return 0, syntaxError(fnParseUint, s)
83 }
84
85 base0 := base == 0
86
87 s0 := s
88 switch {
89 case 2 <= base && base <= 36:
90
91
92 case base == 0:
93
94 base = 10
95 if s[0] == '0' {
96 switch {
97 case len(s) >= 3 && lower(s[1]) == 'b':
98 base = 2
99 s = s[2:]
100 case len(s) >= 3 && lower(s[1]) == 'o':
101 base = 8
102 s = s[2:]
103 case len(s) >= 3 && lower(s[1]) == 'x':
104 base = 16
105 s = s[2:]
106 default:
107 base = 8
108 s = s[1:]
109 }
110 }
111
112 default:
113 return 0, baseError(fnParseUint, s0, base)
114 }
115
116 if bitSize == 0 {
117 bitSize = IntSize
118 } else if bitSize < 0 || bitSize > 64 {
119 return 0, bitSizeError(fnParseUint, s0, bitSize)
120 }
121
122
123
124 var cutoff uint64
125 switch base {
126 case 10:
127 cutoff = maxUint64/10 + 1
128 case 16:
129 cutoff = maxUint64/16 + 1
130 default:
131 cutoff = maxUint64/uint64(base) + 1
132 }
133
134 maxVal := uint64(1)<<uint(bitSize) - 1
135
136 underscores := false
137 var n uint64
138 for _, c := range []byte(s) {
139 var d byte
140 switch {
141 case c == '_' && base0:
142 underscores = true
143 continue
144 case '0' <= c && c <= '9':
145 d = c - '0'
146 case 'a' <= lower(c) && lower(c) <= 'z':
147 d = lower(c) - 'a' + 10
148 default:
149 return 0, syntaxError(fnParseUint, s0)
150 }
151
152 if d >= byte(base) {
153 return 0, syntaxError(fnParseUint, s0)
154 }
155
156 if n >= cutoff {
157
158 return maxVal, rangeError(fnParseUint, s0)
159 }
160 n *= uint64(base)
161
162 n1 := n + uint64(d)
163 if n1 < n || n1 > maxVal {
164
165 return maxVal, rangeError(fnParseUint, s0)
166 }
167 n = n1
168 }
169
170 if underscores && !underscoreOK(s0) {
171 return 0, syntaxError(fnParseUint, s0)
172 }
173
174 return n, nil
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 func ParseInt(s string, base int, bitSize int) (i int64, err error) {
203 const fnParseInt = "ParseInt"
204
205 if s == "" {
206 return 0, syntaxError(fnParseInt, s)
207 }
208
209
210 s0 := s
211 neg := false
212 if s[0] == '+' {
213 s = s[1:]
214 } else if s[0] == '-' {
215 neg = true
216 s = s[1:]
217 }
218
219
220 var un uint64
221 un, err = ParseUint(s, base, bitSize)
222 if err != nil && err.(*NumError).Err != ErrRange {
223 err.(*NumError).Func = fnParseInt
224 err.(*NumError).Num = cloneString(s0)
225 return 0, err
226 }
227
228 if bitSize == 0 {
229 bitSize = IntSize
230 }
231
232 cutoff := uint64(1 << uint(bitSize-1))
233 if !neg && un >= cutoff {
234 return int64(cutoff - 1), rangeError(fnParseInt, s0)
235 }
236 if neg && un > cutoff {
237 return -int64(cutoff), rangeError(fnParseInt, s0)
238 }
239 n := int64(un)
240 if neg {
241 n = -n
242 }
243 return n, nil
244 }
245
246
247 func Atoi(s string) (int, error) {
248 const fnAtoi = "Atoi"
249
250 sLen := len(s)
251 if intSize == 32 && (0 < sLen && sLen < 10) ||
252 intSize == 64 && (0 < sLen && sLen < 19) {
253
254 s0 := s
255 if s[0] == '-' || s[0] == '+' {
256 s = s[1:]
257 if len(s) < 1 {
258 return 0, syntaxError(fnAtoi, s0)
259 }
260 }
261
262 n := 0
263 for _, ch := range []byte(s) {
264 ch -= '0'
265 if ch > 9 {
266 return 0, syntaxError(fnAtoi, s0)
267 }
268 n = n*10 + int(ch)
269 }
270 if s0[0] == '-' {
271 n = -n
272 }
273 return n, nil
274 }
275
276
277 i64, err := ParseInt(s, 10, 0)
278 if nerr, ok := err.(*NumError); ok {
279 nerr.Func = fnAtoi
280 }
281 return int(i64), err
282 }
283
284
285
286
287 func underscoreOK(s string) bool {
288
289
290
291
292
293 saw := '^'
294 i := 0
295
296
297 if len(s) >= 1 && (s[0] == '-' || s[0] == '+') {
298 s = s[1:]
299 }
300
301
302 hex := false
303 if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {
304 i = 2
305 saw = '0'
306 hex = lower(s[1]) == 'x'
307 }
308
309
310 for ; i < len(s); i++ {
311
312 if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' {
313 saw = '0'
314 continue
315 }
316
317 if s[i] == '_' {
318 if saw != '0' {
319 return false
320 }
321 saw = '_'
322 continue
323 }
324
325 if saw == '_' {
326 return false
327 }
328
329 saw = '!'
330 }
331 return saw != '_'
332 }
333
View as plain text