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 5package strconv 6 7import "errors" 8 9// ErrRange indicates that a value is out of range for the target type. 10var ErrRange = errors.New("value out of range") 11 12// ErrSyntax indicates that a value does not have the right syntax for the target type. 13var ErrSyntax = errors.New("invalid syntax") 14 15// A NumError records a failed conversion. 16type 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 (e.g. ErrRange, ErrSyntax, etc.) 20} 21 22func (e *NumError) Error() string { 23 return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error() 24} 25 26func syntaxError(fn, str string) *NumError { 27 return &NumError{fn, str, ErrSyntax} 28} 29 30func rangeError(fn, str string) *NumError { 31 return &NumError{fn, str, ErrRange} 32} 33 34func baseError(fn, str string, base int) *NumError { 35 return &NumError{fn, str, errors.New("invalid base " + Itoa(base))} 36} 37 38func bitSizeError(fn, str string, bitSize int) *NumError { 39 return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))} 40} 41 42const intSize = 32 << (^uint(0) >> 63) 43 44// IntSize is the size in bits of an int or uint value. 45const IntSize = intSize 46 47const maxUint64 = 1<<64 - 1 48 49// ParseUint is like ParseInt but for unsigned numbers. 50func ParseUint(s string, base int, bitSize int) (uint64, error) { 51 const fnParseUint = "ParseUint" 52 53 if len(s) == 0 { 54 return 0, syntaxError(fnParseUint, s) 55 } 56 57 s0 := s 58 switch { 59 case 2 <= base && base <= 36: 60 // valid base; nothing to do 61 62 case base == 0: 63 // Look for octal, hex prefix. 64 switch { 65 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 66 if len(s) < 3 { 67 return 0, syntaxError(fnParseUint, s0) 68 } 69 base = 16 70 s = s[2:] 71 case s[0] == '0': 72 base = 8 73 s = s[1:] 74 default: 75 base = 10 76 } 77 78 default: 79 return 0, baseError(fnParseUint, s0, base) 80 } 81 82 if bitSize == 0 { 83 bitSize = int(IntSize) 84 } else if bitSize < 0 || bitSize > 64 { 85 return 0, bitSizeError(fnParseUint, s0, bitSize) 86 } 87 88 // Cutoff is the smallest number such that cutoff*base > maxUint64. 89 // Use compile-time constants for common cases. 90 var cutoff uint64 91 switch base { 92 case 10: 93 cutoff = maxUint64/10 + 1 94 case 16: 95 cutoff = maxUint64/16 + 1 96 default: 97 cutoff = maxUint64/uint64(base) + 1 98 } 99 100 maxVal := uint64(1)<<uint(bitSize) - 1 101 102 var n uint64 103 for _, c := range []byte(s) { 104 var d byte 105 switch { 106 case '0' <= c && c <= '9': 107 d = c - '0' 108 case 'a' <= c && c <= 'z': 109 d = c - 'a' + 10 110 case 'A' <= c && c <= 'Z': 111 d = c - 'A' + 10 112 default: 113 return 0, syntaxError(fnParseUint, s0) 114 } 115 116 if d >= byte(base) { 117 return 0, syntaxError(fnParseUint, s0) 118 } 119 120 if n >= cutoff { 121 // n*base overflows 122 return maxVal, rangeError(fnParseUint, s0) 123 } 124 n *= uint64(base) 125 126 n1 := n + uint64(d) 127 if n1 < n || n1 > maxVal { 128 // n+v overflows 129 return maxVal, rangeError(fnParseUint, s0) 130 } 131 n = n1 132 } 133 134 return n, nil 135} 136 137// ParseInt interprets a string s in the given base (0, 2 to 36) and 138// bit size (0 to 64) and returns the corresponding value i. 139// 140// If base == 0, the base is implied by the string's prefix: 141// base 16 for "0x", base 8 for "0", and base 10 otherwise. 142// For bases 1, below 0 or above 36 an error is returned. 143// 144// The bitSize argument specifies the integer type 145// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 146// correspond to int, int8, int16, int32, and int64. 147// For a bitSize below 0 or above 64 an error is returned. 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. 156func ParseInt(s string, base int, bitSize int) (i int64, err error) { 157 const fnParseInt = "ParseInt" 158 159 // Empty string bad. 160 if len(s) == 0 { 161 return 0, syntaxError(fnParseInt, s) 162 } 163 164 // Pick off leading sign. 165 s0 := s 166 neg := false 167 if s[0] == '+' { 168 s = s[1:] 169 } else if s[0] == '-' { 170 neg = true 171 s = s[1:] 172 } 173 174 // Convert unsigned and check range. 175 var un uint64 176 un, err = ParseUint(s, base, bitSize) 177 if err != nil && err.(*NumError).Err != ErrRange { 178 err.(*NumError).Func = fnParseInt 179 err.(*NumError).Num = s0 180 return 0, err 181 } 182 183 if bitSize == 0 { 184 bitSize = int(IntSize) 185 } 186 187 cutoff := uint64(1 << uint(bitSize-1)) 188 if !neg && un >= cutoff { 189 return int64(cutoff - 1), rangeError(fnParseInt, s0) 190 } 191 if neg && un > cutoff { 192 return -int64(cutoff), rangeError(fnParseInt, s0) 193 } 194 n := int64(un) 195 if neg { 196 n = -n 197 } 198 return n, nil 199} 200 201// Atoi is equivalent to ParseInt(s, 10, 0), converted to type int. 202func Atoi(s string) (int, error) { 203 const fnAtoi = "Atoi" 204 205 sLen := len(s) 206 if intSize == 32 && (0 < sLen && sLen < 10) || 207 intSize == 64 && (0 < sLen && sLen < 19) { 208 // Fast path for small integers that fit int type. 209 s0 := s 210 if s[0] == '-' || s[0] == '+' { 211 s = s[1:] 212 if len(s) < 1 { 213 return 0, &NumError{fnAtoi, s0, ErrSyntax} 214 } 215 } 216 217 n := 0 218 for _, ch := range []byte(s) { 219 ch -= '0' 220 if ch > 9 { 221 return 0, &NumError{fnAtoi, s0, ErrSyntax} 222 } 223 n = n*10 + int(ch) 224 } 225 if s0[0] == '-' { 226 n = -n 227 } 228 return n, nil 229 } 230 231 // Slow path for invalid or big integers. 232 i64, err := ParseInt(s, 10, 0) 233 if nerr, ok := err.(*NumError); ok { 234 nerr.Func = fnAtoi 235 } 236 return int(i64), err 237} 238