diff --git a/src/pkg/strconv/itoa.go b/src/pkg/strconv/itoa.go index 29a1a81d869..794ef370b26 100644 --- a/src/pkg/strconv/itoa.go +++ b/src/pkg/strconv/itoa.go @@ -6,37 +6,14 @@ package strconv // FormatUint returns the string representation of i in the given base. func FormatUint(i uint64, base int) string { - u := i - if base < 2 || 36 < base { - panic("invalid base " + Itoa(base)) - } - if u == 0 { - return "0" - } - - // Assemble decimal in reverse order. - var buf [64]byte - j := len(buf) - b := uint64(base) - for u > 0 { - j-- - buf[j] = "0123456789abcdefghijklmnopqrstuvwxyz"[u%b] - u /= b - } - - return string(buf[j:]) + _, s := formatBits(nil, i, base, false, false) + return s } // FormatInt returns the string representation of i in the given base. func FormatInt(i int64, base int) string { - if i == 0 { - return "0" - } - - if i < 0 { - return "-" + FormatUint(-uint64(i), base) - } - return FormatUint(uint64(i), base) + _, s := formatBits(nil, uint64(i), base, true, false) + return s } // Itoa is shorthand for FormatInt(i, 10). @@ -47,11 +24,95 @@ 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 { - return append(dst, FormatInt(i, base)...) + dst, _ = formatBits(dst, uint64(i), base, true, true) + return dst } // 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 { - return append(dst, FormatUint(i, base)...) + dst, _ = formatBits(dst, i, base, false, true) + return dst +} + +const digits = "0123456789abcdefghijklmnopqrstuvwxyz" + +var shifts = [len(digits) + 1]uint{ + 1 << 1: 1, + 1 << 2: 2, + 1 << 3: 3, + 1 << 4: 4, + 1 << 5: 5, +} + +// formatBits computes the string representation of u in the given base. +// If signed is set, u is treated as 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 simply returned as the +// second result value. +// +func formatBits(dst []byte, u uint64, base int, signed, append_ bool) (d []byte, s string) { + if base < 2 || base > len(digits) { + panic("invalid base") + } + // 2 <= base && base <= len(digits) + + if u == 0 { + if append_ { + d = append(dst, '0') + return + } + s = "0" + return + } + + var a [64 + 1]byte // +1 for sign of 64bit value in base 2 + i := len(a) + + x := int64(u) + if x < 0 && signed { + 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 != 0 { + i-- + a[i] = digits[u%10] + u /= 10 + } + + } else if s := shifts[base]; s > 0 { + // base is power of 2: use shifts and masks instead of / and % + m := uintptr(1)<>= s + } + + } else { + // general case + b := uint64(base) + for u != 0 { + i-- + a[i] = digits[u%b] + u /= b + } + } + + // add sign, if any + if x < 0 && signed { + i-- + a[i] = '-' + } + + if append_ { + d = append(dst, a[i:]...) + return + } + s = string(a[i:]) + return } diff --git a/src/pkg/strconv/itoa_test.go b/src/pkg/strconv/itoa_test.go index 99be968fff7..e0213ae9afe 100644 --- a/src/pkg/strconv/itoa_test.go +++ b/src/pkg/strconv/itoa_test.go @@ -77,8 +77,8 @@ func TestItoa(t *testing.T) { t.Errorf("FormatUint(%v, %v) = %v want %v", test.in, test.base, s, test.out) } - x := AppendUint([]byte("abc"), uint64(test.in), test.base) - if string(x) != "abc"+test.out { + x := AppendUint(nil, uint64(test.in), test.base) + if string(x) != test.out { t.Errorf("AppendUint(%q, %v, %v) = %q want %v", "abc", uint64(test.in), test.base, x, test.out) } @@ -124,3 +124,37 @@ func TestUitoa(t *testing.T) { } } + +func BenchmarkFormatInt(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, test := range itob64tests { + FormatInt(test.in, test.base) + } + } +} + +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) + } + } +} + +func BenchmarkFormatUint(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, test := range uitob64tests { + FormatUint(test.in, test.base) + } + } +} + +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) + } + } +}