diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-09-14 17:11:35 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2017-09-14 17:11:35 +0000 |
commit | bc998d034f45d1828a8663b2eed928faf22a7d01 (patch) | |
tree | 8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/strconv | |
parent | a41a6142df74219f596e612d3a7775f68ca6e96f (diff) | |
download | gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2 |
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753
From-SVN: r252767
Diffstat (limited to 'libgo/go/strconv')
-rw-r--r-- | libgo/go/strconv/atof_test.go | 10 | ||||
-rw-r--r-- | libgo/go/strconv/decimal.go | 4 | ||||
-rw-r--r-- | libgo/go/strconv/itoa.go | 111 | ||||
-rw-r--r-- | libgo/go/strconv/itoa_test.go | 78 | ||||
-rw-r--r-- | libgo/go/strconv/quote.go | 6 |
5 files changed, 173 insertions, 36 deletions
diff --git a/libgo/go/strconv/atof_test.go b/libgo/go/strconv/atof_test.go index 0a89c3e..3380b20 100644 --- a/libgo/go/strconv/atof_test.go +++ b/libgo/go/strconv/atof_test.go @@ -10,6 +10,7 @@ import ( "reflect" . "strconv" "strings" + "sync" "testing" "time" ) @@ -213,12 +214,17 @@ type atofSimpleTest struct { } var ( + atofOnce sync.Once atofRandomTests []atofSimpleTest benchmarksRandomBits [1024]string benchmarksRandomNormal [1024]string ) -func init() { +func initAtof() { + atofOnce.Do(initAtofOnce) +} + +func initAtofOnce() { // The atof routines return NumErrors wrapping // the error and the string. Convert the table above. for i := range atoftests { @@ -261,6 +267,7 @@ func init() { } func testAtof(t *testing.T, opt bool) { + initAtof() oldopt := SetOptimize(opt) for i := 0; i < len(atoftests); i++ { test := &atoftests[i] @@ -306,6 +313,7 @@ func TestAtof(t *testing.T) { testAtof(t, true) } func TestAtofSlow(t *testing.T) { testAtof(t, false) } func TestAtofRandom(t *testing.T) { + initAtof() for _, test := range atofRandomTests { x, _ := ParseFloat(test.s, 64) switch { diff --git a/libgo/go/strconv/decimal.go b/libgo/go/strconv/decimal.go index 957acd9..b580018 100644 --- a/libgo/go/strconv/decimal.go +++ b/libgo/go/strconv/decimal.go @@ -15,8 +15,8 @@ type decimal struct { d [800]byte // digits, big-endian representation nd int // number of digits used dp int // decimal point - neg bool - trunc bool // discarded nonzero digits beyond d[:nd] + neg bool // negative flag + trunc bool // discarded nonzero digits beyond d[:nd] } func (a *decimal) String() string { diff --git a/libgo/go/strconv/itoa.go b/libgo/go/strconv/itoa.go index f50d877..78527c8 100644 --- a/libgo/go/strconv/itoa.go +++ b/libgo/go/strconv/itoa.go @@ -4,10 +4,15 @@ package strconv +const fastSmalls = true // enable fast path for small integers + // FormatUint returns the string representation of i in the given base, // for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' // for digit values >= 10. func FormatUint(i uint64, base int) string { + if fastSmalls && i < nSmalls && base == 10 { + return small(int(i)) + } _, s := formatBits(nil, i, base, false, false) return s } @@ -16,6 +21,9 @@ func FormatUint(i uint64, base int) string { // for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' // for digit values >= 10. func FormatInt(i int64, base int) string { + if fastSmalls && 0 <= i && i < nSmalls && base == 10 { + return small(int(i)) + } _, s := formatBits(nil, uint64(i), base, i < 0, false) return s } @@ -28,6 +36,9 @@ func Itoa(i int) string { // AppendInt appends the string form of the integer i, // as generated by FormatInt, to dst and returns the extended buffer. func AppendInt(dst []byte, i int64, base int) []byte { + if fastSmalls && 0 <= i && i < nSmalls && base == 10 { + return append(dst, small(int(i))...) + } dst, _ = formatBits(dst, uint64(i), base, i < 0, true) return dst } @@ -35,13 +46,38 @@ func AppendInt(dst []byte, i int64, base int) []byte { // AppendUint appends the string form of the unsigned integer i, // as generated by FormatUint, to dst and returns the extended buffer. func AppendUint(dst []byte, i uint64, base int) []byte { + if fastSmalls && i < nSmalls && base == 10 { + return append(dst, small(int(i))...) + } dst, _ = formatBits(dst, i, base, false, true) return dst } -const ( - digits = "0123456789abcdefghijklmnopqrstuvwxyz" -) +// small returns the string for an i with 0 <= i < nSmalls. +func small(i int) string { + off := 0 + if i < 10 { + off = 1 + } + return smallsString[i*2+off : i*2+2] +} + +const nSmalls = 100 + +const smallsString = "00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899" + +const host32bit = ^uint(0)>>32 == 0 + +const digits = "0123456789abcdefghijklmnopqrstuvwxyz" var shifts = [len(digits) + 1]uint{ 1 << 1: 1, @@ -71,61 +107,84 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s } // convert bits + // We use uint values where we can because those will + // fit into a single register even on a 32bit machine. if base == 10 { // common case: use constants for / because // the compiler can optimize it into a multiply+shift - if ^uintptr(0)>>32 == 0 { - for u > uint64(^uintptr(0)) { + if host32bit { + // convert the lower digits using 32bit operations + for u >= 1e9 { + // Avoid using r = a%b in addition to q = a/b + // since 64bit division and modulo operations + // are calculated by runtime functions on 32bit machines. q := u / 1e9 - us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr - for j := 9; j > 0; j-- { - i-- - qs := us / 10 - a[i] = byte(us - qs*10 + '0') - us = qs + us := uint(u - q*1e9) // u % 1e9 fits into a uint + for j := 4; j > 0; j-- { + is := us % 100 * 2 + us /= 100 + i -= 2 + a[i+1] = smallsString[is+1] + a[i+0] = smallsString[is+0] } + + // us < 10, since it contains the last digit + // from the initial 9-digit us. + i-- + a[i] = smallsString[us*2+1] + u = q } + // u < 1e9 } - // u guaranteed to fit into a uintptr - us := uintptr(u) - for us >= 10 { - i-- - q := us / 10 - a[i] = byte(us - q*10 + '0') - us = q + // u guaranteed to fit into a uint + us := uint(u) + for us >= 100 { + is := us % 100 * 2 + us /= 100 + i -= 2 + a[i+1] = smallsString[is+1] + a[i+0] = smallsString[is+0] } - // u < 10 + + // us < 100 + is := us * 2 i-- - a[i] = byte(us + '0') + a[i] = smallsString[is+1] + if us >= 10 { + i-- + a[i] = smallsString[is] + } } else if s := shifts[base]; s > 0 { // base is power of 2: use shifts and masks instead of / and % b := uint64(base) - m := uintptr(b) - 1 // == 1<<s - 1 + m := uint(base) - 1 // == 1<<s - 1 for u >= b { i-- - a[i] = digits[uintptr(u)&m] + a[i] = digits[uint(u)&m] u >>= s } // u < base i-- - a[i] = digits[uintptr(u)] - + a[i] = digits[uint(u)] } else { // general case b := uint64(base) for u >= b { i-- + // Avoid using r = a%b in addition to q = a/b + // since 64bit division and modulo operations + // are calculated by runtime functions on 32bit machines. q := u / b - a[i] = digits[uintptr(u-q*b)] + a[i] = digits[uint(u-q*b)] u = q } // u < base i-- - a[i] = digits[uintptr(u)] + a[i] = digits[uint(u)] } // add sign, if any diff --git a/libgo/go/strconv/itoa_test.go b/libgo/go/strconv/itoa_test.go index 48dc03e..89c2de6 100644 --- a/libgo/go/strconv/itoa_test.go +++ b/libgo/go/strconv/itoa_test.go @@ -126,10 +126,46 @@ func TestUitoa(t *testing.T) { } } +var varlenUints = []struct { + in uint64 + out string +}{ + {1, "1"}, + {12, "12"}, + {123, "123"}, + {1234, "1234"}, + {12345, "12345"}, + {123456, "123456"}, + {1234567, "1234567"}, + {12345678, "12345678"}, + {123456789, "123456789"}, + {1234567890, "1234567890"}, + {12345678901, "12345678901"}, + {123456789012, "123456789012"}, + {1234567890123, "1234567890123"}, + {12345678901234, "12345678901234"}, + {123456789012345, "123456789012345"}, + {1234567890123456, "1234567890123456"}, + {12345678901234567, "12345678901234567"}, + {123456789012345678, "123456789012345678"}, + {1234567890123456789, "1234567890123456789"}, + {12345678901234567890, "12345678901234567890"}, +} + +func TestFormatUintVarlen(t *testing.T) { + for _, test := range varlenUints { + s := FormatUint(test.in, 10) + if s != test.out { + t.Errorf("FormatUint(%v, 10) = %v want %v", test.in, s, test.out) + } + } +} + func BenchmarkFormatInt(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range itob64tests { - FormatInt(test.in, test.base) + s := FormatInt(test.in, test.base) + BenchSink += len(s) } } } @@ -138,7 +174,8 @@ func BenchmarkAppendInt(b *testing.B) { dst := make([]byte, 0, 30) for i := 0; i < b.N; i++ { for _, test := range itob64tests { - AppendInt(dst, test.in, test.base) + dst = AppendInt(dst[:0], test.in, test.base) + BenchSink += len(dst) } } } @@ -146,7 +183,8 @@ func BenchmarkAppendInt(b *testing.B) { func BenchmarkFormatUint(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range uitob64tests { - FormatUint(test.in, test.base) + s := FormatUint(test.in, test.base) + BenchSink += len(s) } } } @@ -155,7 +193,39 @@ func BenchmarkAppendUint(b *testing.B) { dst := make([]byte, 0, 30) for i := 0; i < b.N; i++ { for _, test := range uitob64tests { - AppendUint(dst, test.in, test.base) + dst = AppendUint(dst[:0], test.in, test.base) + BenchSink += len(dst) } } } + +func BenchmarkFormatIntSmall(b *testing.B) { + const smallInt = 42 + for i := 0; i < b.N; i++ { + s := FormatInt(smallInt, 10) + BenchSink += len(s) + } +} + +func BenchmarkAppendIntSmall(b *testing.B) { + dst := make([]byte, 0, 30) + const smallInt = 42 + for i := 0; i < b.N; i++ { + dst = AppendInt(dst[:0], smallInt, 10) + BenchSink += len(dst) + } +} + +func BenchmarkAppendUintVarlen(b *testing.B) { + for _, test := range varlenUints { + b.Run(test.out, func(b *testing.B) { + dst := make([]byte, 0, 30) + for j := 0; j < b.N; j++ { + dst = AppendUint(dst[:0], test.in, 10) + BenchSink += len(dst) + } + }) + } +} + +var BenchSink int // make sure compiler cannot optimize away benchmarks diff --git a/libgo/go/strconv/quote.go b/libgo/go/strconv/quote.go index 76c5c2a..db57065 100644 --- a/libgo/go/strconv/quote.go +++ b/libgo/go/strconv/quote.go @@ -32,7 +32,7 @@ func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly b buf = append(buf, lowerhex[s[0]&0xF]) continue } - buf = appendEscapedRune(buf, r, width, quote, ASCIIonly, graphicOnly) + buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly) } buf = append(buf, quote) return buf @@ -43,12 +43,12 @@ func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly if !utf8.ValidRune(r) { r = utf8.RuneError } - buf = appendEscapedRune(buf, r, utf8.RuneLen(r), quote, ASCIIonly, graphicOnly) + buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly) buf = append(buf, quote) return buf } -func appendEscapedRune(buf []byte, r rune, width int, quote byte, ASCIIonly, graphicOnly bool) []byte { +func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte { var runeTmp [utf8.UTFMax]byte if r == rune(quote) || r == '\\' { // always backslashed buf = append(buf, '\\') |