...
Run Format

Source file src/strconv/atoi.go

Documentation: strconv

  // Copyright 2009 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package strconv
  
  import "errors"
  
  // ErrRange indicates that a value is out of range for the target type.
  var ErrRange = errors.New("value out of range")
  
  // ErrSyntax indicates that a value does not have the right syntax for the target type.
  var ErrSyntax = errors.New("invalid syntax")
  
  // A NumError records a failed conversion.
  type NumError struct {
  	Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
  	Num  string // the input
  	Err  error  // the reason the conversion failed (ErrRange, ErrSyntax)
  }
  
  func (e *NumError) Error() string {
  	return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
  }
  
  func syntaxError(fn, str string) *NumError {
  	return &NumError{fn, str, ErrSyntax}
  }
  
  func rangeError(fn, str string) *NumError {
  	return &NumError{fn, str, ErrRange}
  }
  
  const intSize = 32 << (^uint(0) >> 63)
  
  // IntSize is the size in bits of an int or uint value.
  const IntSize = intSize
  
  const maxUint64 = (1<<64 - 1)
  
  // ParseUint is like ParseInt but for unsigned numbers.
  func ParseUint(s string, base int, bitSize int) (uint64, error) {
  	var n uint64
  	var err error
  	var cutoff, maxVal uint64
  
  	if bitSize == 0 {
  		bitSize = int(IntSize)
  	}
  
  	i := 0
  	switch {
  	case len(s) < 1:
  		err = ErrSyntax
  		goto Error
  
  	case 2 <= base && base <= 36:
  		// valid base; nothing to do
  
  	case base == 0:
  		// Look for octal, hex prefix.
  		switch {
  		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
  			if len(s) < 3 {
  				err = ErrSyntax
  				goto Error
  			}
  			base = 16
  			i = 2
  		case s[0] == '0':
  			base = 8
  			i = 1
  		default:
  			base = 10
  		}
  
  	default:
  		err = errors.New("invalid base " + Itoa(base))
  		goto Error
  	}
  
  	// Cutoff is the smallest number such that cutoff*base > maxUint64.
  	// Use compile-time constants for common cases.
  	switch base {
  	case 10:
  		cutoff = maxUint64/10 + 1
  	case 16:
  		cutoff = maxUint64/16 + 1
  	default:
  		cutoff = maxUint64/uint64(base) + 1
  	}
  
  	maxVal = 1<<uint(bitSize) - 1
  
  	for ; i < len(s); i++ {
  		var v byte
  		d := s[i]
  		switch {
  		case '0' <= d && d <= '9':
  			v = d - '0'
  		case 'a' <= d && d <= 'z':
  			v = d - 'a' + 10
  		case 'A' <= d && d <= 'Z':
  			v = d - 'A' + 10
  		default:
  			n = 0
  			err = ErrSyntax
  			goto Error
  		}
  		if v >= byte(base) {
  			n = 0
  			err = ErrSyntax
  			goto Error
  		}
  
  		if n >= cutoff {
  			// n*base overflows
  			n = maxUint64
  			err = ErrRange
  			goto Error
  		}
  		n *= uint64(base)
  
  		n1 := n + uint64(v)
  		if n1 < n || n1 > maxVal {
  			// n+v overflows
  			n = maxUint64
  			err = ErrRange
  			goto Error
  		}
  		n = n1
  	}
  
  	return n, nil
  
  Error:
  	return n, &NumError{"ParseUint", s, err}
  }
  
  // ParseInt interprets a string s in the given base (2 to 36) and
  // returns the corresponding value i. If base == 0, the base is
  // implied by the string's prefix: base 16 for "0x", base 8 for
  // "0", and base 10 otherwise.
  //
  // The bitSize argument specifies the integer type
  // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
  // correspond to int, int8, int16, int32, and int64.
  //
  // The errors that ParseInt returns have concrete type *NumError
  // and include err.Num = s. If s is empty or contains invalid
  // digits, err.Err = ErrSyntax and the returned value is 0;
  // if the value corresponding to s cannot be represented by a
  // signed integer of the given size, err.Err = ErrRange and the
  // returned value is the maximum magnitude integer of the
  // appropriate bitSize and sign.
  func ParseInt(s string, base int, bitSize int) (i int64, err error) {
  	const fnParseInt = "ParseInt"
  
  	if bitSize == 0 {
  		bitSize = int(IntSize)
  	}
  
  	// Empty string bad.
  	if len(s) == 0 {
  		return 0, syntaxError(fnParseInt, s)
  	}
  
  	// Pick off leading sign.
  	s0 := s
  	neg := false
  	if s[0] == '+' {
  		s = s[1:]
  	} else if s[0] == '-' {
  		neg = true
  		s = s[1:]
  	}
  
  	// Convert unsigned and check range.
  	var un uint64
  	un, err = ParseUint(s, base, bitSize)
  	if err != nil && err.(*NumError).Err != ErrRange {
  		err.(*NumError).Func = fnParseInt
  		err.(*NumError).Num = s0
  		return 0, err
  	}
  	cutoff := uint64(1 << uint(bitSize-1))
  	if !neg && un >= cutoff {
  		return int64(cutoff - 1), rangeError(fnParseInt, s0)
  	}
  	if neg && un > cutoff {
  		return -int64(cutoff), rangeError(fnParseInt, s0)
  	}
  	n := int64(un)
  	if neg {
  		n = -n
  	}
  	return n, nil
  }
  
  // Atoi returns the result of ParseInt(s, 10, 0) converted to type int.
  func Atoi(s string) (int, error) {
  	const fnAtoi = "Atoi"
  	i64, err := ParseInt(s, 10, 0)
  	if nerr, ok := err.(*NumError); ok {
  		nerr.Func = fnAtoi
  	}
  	return int(i64), err
  }
  

View as plain text