| // 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 "os" | |
| type NumError struct { | |
| Num string | |
| Error os.Error | |
| } | |
| func (e *NumError) String() string { return `parsing "` + e.Num + `": ` + e.Error.String() } | |
| func computeIntsize() uint { | |
| siz := uint(8) | |
| for 1<<siz != 0 { | |
| siz *= 2 | |
| } | |
| return siz | |
| } | |
| var IntSize = computeIntsize() | |
| // Return the first number n such that n*base >= 1<<64. | |
| func cutoff64(base int) uint64 { | |
| if base < 2 { | |
| return 0 | |
| } | |
| return (1<<64-1)/uint64(base) + 1 | |
| } | |
| // Btoui64 interprets a string s in an arbitrary base b (2 to 36) | |
| // and returns the corresponding value n. If b == 0, the base | |
| // is taken from the string prefix: base 16 for "0x", base 8 for "0", | |
| // and base 10 otherwise. | |
| // | |
| // The errors that Btoui64 returns have concrete type *NumError | |
| // and include err.Num = s. If s is empty or contains invalid | |
| // digits, err.Error = os.EINVAL; if the value corresponding | |
| // to s cannot be represented by a uint64, err.Error = os.ERANGE. | |
| func Btoui64(s string, b int) (n uint64, err os.Error) { | |
| s0 := s | |
| switch { | |
| case len(s) < 1: | |
| err = os.EINVAL | |
| goto Error | |
| case 2 <= b && b <= 36: | |
| // valid base; nothing to do | |
| case b == 0: | |
| // Look for octal, hex prefix. | |
| switch { | |
| case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): | |
| b = 16 | |
| s = s[2:] | |
| if len(s) < 1 { | |
| err = os.EINVAL | |
| goto Error | |
| } | |
| case s[0] == '0': | |
| b = 8 | |
| default: | |
| b = 10 | |
| } | |
| default: | |
| err = os.ErrorString("invalid base " + Itoa(b)) | |
| goto Error | |
| } | |
| n = 0 | |
| cutoff := cutoff64(b) | |
| for i := 0; 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 = os.EINVAL | |
| goto Error | |
| } | |
| if int(v) >= b { | |
| n = 0 | |
| err = os.EINVAL | |
| goto Error | |
| } | |
| if n >= cutoff { | |
| // n*b overflows | |
| n = 1<<64 - 1 | |
| err = os.ERANGE | |
| goto Error | |
| } | |
| n *= uint64(b) | |
| n1 := n + uint64(v) | |
| if n1 < n { | |
| // n+v overflows | |
| n = 1<<64 - 1 | |
| err = os.ERANGE | |
| goto Error | |
| } | |
| n = n1 | |
| } | |
| return n, nil | |
| Error: | |
| return n, &NumError{s0, err} | |
| } | |
| // Atoui64 interprets a string s as a decimal number and | |
| // returns the corresponding value n. | |
| // | |
| // Atoui64 returns err == os.EINVAL if s is empty or contains invalid digits. | |
| // It returns err == os.ERANGE if s cannot be represented by a uint64. | |
| func Atoui64(s string) (n uint64, err os.Error) { | |
| return Btoui64(s, 10) | |
| } | |
| // Btoi64 is like Btoui64 but allows signed numbers and | |
| // returns its result in an int64. | |
| func Btoi64(s string, base int) (i int64, err os.Error) { | |
| // Empty string bad. | |
| if len(s) == 0 { | |
| return 0, &NumError{s, os.EINVAL} | |
| } | |
| // 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 = Btoui64(s, base) | |
| if err != nil && err.(*NumError).Error != os.ERANGE { | |
| err.(*NumError).Num = s0 | |
| return 0, err | |
| } | |
| if !neg && un >= 1<<63 { | |
| return 1<<63 - 1, &NumError{s0, os.ERANGE} | |
| } | |
| if neg && un > 1<<63 { | |
| return -1 << 63, &NumError{s0, os.ERANGE} | |
| } | |
| n := int64(un) | |
| if neg { | |
| n = -n | |
| } | |
| return n, nil | |
| } | |
| // Atoi64 is like Atoui64 but allows signed numbers and | |
| // returns its result in an int64. | |
| func Atoi64(s string) (i int64, err os.Error) { return Btoi64(s, 10) } | |
| // Atoui is like Atoui64 but returns its result as a uint. | |
| func Atoui(s string) (i uint, err os.Error) { | |
| i1, e1 := Atoui64(s) | |
| if e1 != nil && e1.(*NumError).Error != os.ERANGE { | |
| return 0, e1 | |
| } | |
| i = uint(i1) | |
| if uint64(i) != i1 { | |
| return ^uint(0), &NumError{s, os.ERANGE} | |
| } | |
| return i, nil | |
| } | |
| // Atoi is like Atoi64 but returns its result as an int. | |
| func Atoi(s string) (i int, err os.Error) { | |
| i1, e1 := Atoi64(s) | |
| if e1 != nil && e1.(*NumError).Error != os.ERANGE { | |
| return 0, e1 | |
| } | |
| i = int(i1) | |
| if int64(i) != i1 { | |
| if i1 < 0 { | |
| return -1 << (IntSize - 1), &NumError{s, os.ERANGE} | |
| } | |
| return 1<<(IntSize-1) - 1, &NumError{s, os.ERANGE} | |
| } | |
| return i, nil | |
| } |