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