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 }