// 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).
// 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 - 1
+ for u != 0 {
+ i--
+ a[i] = digits[uintptr(u)&m]
+ u >>= 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
}
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)
}
}
}
+
+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)
+ }
+ }
+}