blob: d86d0f88b188e3cf02990b0033d6838dbe36c5db [file] [log] [blame]
// 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.
//
// 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) {
if b < 2 || b > 36 {
err = os.ErrorString("invalid base " + Itoa(b));
goto Error;
}
if len(s) < 1 {
err = os.EINVAL;
goto Error;
}
n = 0;
cutoff := cutoff64(b);
for i := 0; i < len(s); i++ {
var v byte;
switch {
case '0' <= s[i] && s[i] <= '9':
v = s[i] - '0';
case 'a' <= s[i] && s[i] <= 'z':
v = s[i] - 'a' + 10;
case 'A' <= s[i] && s[i] <= 'Z':
v = s[i] - '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{s, err};
}
// Atoui64 interprets a string s as an unsigned decimal, octal, or
// hexadecimal number and returns the corresponding value n.
// The default base is decimal. Strings beginning with 0x are
// hexadecimal; strings beginning with 0 are octal.
//
// 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) {
// Empty string bad.
if len(s) == 0 {
return 0, &NumError{s, os.EINVAL}
}
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
n, err = Btoui64(s[2:len(s)], 16);
case s[0] == '0':
n, err = Btoui64(s, 8);
default:
n, err = Btoui64(s, 10);
}
if err != nil {
err.(*NumError).Num = s;
}
return;
}
// Atoi64 is like Atoui64 but allows signed numbers and
// returns its result in an int64.
func Atoi64(s string) (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:len(s)]
} else if s[0] == '-' {
neg = true;
s = s[1:len(s)]
}
// Convert unsigned and check range.
var un uint64;
un, err = Atoui64(s);
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
}
// 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
}