1/** 2 * Copyright 2014 Paul Querna 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18/* Portions of this file are on Go stdlib's strconv/atoi.go */ 19// Copyright 2009 The Go Authors. All rights reserved. 20// Use of this source code is governed by a BSD-style 21// license that can be found in the LICENSE file. 22 23package internal 24 25import ( 26 "errors" 27 "strconv" 28) 29 30// ErrRange indicates that a value is out of range for the target type. 31var ErrRange = errors.New("value out of range") 32 33// ErrSyntax indicates that a value does not have the right syntax for the target type. 34var ErrSyntax = errors.New("invalid syntax") 35 36// A NumError records a failed conversion. 37type NumError struct { 38 Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat) 39 Num string // the input 40 Err error // the reason the conversion failed (ErrRange, ErrSyntax) 41} 42 43func (e *NumError) Error() string { 44 return "strconv." + e.Func + ": " + "parsing " + strconv.Quote(e.Num) + ": " + e.Err.Error() 45} 46 47func syntaxError(fn, str string) *NumError { 48 return &NumError{fn, str, ErrSyntax} 49} 50 51func rangeError(fn, str string) *NumError { 52 return &NumError{fn, str, ErrRange} 53} 54 55const intSize = 32 << uint(^uint(0)>>63) 56 57// IntSize is the size in bits of an int or uint value. 58const IntSize = intSize 59 60// Return the first number n such that n*base >= 1<<64. 61func cutoff64(base int) uint64 { 62 if base < 2 { 63 return 0 64 } 65 return (1<<64-1)/uint64(base) + 1 66} 67 68// ParseUint is like ParseInt but for unsigned numbers, and oeprating on []byte 69func ParseUint(s []byte, base int, bitSize int) (n uint64, err error) { 70 var cutoff, maxVal uint64 71 72 if bitSize == 0 { 73 bitSize = int(IntSize) 74 } 75 76 s0 := s 77 switch { 78 case len(s) < 1: 79 err = ErrSyntax 80 goto Error 81 82 case 2 <= base && base <= 36: 83 // valid base; nothing to do 84 85 case base == 0: 86 // Look for octal, hex prefix. 87 switch { 88 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 89 base = 16 90 s = s[2:] 91 if len(s) < 1 { 92 err = ErrSyntax 93 goto Error 94 } 95 case s[0] == '0': 96 base = 8 97 default: 98 base = 10 99 } 100 101 default: 102 err = errors.New("invalid base " + strconv.Itoa(base)) 103 goto Error 104 } 105 106 n = 0 107 cutoff = cutoff64(base) 108 maxVal = 1<<uint(bitSize) - 1 109 110 for i := 0; i < len(s); i++ { 111 var v byte 112 d := s[i] 113 switch { 114 case '0' <= d && d <= '9': 115 v = d - '0' 116 case 'a' <= d && d <= 'z': 117 v = d - 'a' + 10 118 case 'A' <= d && d <= 'Z': 119 v = d - 'A' + 10 120 default: 121 n = 0 122 err = ErrSyntax 123 goto Error 124 } 125 if int(v) >= base { 126 n = 0 127 err = ErrSyntax 128 goto Error 129 } 130 131 if n >= cutoff { 132 // n*base overflows 133 n = 1<<64 - 1 134 err = ErrRange 135 goto Error 136 } 137 n *= uint64(base) 138 139 n1 := n + uint64(v) 140 if n1 < n || n1 > maxVal { 141 // n+v overflows 142 n = 1<<64 - 1 143 err = ErrRange 144 goto Error 145 } 146 n = n1 147 } 148 149 return n, nil 150 151Error: 152 return n, &NumError{"ParseUint", string(s0), err} 153} 154 155// ParseInt interprets a string s in the given base (2 to 36) and 156// returns the corresponding value i. If base == 0, the base is 157// implied by the string's prefix: base 16 for "0x", base 8 for 158// "0", and base 10 otherwise. 159// 160// The bitSize argument specifies the integer type 161// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 162// correspond to int, int8, int16, int32, and int64. 163// 164// The errors that ParseInt returns have concrete type *NumError 165// and include err.Num = s. If s is empty or contains invalid 166// digits, err.Err = ErrSyntax and the returned value is 0; 167// if the value corresponding to s cannot be represented by a 168// signed integer of the given size, err.Err = ErrRange and the 169// returned value is the maximum magnitude integer of the 170// appropriate bitSize and sign. 171func ParseInt(s []byte, base int, bitSize int) (i int64, err error) { 172 const fnParseInt = "ParseInt" 173 174 if bitSize == 0 { 175 bitSize = int(IntSize) 176 } 177 178 // Empty string bad. 179 if len(s) == 0 { 180 return 0, syntaxError(fnParseInt, string(s)) 181 } 182 183 // Pick off leading sign. 184 s0 := s 185 neg := false 186 if s[0] == '+' { 187 s = s[1:] 188 } else if s[0] == '-' { 189 neg = true 190 s = s[1:] 191 } 192 193 // Convert unsigned and check range. 194 var un uint64 195 un, err = ParseUint(s, base, bitSize) 196 if err != nil && err.(*NumError).Err != ErrRange { 197 err.(*NumError).Func = fnParseInt 198 err.(*NumError).Num = string(s0) 199 return 0, err 200 } 201 cutoff := uint64(1 << uint(bitSize-1)) 202 if !neg && un >= cutoff { 203 return int64(cutoff - 1), rangeError(fnParseInt, string(s0)) 204 } 205 if neg && un > cutoff { 206 return -int64(cutoff), rangeError(fnParseInt, string(s0)) 207 } 208 n := int64(un) 209 if neg { 210 n = -n 211 } 212 return n, nil 213} 214