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