github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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  }