summaryrefslogtreecommitdiff
path: root/src/pkg/strconv/atoi.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/strconv/atoi.go')
-rw-r--r--src/pkg/strconv/atoi.go151
1 files changed, 72 insertions, 79 deletions
diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go
index 584594216..59ef264d1 100644
--- a/src/pkg/strconv/atoi.go
+++ b/src/pkg/strconv/atoi.go
@@ -4,24 +4,36 @@
package strconv
-import "os"
+import "errors"
+// ErrRange indicates that a value is out of range for the target type.
+var ErrRange = errors.New("value out of range")
+
+// ErrSyntax indicates that a value does not have the right syntax for the target type.
+var ErrSyntax = errors.New("invalid syntax")
+
+// A NumError records a failed conversion.
type NumError struct {
- Num string
- Error os.Error
+ Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
+ Num string // the input
+ Err error // the reason the conversion failed (ErrRange, ErrSyntax)
+}
+
+func (e *NumError) Error() string {
+ return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error()
}
-func (e *NumError) String() string { return `parsing "` + e.Num + `": ` + e.Error.String() }
+func syntaxError(fn, str string) *NumError {
+ return &NumError{fn, str, ErrSyntax}
+}
-func computeIntsize() uint {
- siz := uint(8)
- for 1<<siz != 0 {
- siz *= 2
- }
- return siz
+func rangeError(fn, str string) *NumError {
+ return &NumError{fn, str, ErrRange}
}
-var IntSize = computeIntsize()
+const intSize = 32 << uint(^uint(0)>>63)
+
+const IntSize = intSize // number of bits in int, uint (32 or 64)
// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
@@ -31,22 +43,18 @@ func cutoff64(base int) uint64 {
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) {
- var cutoff uint64
+// ParseUint is like ParseInt but for unsigned numbers.
+func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
+ var cutoff, maxVal uint64
+
+ if bitSize == 0 {
+ bitSize = int(IntSize)
+ }
s0 := s
switch {
case len(s) < 1:
- err = os.EINVAL
+ err = ErrSyntax
goto Error
case 2 <= b && b <= 36:
@@ -59,7 +67,7 @@ func Btoui64(s string, b int) (n uint64, err os.Error) {
b = 16
s = s[2:]
if len(s) < 1 {
- err = os.EINVAL
+ err = ErrSyntax
goto Error
}
case s[0] == '0':
@@ -69,12 +77,13 @@ func Btoui64(s string, b int) (n uint64, err os.Error) {
}
default:
- err = os.NewError("invalid base " + Itoa(b))
+ err = errors.New("invalid base " + Itoa(b))
goto Error
}
n = 0
cutoff = cutoff64(b)
+ maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ {
var v byte
@@ -88,28 +97,28 @@ func Btoui64(s string, b int) (n uint64, err os.Error) {
v = d - 'A' + 10
default:
n = 0
- err = os.EINVAL
+ err = ErrSyntax
goto Error
}
if int(v) >= b {
n = 0
- err = os.EINVAL
+ err = ErrSyntax
goto Error
}
if n >= cutoff {
// n*b overflows
n = 1<<64 - 1
- err = os.ERANGE
+ err = ErrRange
goto Error
}
n *= uint64(b)
n1 := n + uint64(v)
- if n1 < n {
+ if n1 < n || n1 > maxVal {
// n+v overflows
n = 1<<64 - 1
- err = os.ERANGE
+ err = ErrRange
goto Error
}
n = n1
@@ -118,24 +127,33 @@ func Btoui64(s string, b int) (n uint64, err os.Error) {
return n, nil
Error:
- return n, &NumError{s0, err}
+ return n, &NumError{"ParseUint", s0, err}
}
-// Atoui64 interprets a string s as a decimal number and
-// returns the corresponding value n.
+// ParseInt interprets a string s in the given base (2 to 36) and
+// returns the corresponding value i. If base == 0, the base is
+// implied by the string's prefix: base 16 for "0x", base 8 for
+// "0", and base 10 otherwise.
//
-// 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)
-}
+// The bitSize argument specifies the integer type
+// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
+// correspond to int, int8, int16, int32, and int64.
+//
+// The errors that ParseInt returns have concrete type *NumError
+// and include err.Num = s. If s is empty or contains invalid
+// digits, err.Error = ErrSyntax; if the value corresponding
+// to s cannot be represented by a signed integer of the
+// given size, err.Error = ErrRange.
+func ParseInt(s string, base int, bitSize int) (i int64, err error) {
+ const fnParseInt = "ParseInt"
+
+ if bitSize == 0 {
+ bitSize = int(IntSize)
+ }
-// 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}
+ return 0, syntaxError(fnParseInt, s)
}
// Pick off leading sign.
@@ -150,16 +168,18 @@ func Btoi64(s string, base int) (i int64, err os.Error) {
// Convert unsigned and check range.
var un uint64
- un, err = Btoui64(s, base)
- if err != nil && err.(*NumError).Error != os.ERANGE {
+ un, err = ParseUint(s, base, bitSize)
+ if err != nil && err.(*NumError).Err != ErrRange {
+ err.(*NumError).Func = fnParseInt
err.(*NumError).Num = s0
return 0, err
}
- if !neg && un >= 1<<63 {
- return 1<<63 - 1, &NumError{s0, os.ERANGE}
+ cutoff := uint64(1 << uint(bitSize-1))
+ if !neg && un >= cutoff {
+ return int64(cutoff - 1), rangeError(fnParseInt, s0)
}
- if neg && un > 1<<63 {
- return -1 << 63, &NumError{s0, os.ERANGE}
+ if neg && un > cutoff {
+ return -int64(cutoff), rangeError(fnParseInt, s0)
}
n := int64(un)
if neg {
@@ -168,35 +188,8 @@ func Btoi64(s string, base int) (i int64, err os.Error) {
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
+// Atoi is shorthand for ParseInt(s, 10, 0).
+func Atoi(s string) (i int, err error) {
+ i64, err := ParseInt(s, 10, 0)
+ return int(i64), err
}