]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: provide support for conversion bases up to 62
authorgriesemer <gri@golang.org>
Mon, 25 Sep 2017 13:54:48 +0000 (15:54 +0200)
committerRobert Griesemer <gri@golang.org>
Fri, 6 Oct 2017 17:46:15 +0000 (17:46 +0000)
Increase MaxBase from 36 to 62 and extend the conversion
alphabet with the upper-case letters 'A' to 'Z'. For int
conversions with bases <= 36, the letters 'A' to 'Z' have
the same values (10 to 35) as the corresponding lower-case
letters. For conversion bases > 36 up to 62, the upper-case
letters have the values 36 to 61.

Added MaxBase to api/except.txt: Clients should not make
assumptions about the value of MaxBase being constant.

The core of the change is in natconv.go. The remaining
changes are adjusted tests and documentation.

Fixes #21558.

Change-Id: I5f74da633caafca03993e13f32ac9546c572cc84
Reviewed-on: https://go-review.googlesource.com/65970
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
api/except.txt
api/next.txt
src/math/big/int.go
src/math/big/intconv.go
src/math/big/intconv_test.go
src/math/big/natconv.go
src/math/big/natconv_test.go

index fbabd18a8100ef1ddc504315f25b31c8ddbe10f2..5c0837e3f7f1370f2a3b7040739c6071d74417a4 100644 (file)
@@ -1,4 +1,5 @@
 pkg encoding/json, method (*RawMessage) MarshalJSON() ([]uint8, error)
+pkg math/big, const MaxBase = 36
 pkg math/big, type Word uintptr
 pkg net, func ListenUnixgram(string, *UnixAddr) (*UDPConn, error)
 pkg os (linux-arm), const O_SYNC = 4096
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..52288941a731f8b62385a44ef4714b11d638bb03 100644 (file)
@@ -0,0 +1 @@
+pkg math/big, const MaxBase = 62
index 000eab50b787daa0ca6bd30779da23d4e12ad450..92e2ae954a1e5d34abd1f65851553babcac7bd4e 100644 (file)
@@ -389,6 +389,11 @@ func (x *Int) IsUint64() bool {
 // ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a
 // ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
 //
+// For bases <= 36, lower and upper case letters are considered the same:
+// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35.
+// For bases > 36, the upper case letters 'A' to 'Z' represent the digit
+// values 36 to 61.
+//
 func (z *Int) SetString(s string, base int) (*Int, bool) {
        return z.setFromScanner(strings.NewReader(s), base)
 }
index 5ac61020d1d31dea7239dd6d56e6c491e6d6dfcd..6cca827c8e3412640a7b070faa6fe9d324e8f48b 100644 (file)
@@ -13,9 +13,10 @@ import (
 )
 
 // Text returns the string representation of x in the given base.
-// Base must be between 2 and 36, inclusive. The result uses the
-// lower-case letters 'a' to 'z' for digit values >= 10. No base
-// prefix (such as "0x") is added to the string.
+// Base must be between 2 and 62, inclusive. The result uses the
+// lower-case letters 'a' to 'z' for digit values 10 to 35, and
+// the upper-case letters 'A' to 'Z' for digit values 36 to 61.
+// No prefix (such as "0x") is added to the string.
 func (x *Int) Text(base int) string {
        if x == nil {
                return "<nil>"
index 514208145fdf119ba98191c9102ca7f9fec656d5..2e01ee327dd8a9f6a0dd0292f9e65cf116e04ec6 100644 (file)
@@ -56,6 +56,10 @@ var stringTests = []struct {
        {"-0b111", "-7", 0, -7, true},
        {"0b1001010111", "599", 0, 0x257, true},
        {"1001010111", "1001010111", 2, 0x257, true},
+       {"A", "a", 36, 10, true},
+       {"A", "A", 37, 36, true},
+       {"ABCXYZ", "abcxyz", 36, 623741435, true},
+       {"ABCXYZ", "ABCXYZ", 62, 33536793425, true},
 }
 
 func TestIntText(t *testing.T) {
@@ -135,8 +139,16 @@ func TestGetString(t *testing.T) {
                        }
                }
 
-               if got := fmt.Sprintf(format(test.base), z); got != test.out {
-                       t.Errorf("#%db got %s; want %s", i, got, test.out)
+               f := format(test.base)
+               got := fmt.Sprintf(f, z)
+               if f == "%d" {
+                       if got != fmt.Sprintf("%d", test.val) {
+                               t.Errorf("#%db got %s; want %d", i, got, test.val)
+                       }
+               } else {
+                       if got != test.out {
+                               t.Errorf("#%dc got %s; want %s", i, got, test.out)
+                       }
                }
        }
 }
index b50f1be3bda0f0b3cf2ac2c1e4a15b2e84f7d8db..21ccbd6cfafcb42c1fe215bd32289c609d0b52e6 100644 (file)
@@ -15,13 +15,14 @@ import (
        "sync"
 )
 
-const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
+const digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
-// Note: MaxBase = len(digits), but it must remain a rune constant
+// Note: MaxBase = len(digits), but it must remain an untyped rune constant
 //       for API compatibility.
 
 // MaxBase is the largest number base accepted for string conversions.
-const MaxBase = 'z' - 'a' + 10 + 1
+const MaxBase = 10 + ('z' - 'a' + 1) + ('Z' - 'A' + 1)
+const maxBaseSmall = 10 + ('z' - 'a' + 1)
 
 // maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M.
 // For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word.
@@ -59,11 +60,11 @@ func pow(x Word, n int) (p Word) {
 // It returns the corresponding natural number res, the actual base b,
 // a digit count, and a read or syntax error err, if any.
 //
-//     number   = [ prefix ] mantissa .
-//     prefix   = "0" [ "x" | "X" | "b" | "B" ] .
-//      mantissa = digits | digits "." [ digits ] | "." digits .
-//     digits   = digit { digit } .
-//     digit    = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
+//     number   = [ prefix ] mantissa .
+//     prefix   = "0" [ "x" | "X" | "b" | "B" ] .
+//     mantissa = digits | digits "." [ digits ] | "." digits .
+//     digits   = digit { digit } .
+//     digit    = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
 //
 // Unless fracOk is set, the base argument must be 0 or a value between
 // 2 and MaxBase. If fracOk is set, the base argument must be one of
@@ -80,6 +81,11 @@ func pow(x Word, n int) (p Word) {
 // is permitted. The result value is computed as if there were no period
 // present; and the count value is used to determine the fractional part.
 //
+// For bases <= 36, lower and upper case letters are considered the same:
+// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35.
+// For bases > 36, the upper case letters 'A' to 'Z' represent the digit
+// values 36 to 61.
+//
 // A result digit count > 0 corresponds to the number of (non-prefix) digits
 // parsed. A digit count <= 0 indicates the presence of a period (if fracOk
 // is set, only), and -count is the number of fractional digits found.
@@ -173,7 +179,11 @@ func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count in
                case 'a' <= ch && ch <= 'z':
                        d1 = Word(ch - 'a' + 10)
                case 'A' <= ch && ch <= 'Z':
-                       d1 = Word(ch - 'A' + 10)
+                       if b <= maxBaseSmall {
+                               d1 = Word(ch - 'A' + 10)
+                       } else {
+                               d1 = Word(ch - 'A' + maxBaseSmall)
+                       }
                default:
                        d1 = MaxBase + 1
                }
index 898a39fc2c2445bd219bbc007e2a48ea62136e6f..9f38bd94bbab985743f49cd2cd4b35def051956b 100644 (file)
@@ -13,6 +13,12 @@ import (
        "testing"
 )
 
+func TestMaxBase(t *testing.T) {
+       if MaxBase != len(digits) {
+               t.Fatalf("%d != %d", MaxBase, len(digits))
+       }
+}
+
 // log2 computes the integer binary logarithm of x.
 // The result is the integer n for which 2^n <= x < 2^(n+1).
 // If x == 0, the result is -1.
@@ -61,6 +67,7 @@ var strTests = []struct {
        {nat{0xdeadbeef}, 16, "deadbeef"},
        {nat{0x229be7}, 17, "1a2b3c"},
        {nat{0x309663e6}, 32, "o9cov6"},
+       {nat{0x309663e6}, 62, "TakXI"},
 }
 
 func TestString(t *testing.T) {
@@ -110,6 +117,7 @@ var natScanTests = []struct {
        {s: "?"},
        {base: 10},
        {base: 36},
+       {base: 62},
        {s: "?", base: 10},
        {s: "0x"},
        {s: "345", base: 2},
@@ -124,6 +132,7 @@ var natScanTests = []struct {
        {"0", 0, false, nil, 10, 1, true, 0},
        {"0", 10, false, nil, 10, 1, true, 0},
        {"0", 36, false, nil, 36, 1, true, 0},
+       {"0", 62, false, nil, 62, 1, true, 0},
        {"1", 0, false, nat{1}, 10, 1, true, 0},
        {"1", 10, false, nat{1}, 10, 1, true, 0},
        {"0 ", 0, false, nil, 10, 1, true, ' '},
@@ -135,8 +144,11 @@ var natScanTests = []struct {
        {"03271", 0, false, nat{03271}, 8, 4, true, 0},
        {"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
        {"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
+       {"A", 36, false, nat{10}, 36, 1, true, 0},
+       {"A", 37, false, nat{36}, 37, 1, true, 0},
        {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
-       {"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
+       {"XYZ?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
+       {"XYZ?", 62, false, nat{(59*62+60)*62 + 61}, 62, 3, true, '?'},
        {"0x", 16, false, nil, 16, 1, true, 'x'},
        {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
        {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},