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) (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 }
    209