Source file src/pkg/strconv/atoi.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package strconv
6
7 import "errors"
8
9 // ErrRange indicates that a value is out of range for the target type.
10 var ErrRange = errors.New("value out of range")
11
12 // ErrSyntax indicates that a value does not have the right syntax for the target type.
13 var ErrSyntax = errors.New("invalid syntax")
14
15 // A NumError records a failed conversion.
16 type NumError struct {
17 Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
18 Num string // the input
19 Err error // the reason the conversion failed (ErrRange, ErrSyntax)
20 }
21
22 func (e *NumError) Error() string {
23 return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error()
24 }
25
26 func syntaxError(fn, str string) *NumError {
27 return &NumError{fn, str, ErrSyntax}
28 }
29
30 func rangeError(fn, str string) *NumError {
31 return &NumError{fn, str, ErrRange}
32 }
33
34 const intSize = 32 << uint(^uint(0)>>63)
35
36 const IntSize = intSize // number of bits in int, uint (32 or 64)
37
38 // Return the first number n such that n*base >= 1<<64.
39 func cutoff64(base int) uint64 {
40 if base < 2 {
41 return 0
42 }
43 return (1<<64-1)/uint64(base) + 1
44 }
45
46 // ParseUint is like ParseInt but for unsigned numbers.
47 func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
48 var cutoff, maxVal uint64
49
50 if bitSize == 0 {
51 bitSize = int(IntSize)
52 }
53
54 s0 := s
55 switch {
56 case len(s) < 1:
57 err = ErrSyntax
58 goto Error
59
60 case 2 <= b && b <= 36:
61 // valid base; nothing to do
62
63 case b == 0:
64 // Look for octal, hex prefix.
65 switch {
66 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
67 b = 16
68 s = s[2:]
69 if len(s) < 1 {
70 err = ErrSyntax
71 goto Error
72 }
73 case s[0] == '0':
74 b = 8
75 default:
76 b = 10
77 }
78
79 default:
80 err = errors.New("invalid base " + Itoa(b))
81 goto Error
82 }
83
84 n = 0
85 cutoff = cutoff64(b)
86 maxVal = 1<<uint(bitSize) - 1
87
88 for i := 0; i < len(s); i++ {
89 var v byte
90 d := s[i]
91 switch {
92 case '0' <= d && d <= '9':
93 v = d - '0'
94 case 'a' <= d && d <= 'z':
95 v = d - 'a' + 10
96 case 'A' <= d && d <= 'Z':
97 v = d - 'A' + 10
98 default:
99 n = 0
100 err = ErrSyntax
101 goto Error
102 }
103 if int(v) >= b {
104 n = 0
105 err = ErrSyntax
106 goto Error
107 }
108
109 if n >= cutoff {
110 // n*b overflows
111 n = 1<<64 - 1
112 err = ErrRange
113 goto Error
114 }
115 n *= uint64(b)
116
117 n1 := n + uint64(v)
118 if n1 < n || n1 > maxVal {
119 // n+v overflows
120 n = 1<<64 - 1
121 err = ErrRange
122 goto Error
123 }
124 n = n1
125 }
126
127 return n, nil
128
129 Error:
130 return n, &NumError{"ParseUint", s0, err}
131 }
132
133 // ParseInt interprets a string s in the given base (2 to 36) and
134 // returns the corresponding value i. If base == 0, the base is
135 // implied by the string's prefix: base 16 for "0x", base 8 for
136 // "0", and base 10 otherwise.
137 //
138 // The bitSize argument specifies the integer type
139 // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
140 // correspond to int, int8, int16, int32, and int64.
141 //
142 // The errors that ParseInt returns have concrete type *NumError
143 // and include err.Num = s. If s is empty or contains invalid
144 // digits, err.Error = ErrSyntax; if the value corresponding
145 // to s cannot be represented by a signed integer of the
146 // given size, err.Error = ErrRange.
147 func ParseInt(s string, base int, bitSize int) (i int64, err error) {
148 const fnParseInt = "ParseInt"
149
150 if bitSize == 0 {
151 bitSize = int(IntSize)
152 }
153
154 // Empty string bad.
155 if len(s) == 0 {
156 return 0, syntaxError(fnParseInt, s)
157 }
158
159 // Pick off leading sign.
160 s0 := s
161 neg := false
162 if s[0] == '+' {
163 s = s[1:]
164 } else if s[0] == '-' {
165 neg = true
166 s = s[1:]
167 }
168
169 // Convert unsigned and check range.
170 var un uint64
171 un, err = ParseUint(s, base, bitSize)
172 if err != nil && err.(*NumError).Err != ErrRange {
173 err.(*NumError).Func = fnParseInt
174 err.(*NumError).Num = s0
175 return 0, err
176 }
177 cutoff := uint64(1 << uint(bitSize-1))
178 if !neg && un >= cutoff {
179 return int64(cutoff - 1), rangeError(fnParseInt, s0)
180 }
181 if neg && un > cutoff {
182 return -int64(cutoff), rangeError(fnParseInt, s0)
183 }
184 n := int64(un)
185 if neg {
186 n = -n
187 }
188 return n, nil
189 }
190
191 // Atoi is shorthand for ParseInt(s, 10, 0).
192 func Atoi(s string) (i int, err error) {
193 i64, err := ParseInt(s, 10, 0)
194 return int(i64), err
195 }