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