aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/strconv
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-09-14 17:11:35 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-09-14 17:11:35 +0000
commitbc998d034f45d1828a8663b2eed928faf22a7d01 (patch)
tree8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/strconv
parenta41a6142df74219f596e612d3a7775f68ca6e96f (diff)
downloadgcc-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.go10
-rw-r--r--libgo/go/strconv/decimal.go4
-rw-r--r--libgo/go/strconv/itoa.go111
-rw-r--r--libgo/go/strconv/itoa_test.go78
-rw-r--r--libgo/go/strconv/quote.go6
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, '\\')