The Go Programming Language

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 "os"
     8	
     9	type NumError struct {
    10		Num   string
    11		Error os.Error
    12	}
    13	
    14	func (e *NumError) String() string { return `parsing "` + e.Num + `": ` + e.Error.String() }
    15	
    16	func computeIntsize() uint {
    17		siz := uint(8)
    18		for 1<<siz != 0 {
    19			siz *= 2
    20		}
    21		return siz
    22	}
    23	
    24	var IntSize = computeIntsize()
    25	
    26	// Return the first number n such that n*base >= 1<<64.
    27	func cutoff64(base int) uint64 {
    28		if base < 2 {
    29			return 0
    30		}
    31		return (1<<64-1)/uint64(base) + 1
    32	}
    33	
    34	// Btoui64 interprets a string s in an arbitrary base b (2 to 36)
    35	// and returns the corresponding value n.  If b == 0, the base
    36	// is taken from the string prefix: base 16 for "0x", base 8 for "0",
    37	// and base 10 otherwise.
    38	//
    39	// The errors that Btoui64 returns have concrete type *NumError
    40	// and include err.Num = s.  If s is empty or contains invalid
    41	// digits, err.Error = os.EINVAL; if the value corresponding
    42	// to s cannot be represented by a uint64, err.Error = os.ERANGE.
    43	func Btoui64(s string, b int) (n uint64, err os.Error) {
    44		var cutoff uint64
    45	
    46		s0 := s
    47		switch {
    48		case len(s) < 1:
    49			err = os.EINVAL
    50			goto Error
    51	
    52		case 2 <= b && b <= 36:
    53			// valid base; nothing to do
    54	
    55		case b == 0:
    56			// Look for octal, hex prefix.
    57			switch {
    58			case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
    59				b = 16
    60				s = s[2:]
    61				if len(s) < 1 {
    62					err = os.EINVAL
    63					goto Error
    64				}
    65			case s[0] == '0':
    66				b = 8
    67			default:
    68				b = 10
    69			}
    70	
    71		default:
    72			err = os.NewError("invalid base " + Itoa(b))
    73			goto Error
    74		}
    75	
    76		n = 0
    77		cutoff = cutoff64(b)
    78	
    79		for i := 0; i < len(s); i++ {
    80			var v byte
    81			d := s[i]
    82			switch {
    83			case '0' <= d && d <= '9':
    84				v = d - '0'
    85			case 'a' <= d && d <= 'z':
    86				v = d - 'a' + 10
    87			case 'A' <= d && d <= 'Z':
    88				v = d - 'A' + 10
    89			default:
    90				n = 0
    91				err = os.EINVAL
    92				goto Error
    93			}
    94			if int(v) >= b {
    95				n = 0
    96				err = os.EINVAL
    97				goto Error
    98			}
    99	
   100			if n >= cutoff {
   101				// n*b overflows
   102				n = 1<<64 - 1
   103				err = os.ERANGE
   104				goto Error
   105			}
   106			n *= uint64(b)
   107	
   108			n1 := n + uint64(v)
   109			if n1 < n {
   110				// n+v overflows
   111				n = 1<<64 - 1
   112				err = os.ERANGE
   113				goto Error
   114			}
   115			n = n1
   116		}
   117	
   118		return n, nil
   119	
   120	Error:
   121		return n, &NumError{s0, err}
   122	}
   123	
   124	// Atoui64 interprets a string s as a decimal number and
   125	// returns the corresponding value n.
   126	//
   127	// Atoui64 returns err == os.EINVAL if s is empty or contains invalid digits.
   128	// It returns err == os.ERANGE if s cannot be represented by a uint64.
   129	func Atoui64(s string) (n uint64, err os.Error) {
   130		return Btoui64(s, 10)
   131	}
   132	
   133	// Btoi64 is like Btoui64 but allows signed numbers and
   134	// returns its result in an int64.
   135	func Btoi64(s string, base int) (i int64, err os.Error) {
   136		// Empty string bad.
   137		if len(s) == 0 {
   138			return 0, &NumError{s, os.EINVAL}
   139		}
   140	
   141		// Pick off leading sign.
   142		s0 := s
   143		neg := false
   144		if s[0] == '+' {
   145			s = s[1:]
   146		} else if s[0] == '-' {
   147			neg = true
   148			s = s[1:]
   149		}
   150	
   151		// Convert unsigned and check range.
   152		var un uint64
   153		un, err = Btoui64(s, base)
   154		if err != nil && err.(*NumError).Error != os.ERANGE {
   155			err.(*NumError).Num = s0
   156			return 0, err
   157		}
   158		if !neg && un >= 1<<63 {
   159			return 1<<63 - 1, &NumError{s0, os.ERANGE}
   160		}
   161		if neg && un > 1<<63 {
   162			return -1 << 63, &NumError{s0, os.ERANGE}
   163		}
   164		n := int64(un)
   165		if neg {
   166			n = -n
   167		}
   168		return n, nil
   169	}
   170	
   171	// Atoi64 is like Atoui64 but allows signed numbers and
   172	// returns its result in an int64.
   173	func Atoi64(s string) (i int64, err os.Error) { return Btoi64(s, 10) }
   174	
   175	// Atoui is like Atoui64 but returns its result as a uint.
   176	func Atoui(s string) (i uint, err os.Error) {
   177		i1, e1 := Atoui64(s)
   178		if e1 != nil && e1.(*NumError).Error != os.ERANGE {
   179			return 0, e1
   180		}
   181		i = uint(i1)
   182		if uint64(i) != i1 {
   183			return ^uint(0), &NumError{s, os.ERANGE}
   184		}
   185		return i, nil
   186	}
   187	
   188	// Atoi is like Atoi64 but returns its result as an int.
   189	func Atoi(s string) (i int, err os.Error) {
   190		i1, e1 := Atoi64(s)
   191		if e1 != nil && e1.(*NumError).Error != os.ERANGE {
   192			return 0, e1
   193		}
   194		i = int(i1)
   195		if int64(i) != i1 {
   196			if i1 < 0 {
   197				return -1 << (IntSize - 1), &NumError{s, os.ERANGE}
   198			}
   199			return 1<<(IntSize-1) - 1, &NumError{s, os.ERANGE}
   200		}
   201		return i, nil
   202	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.