github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/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 (e.g. ErrRange, ErrSyntax, etc.)
    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  func baseError(fn, str string, base int) *NumError {
    35  	return &NumError{fn, str, errors.New("invalid base " + Itoa(base))}
    36  }
    37  
    38  func bitSizeError(fn, str string, bitSize int) *NumError {
    39  	return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))}
    40  }
    41  
    42  const intSize = 32 << (^uint(0) >> 63)
    43  
    44  // IntSize is the size in bits of an int or uint value.
    45  const IntSize = intSize
    46  
    47  const maxUint64 = (1<<64 - 1)
    48  
    49  // ParseUint is like ParseInt but for unsigned numbers.
    50  func 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.
   156  func 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 returns the result of ParseInt(s, 10, 0) converted to type int.
   202  func 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  }