Home | History | Annotate | Download | only in strconv
      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) (n uint64, err error) {
     43 	var cutoff, maxVal uint64
     44 
     45 	if bitSize == 0 {
     46 		bitSize = int(IntSize)
     47 	}
     48 
     49 	i := 0
     50 	switch {
     51 	case len(s) < 1:
     52 		err = ErrSyntax
     53 		goto Error
     54 
     55 	case 2 <= base && base <= 36:
     56 		// valid base; nothing to do
     57 
     58 	case base == 0:
     59 		// Look for octal, hex prefix.
     60 		switch {
     61 		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
     62 			if len(s) < 3 {
     63 				err = ErrSyntax
     64 				goto Error
     65 			}
     66 			base = 16
     67 			i = 2
     68 		case s[0] == '0':
     69 			base = 8
     70 			i = 1
     71 		default:
     72 			base = 10
     73 		}
     74 
     75 	default:
     76 		err = errors.New("invalid base " + Itoa(base))
     77 		goto Error
     78 	}
     79 
     80 	// Cutoff is the smallest number such that cutoff*base > maxUint64.
     81 	// Use compile-time constants for common cases.
     82 	switch base {
     83 	case 10:
     84 		cutoff = maxUint64/10 + 1
     85 	case 16:
     86 		cutoff = maxUint64/16 + 1
     87 	default:
     88 		cutoff = maxUint64/uint64(base) + 1
     89 	}
     90 
     91 	maxVal = 1<<uint(bitSize) - 1
     92 
     93 	for ; i < len(s); i++ {
     94 		var v byte
     95 		d := s[i]
     96 		switch {
     97 		case '0' <= d && d <= '9':
     98 			v = d - '0'
     99 		case 'a' <= d && d <= 'z':
    100 			v = d - 'a' + 10
    101 		case 'A' <= d && d <= 'Z':
    102 			v = d - 'A' + 10
    103 		default:
    104 			n = 0
    105 			err = ErrSyntax
    106 			goto Error
    107 		}
    108 		if v >= byte(base) {
    109 			n = 0
    110 			err = ErrSyntax
    111 			goto Error
    112 		}
    113 
    114 		if n >= cutoff {
    115 			// n*base overflows
    116 			n = maxUint64
    117 			err = ErrRange
    118 			goto Error
    119 		}
    120 		n *= uint64(base)
    121 
    122 		n1 := n + uint64(v)
    123 		if n1 < n || n1 > maxVal {
    124 			// n+v overflows
    125 			n = maxUint64
    126 			err = ErrRange
    127 			goto Error
    128 		}
    129 		n = n1
    130 	}
    131 
    132 	return n, nil
    133 
    134 Error:
    135 	return n, &NumError{"ParseUint", s, err}
    136 }
    137 
    138 // ParseInt interprets a string s in the given base (2 to 36) and
    139 // returns the corresponding value i.  If base == 0, the base is
    140 // implied by the string's prefix: base 16 for "0x", base 8 for
    141 // "0", and base 10 otherwise.
    142 //
    143 // The bitSize argument specifies the integer type
    144 // that the result must fit into.  Bit sizes 0, 8, 16, 32, and 64
    145 // correspond to int, int8, int16, int32, and int64.
    146 //
    147 // The errors that ParseInt returns have concrete type *NumError
    148 // and include err.Num = s.  If s is empty or contains invalid
    149 // digits, err.Err = ErrSyntax and the returned value is 0;
    150 // if the value corresponding to s cannot be represented by a
    151 // signed integer of the given size, err.Err = ErrRange and the
    152 // returned value is the maximum magnitude integer of the
    153 // appropriate bitSize and sign.
    154 func ParseInt(s string, base int, bitSize int) (i int64, err error) {
    155 	const fnParseInt = "ParseInt"
    156 
    157 	if bitSize == 0 {
    158 		bitSize = int(IntSize)
    159 	}
    160 
    161 	// Empty string bad.
    162 	if len(s) == 0 {
    163 		return 0, syntaxError(fnParseInt, s)
    164 	}
    165 
    166 	// Pick off leading sign.
    167 	s0 := s
    168 	neg := false
    169 	if s[0] == '+' {
    170 		s = s[1:]
    171 	} else if s[0] == '-' {
    172 		neg = true
    173 		s = s[1:]
    174 	}
    175 
    176 	// Convert unsigned and check range.
    177 	var un uint64
    178 	un, err = ParseUint(s, base, bitSize)
    179 	if err != nil && err.(*NumError).Err != ErrRange {
    180 		err.(*NumError).Func = fnParseInt
    181 		err.(*NumError).Num = s0
    182 		return 0, err
    183 	}
    184 	cutoff := uint64(1 << uint(bitSize-1))
    185 	if !neg && un >= cutoff {
    186 		return int64(cutoff - 1), rangeError(fnParseInt, s0)
    187 	}
    188 	if neg && un > cutoff {
    189 		return -int64(cutoff), rangeError(fnParseInt, s0)
    190 	}
    191 	n := int64(un)
    192 	if neg {
    193 		n = -n
    194 	}
    195 	return n, nil
    196 }
    197 
    198 // Atoi is shorthand for ParseInt(s, 10, 0).
    199 func Atoi(s string) (i int, err error) {
    200 	i64, err := ParseInt(s, 10, 0)
    201 	return int(i64), err
    202 }
    203