github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/strconv/atoi.go (about) 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 " + Quote(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(0) >> 63) 35 36 // IntSize is the size in bits of an int or uint value. 37 const IntSize = intSize 38 39 // Return the first number n such that n*base >= 1<<64. 40 func cutoff64(base int) uint64 { 41 if base < 2 { 42 return 0 43 } 44 return (1<<64-1)/uint64(base) + 1 45 } 46 47 // ParseUint is like ParseInt but for unsigned numbers. 48 func ParseUint(s string, base int, bitSize int) (n uint64, err error) { 49 var cutoff, maxVal uint64 50 51 if bitSize == 0 { 52 bitSize = int(IntSize) 53 } 54 55 s0 := s 56 switch { 57 case len(s) < 1: 58 err = ErrSyntax 59 goto Error 60 61 case 2 <= base && base <= 36: 62 // valid base; nothing to do 63 64 case base == 0: 65 // Look for octal, hex prefix. 66 switch { 67 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 68 base = 16 69 s = s[2:] 70 if len(s) < 1 { 71 err = ErrSyntax 72 goto Error 73 } 74 case s[0] == '0': 75 base = 8 76 default: 77 base = 10 78 } 79 80 default: 81 err = errors.New("invalid base " + Itoa(base)) 82 goto Error 83 } 84 85 n = 0 86 cutoff = cutoff64(base) 87 maxVal = 1<<uint(bitSize) - 1 88 89 for i := 0; i < len(s); i++ { 90 var v byte 91 d := s[i] 92 switch { 93 case '0' <= d && d <= '9': 94 v = d - '0' 95 case 'a' <= d && d <= 'z': 96 v = d - 'a' + 10 97 case 'A' <= d && d <= 'Z': 98 v = d - 'A' + 10 99 default: 100 n = 0 101 err = ErrSyntax 102 goto Error 103 } 104 if int(v) >= base { 105 n = 0 106 err = ErrSyntax 107 goto Error 108 } 109 110 if n >= cutoff { 111 // n*base overflows 112 n = 1<<64 - 1 113 err = ErrRange 114 goto Error 115 } 116 n *= uint64(base) 117 118 n1 := n + uint64(v) 119 if n1 < n || n1 > maxVal { 120 // n+v overflows 121 n = 1<<64 - 1 122 err = ErrRange 123 goto Error 124 } 125 n = n1 126 } 127 128 return n, nil 129 130 Error: 131 return n, &NumError{"ParseUint", s0, err} 132 } 133 134 // ParseInt interprets a string s in the given base (2 to 36) and 135 // returns the corresponding value i. If base == 0, the base is 136 // implied by the string's prefix: base 16 for "0x", base 8 for 137 // "0", and base 10 otherwise. 138 // 139 // The bitSize argument specifies the integer type 140 // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 141 // correspond to int, int8, int16, int32, and int64. 142 // 143 // The errors that ParseInt returns have concrete type *NumError 144 // and include err.Num = s. If s is empty or contains invalid 145 // digits, err.Err = ErrSyntax and the returned value is 0; 146 // if the value corresponding to s cannot be represented by a 147 // signed integer of the given size, err.Err = ErrRange and the 148 // returned value is the maximum magnitude integer of the 149 // appropriate bitSize and sign. 150 func ParseInt(s string, base int, bitSize int) (i int64, err error) { 151 const fnParseInt = "ParseInt" 152 153 if bitSize == 0 { 154 bitSize = int(IntSize) 155 } 156 157 // Empty string bad. 158 if len(s) == 0 { 159 return 0, syntaxError(fnParseInt, s) 160 } 161 162 // Pick off leading sign. 163 s0 := s 164 neg := false 165 if s[0] == '+' { 166 s = s[1:] 167 } else if s[0] == '-' { 168 neg = true 169 s = s[1:] 170 } 171 172 // Convert unsigned and check range. 173 var un uint64 174 un, err = ParseUint(s, base, bitSize) 175 if err != nil && err.(*NumError).Err != ErrRange { 176 err.(*NumError).Func = fnParseInt 177 err.(*NumError).Num = s0 178 return 0, err 179 } 180 cutoff := uint64(1 << uint(bitSize-1)) 181 if !neg && un >= cutoff { 182 return int64(cutoff - 1), rangeError(fnParseInt, s0) 183 } 184 if neg && un > cutoff { 185 return -int64(cutoff), rangeError(fnParseInt, s0) 186 } 187 n := int64(un) 188 if neg { 189 n = -n 190 } 191 return n, nil 192 } 193 194 // Atoi is shorthand for ParseInt(s, 10, 0). 195 func Atoi(s string) (i int, err error) { 196 i64, err := ParseInt(s, 10, 0) 197 return int(i64), err 198 }