src/pkg/strconv/atoi.go - The Go Programming Language

Golang

Source file src/pkg/strconv/atoi.go

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