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