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