From 9a0e3259f44ad2de9c65f14f756dab01b3598391 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 12 Jan 2012 01:31:45 +0000 Subject: libgo: Update to weekly.2011-12-14. From-SVN: r183118 --- libgo/go/strconv/atob.go | 2 +- libgo/go/strconv/atof.go | 10 +-- libgo/go/strconv/atof_test.go | 2 +- libgo/go/strconv/atoi.go | 36 +++++++--- libgo/go/strconv/atoi_test.go | 12 ++-- libgo/go/strconv/ftoa.go | 160 ++++++++++++++++++++--------------------- libgo/go/strconv/ftoa_test.go | 57 +++++++++++++-- libgo/go/strconv/itoa.go | 40 +++++++---- libgo/go/strconv/itoa_test.go | 32 +++++++++ libgo/go/strconv/quote.go | 16 ++--- libgo/go/strconv/quote_test.go | 2 +- 11 files changed, 238 insertions(+), 131 deletions(-) (limited to 'libgo/go/strconv') diff --git a/libgo/go/strconv/atob.go b/libgo/go/strconv/atob.go index 1508118d2ac..d0cb097213f 100644 --- a/libgo/go/strconv/atob.go +++ b/libgo/go/strconv/atob.go @@ -14,7 +14,7 @@ func ParseBool(str string) (value bool, err error) { case "0", "f", "F", "false", "FALSE", "False": return false, nil } - return false, &NumError{str, ErrSyntax} + return false, syntaxError("ParseBool", str) } // FormatBool returns "true" or "false" according to the value of b diff --git a/libgo/go/strconv/atof.go b/libgo/go/strconv/atof.go index 1642c18d748..8bda89088bf 100644 --- a/libgo/go/strconv/atof.go +++ b/libgo/go/strconv/atof.go @@ -338,6 +338,8 @@ func (d *decimal) atof32() (f float32, ok bool) { return } +const fnParseFloat = "ParseFloat" + func atof32(s string) (f float32, err error) { if val, ok := special(s); ok { return float32(val), nil @@ -345,7 +347,7 @@ func atof32(s string) (f float32, err error) { var d decimal if !d.set(s) { - return 0, &NumError{s, ErrSyntax} + return 0, syntaxError(fnParseFloat, s) } if optimize { if f, ok := d.atof32(); ok { @@ -355,7 +357,7 @@ func atof32(s string) (f float32, err error) { b, ovf := d.floatBits(&float32info) f = math.Float32frombits(uint32(b)) if ovf { - err = &NumError{s, ErrRange} + err = rangeError(fnParseFloat, s) } return f, err } @@ -367,7 +369,7 @@ func atof64(s string) (f float64, err error) { var d decimal if !d.set(s) { - return 0, &NumError{s, ErrSyntax} + return 0, syntaxError(fnParseFloat, s) } if optimize { if f, ok := d.atof64(); ok { @@ -377,7 +379,7 @@ func atof64(s string) (f float64, err error) { b, ovf := d.floatBits(&float64info) f = math.Float64frombits(b) if ovf { - err = &NumError{s, ErrRange} + err = rangeError(fnParseFloat, s) } return f, err } diff --git a/libgo/go/strconv/atof_test.go b/libgo/go/strconv/atof_test.go index a9820d1bbad..4d5ce1714f4 100644 --- a/libgo/go/strconv/atof_test.go +++ b/libgo/go/strconv/atof_test.go @@ -119,7 +119,7 @@ func init() { for i := range atoftests { test := &atoftests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseFloat", test.in, test.err} } } } diff --git a/libgo/go/strconv/atoi.go b/libgo/go/strconv/atoi.go index 438d496948d..59ef264d17c 100644 --- a/libgo/go/strconv/atoi.go +++ b/libgo/go/strconv/atoi.go @@ -14,11 +14,22 @@ var ErrSyntax = errors.New("invalid syntax") // A NumError records a failed conversion. type NumError struct { - Num string // the input - Err error // the reason the conversion failed (ErrRange, ErrSyntax) + 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 `parsing "` + e.Num + `": ` + e.Err.Error() } +func (e *NumError) Error() string { + return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error() +} + +func syntaxError(fn, str string) *NumError { + return &NumError{fn, str, ErrSyntax} +} + +func rangeError(fn, str string) *NumError { + return &NumError{fn, str, ErrRange} +} const intSize = 32 << uint(^uint(0)>>63) @@ -116,13 +127,13 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) { return n, nil Error: - return n, &NumError{s0, err} + return n, &NumError{"ParseUint", s0, err} } -// ParseInt 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. +// 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. // // The bitSize argument specifies the integer type // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 @@ -134,13 +145,15 @@ Error: // 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) } // Empty string bad. if len(s) == 0 { - return 0, &NumError{s, ErrSyntax} + return 0, syntaxError(fnParseInt, s) } // Pick off leading sign. @@ -157,15 +170,16 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) { var un uint64 un, err = ParseUint(s, base, bitSize) if err != nil && err.(*NumError).Err != ErrRange { + err.(*NumError).Func = fnParseInt err.(*NumError).Num = s0 return 0, err } cutoff := uint64(1 << uint(bitSize-1)) if !neg && un >= cutoff { - return int64(cutoff - 1), &NumError{s0, ErrRange} + return int64(cutoff - 1), rangeError(fnParseInt, s0) } if neg && un > cutoff { - return -int64(cutoff), &NumError{s0, ErrRange} + return -int64(cutoff), rangeError(fnParseInt, s0) } n := int64(un) if neg { diff --git a/libgo/go/strconv/atoi_test.go b/libgo/go/strconv/atoi_test.go index 2d06efed0de..d0e7b61dba8 100644 --- a/libgo/go/strconv/atoi_test.go +++ b/libgo/go/strconv/atoi_test.go @@ -152,37 +152,37 @@ func init() { for i := range atoui64tests { test := &atoui64tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseUint", test.in, test.err} } } for i := range btoui64tests { test := &btoui64tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseUint", test.in, test.err} } } for i := range atoi64tests { test := &atoi64tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseInt", test.in, test.err} } } for i := range btoi64tests { test := &btoi64tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseInt", test.in, test.err} } } for i := range atoui32tests { test := &atoui32tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseUint", test.in, test.err} } } for i := range atoi32tests { test := &atoi32tests[i] if test.err != nil { - test.err = &NumError{test.in, test.err} + test.err = &NumError{"ParseInt", test.in, test.err} } } } diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index e1ea0a35038..692e3e40875 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -45,20 +45,30 @@ var float64info = floatInfo{52, 11, -1023} // Ftoa32(f) is not the same as Ftoa64(float32(f)), // because correct rounding and the number of digits // needed to identify f depend on the precision of the representation. -func FormatFloat(f float64, fmt byte, prec int, n int) string { - if n == 32 { - return genericFtoa(uint64(math.Float32bits(float32(f))), fmt, prec, &float32info) - } - return genericFtoa(math.Float64bits(f), fmt, prec, &float64info) +func FormatFloat(f float64, fmt byte, prec, bitSize int) string { + return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize)) } // AppendFloat appends the string form of the floating-point number f, // as generated by FormatFloat, to dst and returns the extended buffer. -func AppendFloat(dst []byte, f float64, fmt byte, prec int, n int) []byte { - return append(dst, FormatFloat(f, fmt, prec, n)...) +func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byte { + return genericFtoa(dst, f, fmt, prec, bitSize) } -func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { +func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { + var bits uint64 + var flt *floatInfo + switch bitSize { + case 32: + bits = uint64(math.Float32bits(float32(val))) + flt = &float32info + case 64: + bits = math.Float64bits(val) + flt = &float64info + default: + panic("strconv: illegal AppendFloat/FormatFloat bitSize") + } + neg := bits>>(flt.expbits+flt.mantbits) != 0 exp := int(bits>>flt.mantbits) & (1< d.nd { prec = d.nd } - return fmtE(neg, d, prec-1, fmt+'e'-'g') + return fmtE(dst, neg, d, prec-1, fmt+'e'-'g') } if prec > d.dp { prec = d.nd } - return fmtF(neg, d, max(prec-d.dp, 0)) + return fmtF(dst, neg, d, max(prec-d.dp, 0)) } - return "%" + string(fmt) + // unknown format + return append(dst, '%', fmt) } // Round d (= mant * 2^exp) to the shortest number of digits @@ -250,121 +264,103 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { } // %e: -d.ddddde±dd -func fmtE(neg bool, d *decimal, prec int, fmt byte) string { - buf := make([]byte, 3+max(prec, 0)+30) // "-0." + prec digits + exp - w := 0 // write index - +func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte { // sign if neg { - buf[w] = '-' - w++ + dst = append(dst, '-') } // first digit - if d.nd == 0 { - buf[w] = '0' - } else { - buf[w] = d.d[0] + ch := byte('0') + if d.nd != 0 { + ch = d.d[0] } - w++ + dst = append(dst, ch) // .moredigits if prec > 0 { - buf[w] = '.' - w++ - for i := 0; i < prec; i++ { - if 1+i < d.nd { - buf[w] = d.d[1+i] - } else { - buf[w] = '0' + dst = append(dst, '.') + for i := 1; i <= prec; i++ { + ch = '0' + if i < d.nd { + ch = d.d[i] } - w++ + dst = append(dst, ch) } } // e± - buf[w] = fmt - w++ + dst = append(dst, fmt) exp := d.dp - 1 if d.nd == 0 { // special case: 0 has exponent 0 exp = 0 } if exp < 0 { - buf[w] = '-' + ch = '-' exp = -exp } else { - buf[w] = '+' + ch = '+' } - w++ + dst = append(dst, ch) // dddd - // count digits - n := 0 - for e := exp; e > 0; e /= 10 { - n++ + var buf [3]byte + i := len(buf) + for exp >= 10 { + i-- + buf[i] = byte(exp%10 + '0') + exp /= 10 } - // leading zeros - for i := n; i < 2; i++ { - buf[w] = '0' - w++ - } - // digits - w += n - n = 0 - for e := exp; e > 0; e /= 10 { - n++ - buf[w-n] = byte(e%10 + '0') + // exp < 10 + i-- + buf[i] = byte(exp + '0') + + // leading zeroes + if i > len(buf)-2 { + i-- + buf[i] = '0' } - return string(buf[0:w]) + return append(dst, buf[i:]...) } // %f: -ddddddd.ddddd -func fmtF(neg bool, d *decimal, prec int) string { - buf := make([]byte, 1+max(d.dp, 1)+1+max(prec, 0)) - w := 0 - +func fmtF(dst []byte, neg bool, d *decimal, prec int) []byte { // sign if neg { - buf[w] = '-' - w++ + dst = append(dst, '-') } // integer, padded with zeros as needed. if d.dp > 0 { var i int for i = 0; i < d.dp && i < d.nd; i++ { - buf[w] = d.d[i] - w++ + dst = append(dst, d.d[i]) } for ; i < d.dp; i++ { - buf[w] = '0' - w++ + dst = append(dst, '0') } } else { - buf[w] = '0' - w++ + dst = append(dst, '0') } // fraction if prec > 0 { - buf[w] = '.' - w++ + dst = append(dst, '.') for i := 0; i < prec; i++ { - if d.dp+i < 0 || d.dp+i >= d.nd { - buf[w] = '0' - } else { - buf[w] = d.d[d.dp+i] + ch := byte('0') + if j := d.dp + i; 0 <= j && j < d.nd { + ch = d.d[j] } - w++ + dst = append(dst, ch) } } - return string(buf[0:w]) + return dst } // %b: -ddddddddp+ddd -func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string { +func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { var buf [50]byte w := len(buf) exp -= int(flt.mantbits) @@ -395,7 +391,7 @@ func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string { w-- buf[w] = '-' } - return string(buf[w:]) + return append(dst, buf[w:]...) } func max(a, b int) int { diff --git a/libgo/go/strconv/ftoa_test.go b/libgo/go/strconv/ftoa_test.go index 02206d5ad25..c69f8c2466d 100644 --- a/libgo/go/strconv/ftoa_test.go +++ b/libgo/go/strconv/ftoa_test.go @@ -149,26 +149,75 @@ func TestFtoa(t *testing.T) { } } -func BenchmarkFtoa64Decimal(b *testing.B) { +/* This test relies on escape analysis which gccgo does not yet do. + +func TestAppendFloatDoesntAllocate(t *testing.T) { + n := numAllocations(func() { + var buf [64]byte + AppendFloat(buf[:0], 1.23, 'g', 5, 64) + }) + want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better + if n != want { + t.Errorf("with local buffer, did %d allocations, want %d", n, want) + } + n = numAllocations(func() { + AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) + }) + if n != 0 { + t.Errorf("with reused buffer, did %d allocations, want 0", n) + } +} + +*/ + +func BenchmarkFormatFloatDecimal(b *testing.B) { for i := 0; i < b.N; i++ { FormatFloat(33909, 'g', -1, 64) } } -func BenchmarkFtoa64Float(b *testing.B) { +func BenchmarkFormatFloat(b *testing.B) { for i := 0; i < b.N; i++ { FormatFloat(339.7784, 'g', -1, 64) } } -func BenchmarkFtoa64FloatExp(b *testing.B) { +func BenchmarkFormatFloatExp(b *testing.B) { for i := 0; i < b.N; i++ { FormatFloat(-5.09e75, 'g', -1, 64) } } -func BenchmarkFtoa64Big(b *testing.B) { +func BenchmarkFormatFloatBig(b *testing.B) { for i := 0; i < b.N; i++ { FormatFloat(123456789123456789123456789, 'g', -1, 64) } } + +func BenchmarkAppendFloatDecimal(b *testing.B) { + dst := make([]byte, 0, 30) + for i := 0; i < b.N; i++ { + AppendFloat(dst, 33909, 'g', -1, 64) + } +} + +func BenchmarkAppendFloat(b *testing.B) { + dst := make([]byte, 0, 30) + for i := 0; i < b.N; i++ { + AppendFloat(dst, 339.7784, 'g', -1, 64) + } +} + +func BenchmarkAppendFloatExp(b *testing.B) { + dst := make([]byte, 0, 30) + for i := 0; i < b.N; i++ { + AppendFloat(dst, -5.09e75, 'g', -1, 64) + } +} + +func BenchmarkAppendFloatBig(b *testing.B) { + dst := make([]byte, 0, 30) + for i := 0; i < b.N; i++ { + AppendFloat(dst, 123456789123456789123456789, 'g', -1, 64) + } +} diff --git a/libgo/go/strconv/itoa.go b/libgo/go/strconv/itoa.go index 65229f704b8..ca40dd7ef62 100644 --- a/libgo/go/strconv/itoa.go +++ b/libgo/go/strconv/itoa.go @@ -35,7 +35,11 @@ func AppendUint(dst []byte, i uint64, base int) []byte { return dst } -const digits = "0123456789abcdefghijklmnopqrstuvwxyz" +const ( + digits = "0123456789abcdefghijklmnopqrstuvwxyz" + digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" +) var shifts = [len(digits) + 1]uint{ 1 << 1: 1, @@ -46,32 +50,42 @@ var shifts = [len(digits) + 1]uint{ } // formatBits computes the string representation of u in the given base. -// If negative is set, u is treated as negative int64 value. If append_ -// is set, the string is appended to dst and the resulting byte slice is +// If neg is set, u is treated as negative int64 value. If append_ is +// set, the string is appended to dst and the resulting byte slice is // returned as the first result value; otherwise the string is returned // as the second result value. // -func formatBits(dst []byte, u uint64, base int, negative, append_ bool) (d []byte, s string) { +func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) { if base < 2 || base > len(digits) { - panic("invalid base") + panic("strconv: illegal AppendInt/FormatInt base") } // 2 <= base && base <= len(digits) var a [64 + 1]byte // +1 for sign of 64bit value in base 2 i := len(a) - if negative { + if neg { u = -u } // convert bits if base == 10 { - // common case: use constant 10 for / and % because - // the compiler can optimize it into a multiply+shift - for u >= 10 { + // common case: use constants for / and % because + // the compiler can optimize it into a multiply+shift, + // and unroll loop + for u >= 100 { + i -= 2 + q := u / 100 + j := uintptr(u - q*100) + a[i+1] = digits01[j] + a[i+0] = digits10[j] + u = q + } + if u >= 10 { i-- - a[i] = digits[u%10] - u /= 10 + q := u / 10 + a[i] = digits[uintptr(u-q*10)] + u = q } } else if s := shifts[base]; s > 0 { @@ -89,7 +103,7 @@ func formatBits(dst []byte, u uint64, base int, negative, append_ bool) (d []byt b := uint64(base) for u >= b { i-- - a[i] = digits[u%b] + a[i] = digits[uintptr(u%b)] u /= b } } @@ -99,7 +113,7 @@ func formatBits(dst []byte, u uint64, base int, negative, append_ bool) (d []byt a[i] = digits[uintptr(u)] // add sign, if any - if negative { + if neg { i-- a[i] = '-' } diff --git a/libgo/go/strconv/itoa_test.go b/libgo/go/strconv/itoa_test.go index e0213ae9afe..186f16c27db 100644 --- a/libgo/go/strconv/itoa_test.go +++ b/libgo/go/strconv/itoa_test.go @@ -5,6 +5,7 @@ package strconv_test import ( + "runtime" . "strconv" "testing" ) @@ -125,6 +126,37 @@ func TestUitoa(t *testing.T) { } } +func numAllocations(f func()) int { + runtime.UpdateMemStats() + n0 := runtime.MemStats.Mallocs + f() + runtime.UpdateMemStats() + return int(runtime.MemStats.Mallocs - n0) +} + +/* This test relies on escape analysis which gccgo does not yet do. + +var globalBuf [64]byte + +func TestAppendUintDoesntAllocate(t *testing.T) { + n := numAllocations(func() { + var buf [64]byte + AppendInt(buf[:0], 123, 10) + }) + want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better + if n != want { + t.Errorf("with local buffer, did %d allocations, want %d", n, want) + } + n = numAllocations(func() { + AppendInt(globalBuf[:0], 123, 10) + }) + if n != 0 { + t.Errorf("with reused buffer, did %d allocations, want 0", n) + } +} + +*/ + func BenchmarkFormatInt(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range itob64tests { diff --git a/libgo/go/strconv/quote.go b/libgo/go/strconv/quote.go index 30b384df8e3..edba62954be 100644 --- a/libgo/go/strconv/quote.go +++ b/libgo/go/strconv/quote.go @@ -116,30 +116,30 @@ func AppendQuoteToASCII(dst []byte, s string) []byte { // rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) // for control characters and non-printable characters as defined by // unicode.IsPrint. -func QuoteRune(rune int) string { +func QuoteRune(r rune) string { // TODO: avoid the allocation here. - return quoteWith(string(rune), '\'', false) + return quoteWith(string(r), '\'', false) } // AppendQuoteRune appends a single-quoted Go character literal representing the rune, // as generated by QuoteRune, to dst and returns the extended buffer. -func AppendQuoteRune(dst []byte, rune int) []byte { - return append(dst, QuoteRune(rune)...) +func AppendQuoteRune(dst []byte, r rune) []byte { + return append(dst, QuoteRune(r)...) } // QuoteRuneToASCII returns a single-quoted Go character literal representing // the rune. The returned string uses Go escape sequences (\t, \n, \xFF, // \u0100) for non-ASCII characters and non-printable characters as defined // by unicode.IsPrint. -func QuoteRuneToASCII(rune int) string { +func QuoteRuneToASCII(r rune) string { // TODO: avoid the allocation here. - return quoteWith(string(rune), '\'', true) + return quoteWith(string(r), '\'', true) } // AppendQuoteRune appends a single-quoted Go character literal representing the rune, // as generated by QuoteRuneToASCII, to dst and returns the extended buffer. -func AppendQuoteRuneToASCII(dst []byte, rune int) []byte { - return append(dst, QuoteRuneToASCII(rune)...) +func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { + return append(dst, QuoteRuneToASCII(r)...) } // CanBackquote returns whether the string s would be diff --git a/libgo/go/strconv/quote_test.go b/libgo/go/strconv/quote_test.go index e440797162b..419943d83c7 100644 --- a/libgo/go/strconv/quote_test.go +++ b/libgo/go/strconv/quote_test.go @@ -47,7 +47,7 @@ func TestQuoteToASCII(t *testing.T) { } type quoteRuneTest struct { - in int + in rune out string ascii string } -- cgit v1.2.3