]> Cypherpunks repositories - gostls13.git/commitdiff
correct a design flaw: Atoi etc should not guess the base.
authorRuss Cox <rsc@golang.org>
Mon, 12 Oct 2009 06:46:11 +0000 (23:46 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 12 Oct 2009 06:46:11 +0000 (23:46 -0700)
leave that for Btoi with base == 0.

R=r
DELTA=146  (101 added, 29 deleted, 16 changed)
OCL=35584
CL=35593

src/pkg/strconv/atoi.go
src/pkg/strconv/atoi_test.go

index f31632eff8074711be9d9e909f7e358c29f00c25..2f1d5dec64decc695fae32ec2ea447933f0e2463 100644 (file)
@@ -35,20 +35,43 @@ func cutoff64(base int) uint64 {
 }
 
 // Btoui64 interprets a string s in an arbitrary base b (2 to 36)
-// and returns the corresponding value n.
+// 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) {
-       if b < 2 || b > 36 {
-               err = os.ErrorString("invalid base " + Itoa(b));
-               goto Error;
-       }
-       if len(s) < 1 {
+       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:len(s)];
+                       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;
@@ -95,42 +118,21 @@ func Btoui64(s string, b int) (n uint64, err os.Error) {
        return n, nil;
 
 Error:
-       return n, &NumError{s, err};
+       return n, &NumError{s0, 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 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) {
-       // 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;
+       return Btoui64(s, 10);
 }
 
-
-// Atoi64 is like Atoui64 but allows signed numbers and
+// Btoi64 is like Btoui64 but allows signed numbers and
 // returns its result in an int64.
-func Atoi64(s string) (i int64, err os.Error) {
+func Btoi64(s string, base int) (i int64, err os.Error) {
        // Empty string bad.
        if len(s) == 0 {
                return 0, &NumError{s, os.EINVAL};
@@ -148,7 +150,7 @@ func Atoi64(s string) (i int64, err os.Error) {
 
        // Convert unsigned and check range.
        var un uint64;
-       un, err = Atoui64(s);
+       un, err = Btoui64(s, base);
        if err != nil && err.(*NumError).Error != os.ERANGE {
                err.(*NumError).Num = s0;
                return 0, err;
@@ -166,6 +168,13 @@ func Atoi64(s string) (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);
index adbf7f5c366bc83c0867147091e81528d3fe5542..34eadaad51126e2c12e8705b22b1b3418570c0af 100644 (file)
@@ -18,6 +18,19 @@ type atoui64Test struct {
 }
 
 var atoui64tests = []atoui64Test{
+       atoui64Test{"", 0, os.EINVAL},
+       atoui64Test{"0", 0, nil},
+       atoui64Test{"1", 1, nil},
+       atoui64Test{"12345", 12345, nil},
+       atoui64Test{"012345", 12345, nil},
+       atoui64Test{"12345x", 0, os.EINVAL},
+       atoui64Test{"98765432100", 98765432100, nil},
+       atoui64Test{"18446744073709551615", 1<<64 - 1, nil},
+       atoui64Test{"18446744073709551616", 1<<64 - 1, os.ERANGE},
+       atoui64Test{"18446744073709551620", 1<<64 - 1, os.ERANGE},
+}
+
+var btoui64tests = []atoui64Test{
        atoui64Test{"", 0, os.EINVAL},
        atoui64Test{"0", 0, nil},
        atoui64Test{"1", 1, nil},
@@ -45,6 +58,26 @@ type atoi64Test struct {
 }
 
 var atoi64tests = []atoi64Test{
+       atoi64Test{"", 0, os.EINVAL},
+       atoi64Test{"0", 0, nil},
+       atoi64Test{"-0", 0, nil},
+       atoi64Test{"1", 1, nil},
+       atoi64Test{"-1", -1, nil},
+       atoi64Test{"12345", 12345, nil},
+       atoi64Test{"-12345", -12345, nil},
+       atoi64Test{"012345", 12345, nil},
+       atoi64Test{"-012345", -12345, nil},
+       atoi64Test{"98765432100", 98765432100, nil},
+       atoi64Test{"-98765432100", -98765432100, nil},
+       atoi64Test{"9223372036854775807", 1<<63 - 1, nil},
+       atoi64Test{"-9223372036854775807", -(1<<63 - 1), nil},
+       atoi64Test{"9223372036854775808", 1<<63 - 1, os.ERANGE},
+       atoi64Test{"-9223372036854775808", -1 << 63, nil},
+       atoi64Test{"9223372036854775809", 1<<63 - 1, os.ERANGE},
+       atoi64Test{"-9223372036854775809", -1 << 63, os.ERANGE},
+}
+
+var btoi64tests = []atoi64Test{
        atoi64Test{"", 0, os.EINVAL},
        atoi64Test{"0", 0, nil},
        atoi64Test{"-0", 0, nil},
@@ -79,9 +112,7 @@ var atoui32tests = []atoui32Test{
        atoui32Test{"0", 0, nil},
        atoui32Test{"1", 1, nil},
        atoui32Test{"12345", 12345, nil},
-       atoui32Test{"012345", 012345, nil},
-       atoui32Test{"0x12345", 0x12345, nil},
-       atoui32Test{"0X12345", 0x12345, nil},
+       atoui32Test{"012345", 12345, nil},
        atoui32Test{"12345x", 0, os.EINVAL},
        atoui32Test{"987654321", 987654321, nil},
        atoui32Test{"4294967295", 1<<32 - 1, nil},
@@ -102,10 +133,8 @@ var atoi32tests = []atoi32Test{
        atoi32Test{"-1", -1, nil},
        atoi32Test{"12345", 12345, nil},
        atoi32Test{"-12345", -12345, nil},
-       atoi32Test{"012345", 012345, nil},
-       atoi32Test{"-012345", -012345, nil},
-       atoi32Test{"0x12345", 0x12345, nil},
-       atoi32Test{"-0X12345", -0x12345, nil},
+       atoi32Test{"012345", 12345, nil},
+       atoi32Test{"-012345", -12345, nil},
        atoi32Test{"12345x", 0, os.EINVAL},
        atoi32Test{"-12345x", 0, os.EINVAL},
        atoi32Test{"987654321", 987654321, nil},
@@ -127,12 +156,24 @@ func init() {
                        test.err = &NumError{test.in, test.err};
                }
        }
+       for i := range btoui64tests {
+               test := &btoui64tests[i];
+               if test.err != nil {
+                       test.err = &NumError{test.in, test.err};
+               }
+       }
        for i := range atoi64tests {
                test := &atoi64tests[i];
                if test.err != nil {
                        test.err = &NumError{test.in, test.err};
                }
        }
+       for i := range btoi64tests {
+               test := &btoi64tests[i];
+               if test.err != nil {
+                       test.err = &NumError{test.in, test.err};
+               }
+       }
        for i := range atoui32tests {
                test := &atoui32tests[i];
                if test.err != nil {
@@ -158,6 +199,17 @@ func TestAtoui64(t *testing.T) {
        }
 }
 
+func TestBtoui64(t *testing.T) {
+       for i := range btoui64tests {
+               test := &btoui64tests[i];
+               out, err := Btoui64(test.in, 0);
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("Btoui64(%q) = %v, %v want %v, %v\n",
+                               test.in, out, err, test.out, test.err);
+               }
+       }
+}
+
 func TestAtoi64(t *testing.T) {
        for i := range atoi64tests {
                test := &atoi64tests[i];
@@ -169,6 +221,17 @@ func TestAtoi64(t *testing.T) {
        }
 }
 
+func TestBtoi64(t *testing.T) {
+       for i := range btoi64tests {
+               test := &btoi64tests[i];
+               out, err := Btoi64(test.in, 0);
+               if test.out != out || !reflect.DeepEqual(test.err, err) {
+                       t.Errorf("Btoi64(%q) = %v, %v want %v, %v\n",
+                               test.in, out, err, test.out, test.err);
+               }
+       }
+}
+
 func TestAtoui(t *testing.T) {
        switch IntSize {
        case 32: