Commit 4c0e51cd authored by Rob Pike's avatar Rob Pike

Make printing faster by avoiding mallocs and some other advances.

Roughly 33% faster for simple cases, probably more for complex ones.

Before:

mallocs per Sprintf(""): 4
mallocs per Sprintf("xxx"): 6
mallocs per Sprintf("%x"): 10
mallocs per Sprintf("%x %x"): 12

Now:

mallocs per Sprintf(""): 2
mallocs per Sprintf("xxx"): 3
mallocs per Sprintf("%x"): 5
mallocs per Sprintf("%x %x"): 7

Speed improves because of avoiding mallocs and also by sharing a bytes.Buffer
between print.go and format.go rather than copying the data back after each
printed item.

Before:

fmt_test.BenchmarkSprintfEmpty	1000000	      1346 ns/op
fmt_test.BenchmarkSprintfString	500000	      3461 ns/op
fmt_test.BenchmarkSprintfInt	500000	      3671 ns/op

Now:

fmt_test.BenchmarkSprintfEmpty	 2000000	       995 ns/op
fmt_test.BenchmarkSprintfString	 1000000	      2745 ns/op
fmt_test.BenchmarkSprintfInt	 1000000	      2391 ns/op
fmt_test.BenchmarkSprintfIntInt	  500000	      3751 ns/op

I believe there is more to get but this is a good milestone.

R=rsc
CC=golang-dev, hong
https://golang.org/cl/166076
parent ed6fd1bc
...@@ -35,6 +35,7 @@ type Buffer struct { ...@@ -35,6 +35,7 @@ type Buffer struct {
buf []byte; // contents are the bytes buf[off : len(buf)] buf []byte; // contents are the bytes buf[off : len(buf)]
off int; // read at &buf[off], write at &buf[len(buf)] off int; // read at &buf[off], write at &buf[len(buf)]
oneByte [1]byte; // avoid allocation of slice on each WriteByte oneByte [1]byte; // avoid allocation of slice on each WriteByte
bootstrap [64]byte; // memory to hold first slice; helps small buffers (Printf) avoid allocation.
} }
// Bytes returns the contents of the unread portion of the buffer; // Bytes returns the contents of the unread portion of the buffer;
...@@ -69,29 +70,51 @@ func (b *Buffer) Truncate(n int) { ...@@ -69,29 +70,51 @@ func (b *Buffer) Truncate(n int) {
// b.Reset() is the same as b.Truncate(0). // b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset() { b.Truncate(0) } func (b *Buffer) Reset() { b.Truncate(0) }
// Write appends the contents of p to the buffer. The return // Resize buffer to guarantee enough space for n more bytes.
// value n is the length of p; err is always nil. // After this call, the state of b.buf is inconsistent.
func (b *Buffer) Write(p []byte) (n int, err os.Error) { // It must be fixed up as is done in Write and WriteString.
m := b.Len(); func (b *Buffer) resize(n int) {
n = len(p); var buf []byte;
if b.buf == nil && n <= len(b.bootstrap) {
buf = &b.bootstrap
} else {
buf = b.buf;
if len(b.buf)+n > cap(b.buf) { if len(b.buf)+n > cap(b.buf) {
// not enough space at end
buf := b.buf;
if m+n > cap(b.buf) {
// not enough space anywhere // not enough space anywhere
buf = make([]byte, 2*cap(b.buf)+n) buf = make([]byte, 2*cap(b.buf)+n)
} }
copyBytes(buf, 0, b.buf[b.off:b.off+m]); copy(buf, b.buf[b.off:]);
}
b.buf = buf; b.buf = buf;
b.off = 0; b.off = 0;
} }
// Write appends the contents of p to the buffer. The return
// value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) {
m := b.Len();
n = len(p);
if len(b.buf)+n > cap(b.buf) {
b.resize(n)
}
b.buf = b.buf[0 : b.off+m+n]; b.buf = b.buf[0 : b.off+m+n];
copyBytes(b.buf, b.off+m, p); copyBytes(b.buf, b.off+m, p);
return n, nil; return n, nil;
} }
// WriteString appends the contents of s to the buffer. The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
m := b.Len();
n = len(s);
if len(b.buf)+n > cap(b.buf) {
b.resize(n)
}
b.buf = b.buf[0 : b.off+m+n];
copyString(b.buf, b.off+m, s);
return n, nil;
}
// MinRead is the minimum slice size passed to a Read call by // MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond // Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the // what is required to hold the contents of r, ReadFrom will not grow the
...@@ -146,29 +169,6 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { ...@@ -146,29 +169,6 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) {
return; return;
} }
// WriteString appends the contents of s to the buffer. The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
m := b.Len();
n = len(s);
if len(b.buf)+n > cap(b.buf) {
// not enough space at end
buf := b.buf;
if m+n > cap(b.buf) {
// not enough space anywhere
buf = make([]byte, 2*cap(b.buf)+n)
}
copyBytes(buf, 0, b.buf[b.off:b.off+m]);
b.buf = buf;
b.off = 0;
}
b.buf = b.buf[0 : b.off+m+n];
copyString(b.buf, b.off+m, s);
return n, nil;
}
// WriteByte appends the byte c to the buffer. // WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included // The returned error is always nil, but is included
// to match bufio.Writer's WriteByte. // to match bufio.Writer's WriteByte.
......
...@@ -7,6 +7,7 @@ package fmt_test ...@@ -7,6 +7,7 @@ package fmt_test
import ( import (
. "fmt"; . "fmt";
"io"; "io";
"malloc"; // for the malloc count test only
"math"; "math";
"strings"; "strings";
"testing"; "testing";
...@@ -242,7 +243,7 @@ func TestSprintf(t *testing.T) { ...@@ -242,7 +243,7 @@ func TestSprintf(t *testing.T) {
if _, ok := tt.val.(string); ok { if _, ok := tt.val.(string); ok {
// Don't requote the already-quoted strings. // Don't requote the already-quoted strings.
// It's too confusing to read the errors. // It's too confusing to read the errors.
t.Errorf("Sprintf(%q, %q) = %s want %s", tt.fmt, tt.val, s, tt.out) t.Errorf("Sprintf(%q, %q) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out)
} else { } else {
t.Errorf("Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out) t.Errorf("Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out)
} }
...@@ -268,6 +269,39 @@ func BenchmarkSprintfInt(b *testing.B) { ...@@ -268,6 +269,39 @@ func BenchmarkSprintfInt(b *testing.B) {
} }
} }
func BenchmarkSprintfIntInt(b *testing.B) {
for i := 0; i < b.N; i++ {
Sprintf("%d %d", 5, 6)
}
}
func TestCountMallocs(t *testing.T) {
mallocs := 0 - malloc.GetStats().Mallocs;
for i := 0; i < 100; i++ {
Sprintf("")
}
mallocs += malloc.GetStats().Mallocs;
Printf("mallocs per Sprintf(\"\"): %d\n", mallocs/100);
mallocs = 0 - malloc.GetStats().Mallocs;
for i := 0; i < 100; i++ {
Sprintf("xxx")
}
mallocs += malloc.GetStats().Mallocs;
Printf("mallocs per Sprintf(\"xxx\"): %d\n", mallocs/100);
mallocs = 0 - malloc.GetStats().Mallocs;
for i := 0; i < 100; i++ {
Sprintf("%x", i)
}
mallocs += malloc.GetStats().Mallocs;
Printf("mallocs per Sprintf(\"%%x\"): %d\n", mallocs/100);
mallocs = 0 - malloc.GetStats().Mallocs;
for i := 0; i < 100; i++ {
Sprintf("%x %x", i, i)
}
mallocs += malloc.GetStats().Mallocs;
Printf("mallocs per Sprintf(\"%%x %%x\"): %d\n", mallocs/100);
}
type flagPrinter struct{} type flagPrinter struct{}
func (*flagPrinter) Format(f State, c int) { func (*flagPrinter) Format(f State, c int) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package fmt package fmt
import ( import (
"bytes";
"strconv"; "strconv";
) )
...@@ -16,34 +17,30 @@ const ( ...@@ -16,34 +17,30 @@ const (
udigits = "0123456789ABCDEF"; udigits = "0123456789ABCDEF";
) )
const padZeros = "0000000000000000000000000000000000000000000000000000000000000000" var padZeroBytes = make([]byte, nByte)
const padSpaces = " " var padSpaceBytes = make([]byte, nByte)
var newline = []byte{'\n'}
func init() { func init() {
if len(padZeros) != nByte || len(padSpaces) != nByte { for i := 0; i < nByte; i++ {
panic("fmt padding wrong length") padZeroBytes[i] = '0';
padSpaceBytes[i] = ' ';
} }
} }
/* /*
Fmt is the raw formatter used by Printf etc. Not meant for normal use. Fmt is the raw formatter used by Printf etc. Not meant for normal use.
It prints into a bytes.Buffer that must be set up externally.
See print.go for a more palatable interface. See print.go for a more palatable interface.
The model is to accumulate operands into an internal buffer and then
retrieve the buffer in one hit using Str(), Putnl(), etc. The formatting
methods return ``self'' so the operations can be chained.
f := fmt.New();
print(f.Fmt_d(1234).Fmt_s("\n").Str()); // create string, print it
f.Fmt_d(-1234).Fmt_s("\n").Put(); // print string
f.Fmt_ud(1<<63).Putnl(); // print string with automatic newline
*/ */
type Fmt struct { type Fmt struct {
buf string; intbuf [nByte]byte;
buf *bytes.Buffer;
wid int; wid int;
wid_present bool; widPresent bool;
prec int; prec int;
prec_present bool; precPresent bool;
// flags // flags
minus bool; minus bool;
plus bool; plus bool;
...@@ -52,11 +49,11 @@ type Fmt struct { ...@@ -52,11 +49,11 @@ type Fmt struct {
zero bool; zero bool;
} }
func (f *Fmt) clearflags() { func (f *Fmt) ClearFlags() {
f.wid = 0; f.wid = 0;
f.wid_present = false; f.widPresent = false;
f.prec = 0; f.prec = 0;
f.prec_present = false; f.precPresent = false;
f.minus = false; f.minus = false;
f.plus = false; f.plus = false;
f.sharp = false; f.sharp = false;
...@@ -64,94 +61,98 @@ func (f *Fmt) clearflags() { ...@@ -64,94 +61,98 @@ func (f *Fmt) clearflags() {
f.zero = false; f.zero = false;
} }
func (f *Fmt) clearbuf() { f.buf = "" } func (f *Fmt) Init(buf *bytes.Buffer) {
f.buf = buf;
func (f *Fmt) init() { f.ClearFlags();
f.clearbuf();
f.clearflags();
}
// New returns a new initialized Fmt
func New() *Fmt {
f := new(Fmt);
f.init();
return f;
}
// Str returns the buffered contents as a string and resets the Fmt.
func (f *Fmt) Str() string {
s := f.buf;
f.clearbuf();
f.clearflags();
f.buf = "";
return s;
}
// Put writes the buffered contents to stdout and resets the Fmt.
func (f *Fmt) Put() {
print(f.buf);
f.clearbuf();
f.clearflags();
} }
// Putnl writes the buffered contents to stdout, followed by a newline, and resets the Fmt. func (f *Fmt) Reset() { f.ClearFlags() }
func (f *Fmt) Putnl() {
print(f.buf, "\n");
f.clearbuf();
f.clearflags();
}
// Wp sets the width and precision for formatting the next item. // Wp sets the width and precision for formatting the next item.
func (f *Fmt) Wp(w, p int) *Fmt { func (f *Fmt) Wp(w, p int) {
f.wid_present = true; f.widPresent = true;
f.wid = w; f.wid = w;
f.prec_present = true; f.precPresent = true;
f.prec = p; f.prec = p;
return f;
} }
// P sets the precision for formatting the next item. // P sets the precision for formatting the next item.
func (f *Fmt) P(p int) *Fmt { func (f *Fmt) P(p int) {
f.prec_present = true; f.precPresent = true;
f.prec = p; f.prec = p;
return f;
} }
// W sets the width for formatting the next item. // W sets the width for formatting the next item.
func (f *Fmt) W(x int) *Fmt { func (f *Fmt) W(x int) {
f.wid_present = true; f.widPresent = true;
f.wid = x; f.wid = x;
return f;
} }
// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus) // Compute left and right padding widths (only one will be non-zero).
// padding is in bytes, not characters (agrees with ANSIC C, not Plan 9 C) func (f *Fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) {
func (f *Fmt) pad(s string) {
if f.wid_present && f.wid != 0 {
left := !f.minus; left := !f.minus;
w := f.wid; w := f.wid;
if w < 0 { if w < 0 {
left = false; left = false;
w = -w; w = -w;
} }
w -= len(s); w -= width;
padding := padSpaces;
if left && f.zero {
padding = padZeros
}
if w > 0 { if w > 0 {
if w > nByte { if left && f.zero {
w = nByte return padZeroBytes, w, 0
} }
padding = padding[0:w];
if left { if left {
s = padding + s return padSpaceBytes, w, 0
} else { } else {
s += padding // can't be zero padding on the right
return padSpaceBytes, 0, w
}
}
return;
}
// Generate n bytes of padding.
func (f *Fmt) writePadding(n int, padding []byte) {
for n > 0 {
m := n;
if m > nByte {
m = nByte
}
f.buf.Write(padding[0:m]);
n -= m;
}
}
// Append b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus)
func (f *Fmt) padBytes(b []byte) {
var padding []byte;
var left, right int;
if f.widPresent && f.wid != 0 {
padding, left, right = f.computePadding(len(b))
}
if left > 0 {
f.writePadding(left, padding)
}
f.buf.Write(b);
if right > 0 {
f.writePadding(right, padding)
}
}
// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus)
func (f *Fmt) pad(s string) {
var padding []byte;
var left, right int;
if f.widPresent && f.wid != 0 {
padding, left, right = f.computePadding(len(s))
} }
if left > 0 {
f.writePadding(left, padding)
} }
f.buf.WriteString(s);
if right > 0 {
f.writePadding(right, padding)
} }
f.buf += s;
} }
// format val into buf, ending at buf[i]. (printing is easier right-to-left; // format val into buf, ending at buf[i]. (printing is easier right-to-left;
...@@ -171,19 +172,18 @@ func putint(buf []byte, base, val uint64, digits string) int { ...@@ -171,19 +172,18 @@ func putint(buf []byte, base, val uint64, digits string) int {
} }
// Fmt_boolean formats a boolean. // Fmt_boolean formats a boolean.
func (f *Fmt) Fmt_boolean(v bool) *Fmt { func (f *Fmt) Fmt_boolean(v bool) {
if v { if v {
f.pad("true") f.pad("true")
} else { } else {
f.pad("false") f.pad("false")
} }
f.clearflags(); f.ClearFlags();
return f;
} }
// integer; interprets prec but not wid. // integer; interprets prec but not wid.
func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) string { func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) []byte {
var buf [nByte]byte; var buf []byte = &f.intbuf;
negative := is_signed && a < 0; negative := is_signed && a < 0;
if negative { if negative {
a = -a a = -a
...@@ -192,17 +192,17 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) string ...@@ -192,17 +192,17 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) string
// two ways to ask for extra leading zero digits: %.3d or %03d. // two ways to ask for extra leading zero digits: %.3d or %03d.
// apparently the first cancels the second. // apparently the first cancels the second.
prec := 0; prec := 0;
if f.prec_present { if f.precPresent {
prec = f.prec; prec = f.prec;
f.zero = false; f.zero = false;
} else if f.zero && f.wid_present && !f.minus && f.wid > 0 { } else if f.zero && f.widPresent && !f.minus && f.wid > 0 {
prec = f.wid; prec = f.wid;
if negative || f.plus || f.space { if negative || f.plus || f.space {
prec-- // leave room for sign prec-- // leave room for sign
} }
} }
i := putint(&buf, uint64(base), uint64(a), digits); i := putint(buf, uint64(base), uint64(a), digits);
for i > 0 && prec > (nByte-1-i) { for i > 0 && prec > (nByte-1-i) {
buf[i] = '0'; buf[i] = '0';
i--; i--;
...@@ -233,147 +233,137 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) string ...@@ -233,147 +233,137 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits string) string
buf[i] = ' '; buf[i] = ' ';
i--; i--;
} }
return string(buf[i+1 : nByte]); return buf[i+1 : nByte];
} }
// Fmt_d64 formats an int64 in decimal. // Fmt_d64 formats an int64 in decimal.
func (f *Fmt) Fmt_d64(v int64) *Fmt { func (f *Fmt) Fmt_d64(v int64) {
f.pad(f.integer(v, 10, true, ldigits)); f.padBytes(f.integer(v, 10, true, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_d32 formats an int32 in decimal. // Fmt_d32 formats an int32 in decimal.
func (f *Fmt) Fmt_d32(v int32) *Fmt { return f.Fmt_d64(int64(v)) } func (f *Fmt) Fmt_d32(v int32) { f.Fmt_d64(int64(v)) }
// Fmt_d formats an int in decimal. // Fmt_d formats an int in decimal.
func (f *Fmt) Fmt_d(v int) *Fmt { return f.Fmt_d64(int64(v)) } func (f *Fmt) Fmt_d(v int) { f.Fmt_d64(int64(v)) }
// Fmt_ud64 formats a uint64 in decimal. // Fmt_ud64 formats a uint64 in decimal.
func (f *Fmt) Fmt_ud64(v uint64) *Fmt { func (f *Fmt) Fmt_ud64(v uint64) *Fmt {
f.pad(f.integer(int64(v), 10, false, ldigits)); f.padBytes(f.integer(int64(v), 10, false, ldigits));
f.clearflags(); f.ClearFlags();
return f; return f;
} }
// Fmt_ud32 formats a uint32 in decimal. // Fmt_ud32 formats a uint32 in decimal.
func (f *Fmt) Fmt_ud32(v uint32) *Fmt { return f.Fmt_ud64(uint64(v)) } func (f *Fmt) Fmt_ud32(v uint32) { f.Fmt_ud64(uint64(v)) }
// Fmt_ud formats a uint in decimal. // Fmt_ud formats a uint in decimal.
func (f *Fmt) Fmt_ud(v uint) *Fmt { return f.Fmt_ud64(uint64(v)) } func (f *Fmt) Fmt_ud(v uint) { f.Fmt_ud64(uint64(v)) }
// Fmt_x64 formats an int64 in hexadecimal. // Fmt_x64 formats an int64 in hexadecimal.
func (f *Fmt) Fmt_x64(v int64) *Fmt { func (f *Fmt) Fmt_x64(v int64) {
f.pad(f.integer(v, 16, true, ldigits)); f.padBytes(f.integer(v, 16, true, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_x32 formats an int32 in hexadecimal. // Fmt_x32 formats an int32 in hexadecimal.
func (f *Fmt) Fmt_x32(v int32) *Fmt { return f.Fmt_x64(int64(v)) } func (f *Fmt) Fmt_x32(v int32) { f.Fmt_x64(int64(v)) }
// Fmt_x formats an int in hexadecimal. // Fmt_x formats an int in hexadecimal.
func (f *Fmt) Fmt_x(v int) *Fmt { return f.Fmt_x64(int64(v)) } func (f *Fmt) Fmt_x(v int) { f.Fmt_x64(int64(v)) }
// Fmt_ux64 formats a uint64 in hexadecimal. // Fmt_ux64 formats a uint64 in hexadecimal.
func (f *Fmt) Fmt_ux64(v uint64) *Fmt { func (f *Fmt) Fmt_ux64(v uint64) {
f.pad(f.integer(int64(v), 16, false, ldigits)); f.padBytes(f.integer(int64(v), 16, false, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_ux32 formats a uint32 in hexadecimal. // Fmt_ux32 formats a uint32 in hexadecimal.
func (f *Fmt) Fmt_ux32(v uint32) *Fmt { return f.Fmt_ux64(uint64(v)) } func (f *Fmt) Fmt_ux32(v uint32) { f.Fmt_ux64(uint64(v)) }
// Fmt_ux formats a uint in hexadecimal. // Fmt_ux formats a uint in hexadecimal.
func (f *Fmt) Fmt_ux(v uint) *Fmt { return f.Fmt_ux64(uint64(v)) } func (f *Fmt) Fmt_ux(v uint) { f.Fmt_ux64(uint64(v)) }
// Fmt_X64 formats an int64 in upper case hexadecimal. // Fmt_X64 formats an int64 in upper case hexadecimal.
func (f *Fmt) Fmt_X64(v int64) *Fmt { func (f *Fmt) Fmt_X64(v int64) {
f.pad(f.integer(v, 16, true, udigits)); f.padBytes(f.integer(v, 16, true, udigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_X32 formats an int32 in upper case hexadecimal. // Fmt_X32 formats an int32 in upper case hexadecimal.
func (f *Fmt) Fmt_X32(v int32) *Fmt { return f.Fmt_X64(int64(v)) } func (f *Fmt) Fmt_X32(v int32) { f.Fmt_X64(int64(v)) }
// Fmt_X formats an int in upper case hexadecimal. // Fmt_X formats an int in upper case hexadecimal.
func (f *Fmt) Fmt_X(v int) *Fmt { return f.Fmt_X64(int64(v)) } func (f *Fmt) Fmt_X(v int) { f.Fmt_X64(int64(v)) }
// Fmt_uX64 formats a uint64 in upper case hexadecimal. // Fmt_uX64 formats a uint64 in upper case hexadecimal.
func (f *Fmt) Fmt_uX64(v uint64) *Fmt { func (f *Fmt) Fmt_uX64(v uint64) {
f.pad(f.integer(int64(v), 16, false, udigits)); f.padBytes(f.integer(int64(v), 16, false, udigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_uX32 formats a uint32 in upper case hexadecimal. // Fmt_uX32 formats a uint32 in upper case hexadecimal.
func (f *Fmt) Fmt_uX32(v uint32) *Fmt { return f.Fmt_uX64(uint64(v)) } func (f *Fmt) Fmt_uX32(v uint32) { f.Fmt_uX64(uint64(v)) }
// Fmt_uX formats a uint in upper case hexadecimal. // Fmt_uX formats a uint in upper case hexadecimal.
func (f *Fmt) Fmt_uX(v uint) *Fmt { return f.Fmt_uX64(uint64(v)) } func (f *Fmt) Fmt_uX(v uint) { f.Fmt_uX64(uint64(v)) }
// Fmt_o64 formats an int64 in octal. // Fmt_o64 formats an int64 in octal.
func (f *Fmt) Fmt_o64(v int64) *Fmt { func (f *Fmt) Fmt_o64(v int64) {
f.pad(f.integer(v, 8, true, ldigits)); f.padBytes(f.integer(v, 8, true, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_o32 formats an int32 in octal. // Fmt_o32 formats an int32 in octal.
func (f *Fmt) Fmt_o32(v int32) *Fmt { return f.Fmt_o64(int64(v)) } func (f *Fmt) Fmt_o32(v int32) { f.Fmt_o64(int64(v)) }
// Fmt_o formats an int in octal. // Fmt_o formats an int in octal.
func (f *Fmt) Fmt_o(v int) *Fmt { return f.Fmt_o64(int64(v)) } func (f *Fmt) Fmt_o(v int) { f.Fmt_o64(int64(v)) }
// Fmt_uo64 formats a uint64 in octal. // Fmt_uo64 formats a uint64 in octal.
func (f *Fmt) Fmt_uo64(v uint64) *Fmt { func (f *Fmt) Fmt_uo64(v uint64) {
f.pad(f.integer(int64(v), 8, false, ldigits)); f.padBytes(f.integer(int64(v), 8, false, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_uo32 formats a uint32 in octal. // Fmt_uo32 formats a uint32 in octal.
func (f *Fmt) Fmt_uo32(v uint32) *Fmt { return f.Fmt_uo64(uint64(v)) } func (f *Fmt) Fmt_uo32(v uint32) { f.Fmt_uo64(uint64(v)) }
// Fmt_uo formats a uint in octal. // Fmt_uo formats a uint in octal.
func (f *Fmt) Fmt_uo(v uint) *Fmt { return f.Fmt_uo64(uint64(v)) } func (f *Fmt) Fmt_uo(v uint) { f.Fmt_uo64(uint64(v)) }
// Fmt_b64 formats a uint64 in binary. // Fmt_b64 formats a uint64 in binary.
func (f *Fmt) Fmt_b64(v uint64) *Fmt { func (f *Fmt) Fmt_b64(v uint64) {
f.pad(f.integer(int64(v), 2, false, ldigits)); f.padBytes(f.integer(int64(v), 2, false, ldigits));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_b32 formats a uint32 in binary. // Fmt_b32 formats a uint32 in binary.
func (f *Fmt) Fmt_b32(v uint32) *Fmt { return f.Fmt_b64(uint64(v)) } func (f *Fmt) Fmt_b32(v uint32) { f.Fmt_b64(uint64(v)) }
// Fmt_b formats a uint in binary. // Fmt_b formats a uint in binary.
func (f *Fmt) Fmt_b(v uint) *Fmt { return f.Fmt_b64(uint64(v)) } func (f *Fmt) Fmt_b(v uint) { f.Fmt_b64(uint64(v)) }
// Fmt_c formats a Unicode character. // Fmt_c formats a Unicode character.
func (f *Fmt) Fmt_c(v int) *Fmt { func (f *Fmt) Fmt_c(v int) {
f.pad(string(v)); f.pad(string(v));
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_s formats a string. // Fmt_s formats a string.
func (f *Fmt) Fmt_s(s string) *Fmt { func (f *Fmt) Fmt_s(s string) {
if f.prec_present { if f.precPresent {
if f.prec < len(s) { if f.prec < len(s) {
s = s[0:f.prec] s = s[0:f.prec]
} }
} }
f.pad(s); f.pad(s);
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_sx formats a string as a hexadecimal encoding of its bytes. // Fmt_sx formats a string as a hexadecimal encoding of its bytes.
func (f *Fmt) Fmt_sx(s string) *Fmt { func (f *Fmt) Fmt_sx(s string) {
t := ""; t := "";
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
if i > 0 && f.space { if i > 0 && f.space {
...@@ -384,12 +374,11 @@ func (f *Fmt) Fmt_sx(s string) *Fmt { ...@@ -384,12 +374,11 @@ func (f *Fmt) Fmt_sx(s string) *Fmt {
t += string(ldigits[v&0xF]); t += string(ldigits[v&0xF]);
} }
f.pad(t); f.pad(t);
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes. // Fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes.
func (f *Fmt) Fmt_sX(s string) *Fmt { func (f *Fmt) Fmt_sX(s string) {
t := ""; t := "";
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
v := s[i]; v := s[i];
...@@ -397,12 +386,11 @@ func (f *Fmt) Fmt_sX(s string) *Fmt { ...@@ -397,12 +386,11 @@ func (f *Fmt) Fmt_sX(s string) *Fmt {
t += string(udigits[v&0xF]); t += string(udigits[v&0xF]);
} }
f.pad(t); f.pad(t);
f.clearflags(); f.ClearFlags();
return f;
} }
// Fmt_q formats a string as a double-quoted, escaped Go string constant. // Fmt_q formats a string as a double-quoted, escaped Go string constant.
func (f *Fmt) Fmt_q(s string) *Fmt { func (f *Fmt) Fmt_q(s string) {
var quoted string; var quoted string;
if f.sharp && strconv.CanBackquote(s) { if f.sharp && strconv.CanBackquote(s) {
quoted = "`" + s + "`" quoted = "`" + s + "`"
...@@ -410,27 +398,25 @@ func (f *Fmt) Fmt_q(s string) *Fmt { ...@@ -410,27 +398,25 @@ func (f *Fmt) Fmt_q(s string) *Fmt {
quoted = strconv.Quote(s) quoted = strconv.Quote(s)
} }
f.pad(quoted); f.pad(quoted);
f.clearflags(); f.ClearFlags();
return f;
} }
// floating-point // floating-point
func doPrec(f *Fmt, def int) int { func doPrec(f *Fmt, def int) int {
if f.prec_present { if f.precPresent {
return f.prec return f.prec
} }
return def; return def;
} }
func fmtString(f *Fmt, s string) *Fmt { func fmtString(f *Fmt, s string) {
f.pad(s); f.pad(s);
f.clearflags(); f.ClearFlags();
return f;
} }
// Add a plus sign or space to the string if missing and required. // Add a plus sign or space to the string if missing and required.
func (f *Fmt) plusSpace(s string) *Fmt { func (f *Fmt) plusSpace(s string) {
if s[0] != '-' { if s[0] != '-' {
if f.plus { if f.plus {
s = "+" + s s = "+" + s
...@@ -438,94 +424,78 @@ func (f *Fmt) plusSpace(s string) *Fmt { ...@@ -438,94 +424,78 @@ func (f *Fmt) plusSpace(s string) *Fmt {
s = " " + s s = " " + s
} }
} }
return fmtString(f, s); fmtString(f, s);
} }
// Fmt_e64 formats a float64 in the form -1.23e+12. // Fmt_e64 formats a float64 in the form -1.23e+12.
func (f *Fmt) Fmt_e64(v float64) *Fmt { func (f *Fmt) Fmt_e64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'e', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa64(v, 'e', doPrec(f, 6)))
}
// Fmt_E64 formats a float64 in the form -1.23E+12. // Fmt_E64 formats a float64 in the form -1.23E+12.
func (f *Fmt) Fmt_E64(v float64) *Fmt { func (f *Fmt) Fmt_E64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'E', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa64(v, 'E', doPrec(f, 6)))
}
// Fmt_f64 formats a float64 in the form -1.23. // Fmt_f64 formats a float64 in the form -1.23.
func (f *Fmt) Fmt_f64(v float64) *Fmt { func (f *Fmt) Fmt_f64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'f', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa64(v, 'f', doPrec(f, 6)))
}
// Fmt_g64 formats a float64 in the 'f' or 'e' form according to size. // Fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
func (f *Fmt) Fmt_g64(v float64) *Fmt { func (f *Fmt) Fmt_g64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'g', doPrec(f, -1))) }
return f.plusSpace(strconv.Ftoa64(v, 'g', doPrec(f, -1)))
}
// Fmt_g64 formats a float64 in the 'f' or 'E' form according to size. // Fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
func (f *Fmt) Fmt_G64(v float64) *Fmt { func (f *Fmt) Fmt_G64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'G', doPrec(f, -1))) }
return f.plusSpace(strconv.Ftoa64(v, 'G', doPrec(f, -1)))
}
// Fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2). // Fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
func (f *Fmt) Fmt_fb64(v float64) *Fmt { return f.plusSpace(strconv.Ftoa64(v, 'b', 0)) } func (f *Fmt) Fmt_fb64(v float64) { f.plusSpace(strconv.Ftoa64(v, 'b', 0)) }
// float32 // float32
// cannot defer to float64 versions // cannot defer to float64 versions
// because it will get rounding wrong in corner cases. // because it will get rounding wrong in corner cases.
// Fmt_e32 formats a float32 in the form -1.23e+12. // Fmt_e32 formats a float32 in the form -1.23e+12.
func (f *Fmt) Fmt_e32(v float32) *Fmt { func (f *Fmt) Fmt_e32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'e', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa32(v, 'e', doPrec(f, 6)))
}
// Fmt_E32 formats a float32 in the form -1.23E+12. // Fmt_E32 formats a float32 in the form -1.23E+12.
func (f *Fmt) Fmt_E32(v float32) *Fmt { func (f *Fmt) Fmt_E32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'E', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa32(v, 'E', doPrec(f, 6)))
}
// Fmt_f32 formats a float32 in the form -1.23. // Fmt_f32 formats a float32 in the form -1.23.
func (f *Fmt) Fmt_f32(v float32) *Fmt { func (f *Fmt) Fmt_f32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'f', doPrec(f, 6))) }
return f.plusSpace(strconv.Ftoa32(v, 'f', doPrec(f, 6)))
}
// Fmt_g32 formats a float32 in the 'f' or 'e' form according to size. // Fmt_g32 formats a float32 in the 'f' or 'e' form according to size.
func (f *Fmt) Fmt_g32(v float32) *Fmt { func (f *Fmt) Fmt_g32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'g', doPrec(f, -1))) }
return f.plusSpace(strconv.Ftoa32(v, 'g', doPrec(f, -1)))
}
// Fmt_G32 formats a float32 in the 'f' or 'E' form according to size. // Fmt_G32 formats a float32 in the 'f' or 'E' form according to size.
func (f *Fmt) Fmt_G32(v float32) *Fmt { func (f *Fmt) Fmt_G32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'G', doPrec(f, -1))) }
return f.plusSpace(strconv.Ftoa32(v, 'G', doPrec(f, -1)))
}
// Fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2). // Fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
func (f *Fmt) Fmt_fb32(v float32) *Fmt { return fmtString(f, strconv.Ftoa32(v, 'b', 0)) } func (f *Fmt) Fmt_fb32(v float32) { fmtString(f, strconv.Ftoa32(v, 'b', 0)) }
// float // float
func (x *Fmt) f(a float) *Fmt { func (x *Fmt) f(a float) {
if strconv.FloatSize == 32 { if strconv.FloatSize == 32 {
return x.Fmt_f32(float32(a)) x.Fmt_f32(float32(a))
} else {
x.Fmt_f64(float64(a))
} }
return x.Fmt_f64(float64(a));
} }
func (x *Fmt) e(a float) *Fmt { func (x *Fmt) e(a float) {
if strconv.FloatSize == 32 { if strconv.FloatSize == 32 {
return x.Fmt_e32(float32(a)) x.Fmt_e32(float32(a))
} else {
x.Fmt_e64(float64(a))
} }
return x.Fmt_e64(float64(a));
} }
func (x *Fmt) g(a float) *Fmt { func (x *Fmt) g(a float) {
if strconv.FloatSize == 32 { if strconv.FloatSize == 32 {
return x.Fmt_g32(float32(a)) x.Fmt_g32(float32(a))
} else {
x.Fmt_g64(float64(a))
} }
return x.Fmt_g64(float64(a));
} }
func (x *Fmt) fb(a float) *Fmt { func (x *Fmt) fb(a float) {
if strconv.FloatSize == 32 { if strconv.FloatSize == 32 {
return x.Fmt_fb32(float32(a)) x.Fmt_fb32(float32(a))
} else {
x.Fmt_fb64(float64(a))
} }
return x.Fmt_fb64(float64(a));
} }
...@@ -77,12 +77,27 @@ package fmt ...@@ -77,12 +77,27 @@ package fmt
import ( import (
"bytes";
"io"; "io";
"os"; "os";
"reflect"; "reflect";
"utf8"; "utf8";
) )
// Some constants in the form of bytes, to avoid string overhead.
// Needlessly fastidious, I suppose.
var (
trueBytes = []byte{'t', 'r', 'u', 'e'};
falseBytes = []byte{'f', 'a', 'l', 's', 'e'};
commaSpaceBytes = []byte{',', ' '};
nilAngleBytes = []byte{'<', 'n', 'i', 'l', '>'};
nilParenBytes = []byte{'(', 'n', 'i', 'l', ')'};
nilBytes = []byte{'n', 'i', 'l'};
mapBytes = []byte{'m', 'a', 'p', '['};
missingBytes = []byte{'m', 'i', 's', 's', 'i', 'n', 'g'};
extraBytes = []byte{'?', '(', 'e', 'x', 't', 'r', 'a', ' '};
)
// State represents the printer state passed to custom formatters. // State represents the printer state passed to custom formatters.
// It provides access to the io.Writer interface plus information about // It provides access to the io.Writer interface plus information about
// the flags and options for the operand's format specifier. // the flags and options for the operand's format specifier.
...@@ -126,19 +141,29 @@ const allocSize = 32 ...@@ -126,19 +141,29 @@ const allocSize = 32
type pp struct { type pp struct {
n int; n int;
buf []byte; buf bytes.Buffer;
fmt *Fmt; runeBuf [utf8.UTFMax]byte;
fmt Fmt;
} }
// A leaky bucket of reusable pp structures.
var ppFree = make(chan *pp, 100)
func newPrinter() *pp { func newPrinter() *pp {
p := new(pp); p, ok := <-ppFree;
p.fmt = New(); if !ok {
p = new(pp)
}
p.buf.Reset();
p.fmt.Init(&p.buf);
return p; return p;
} }
func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.wid_present } func (p *pp) free() { _ = ppFree <- p }
func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.prec_present } func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }
func (p *pp) Flag(b int) bool { func (p *pp) Flag(b int) bool {
switch b { switch b {
...@@ -156,52 +181,19 @@ func (p *pp) Flag(b int) bool { ...@@ -156,52 +181,19 @@ func (p *pp) Flag(b int) bool {
return false; return false;
} }
func (p *pp) ensure(n int) {
if len(p.buf) < n {
newn := allocSize + len(p.buf);
if newn < n {
newn = n + allocSize
}
b := make([]byte, newn);
for i := 0; i < p.n; i++ {
b[i] = p.buf[i]
}
p.buf = b;
}
}
func (p *pp) addstr(s string) {
n := len(s);
p.ensure(p.n + n);
for i := 0; i < n; i++ {
p.buf[p.n] = s[i];
p.n++;
}
}
func (p *pp) addbytes(b []byte, start, end int) {
p.ensure(p.n + end - start);
for i := start; i < end; i++ {
p.buf[p.n] = b[i];
p.n++;
}
}
func (p *pp) add(c int) { func (p *pp) add(c int) {
p.ensure(p.n + 1);
if c < runeSelf { if c < runeSelf {
p.buf[p.n] = byte(c); p.buf.WriteByte(byte(c))
p.n++;
} else { } else {
p.addstr(string(c)) w := utf8.EncodeRune(c, &p.runeBuf);
p.buf.Write(p.runeBuf[0:w]);
} }
} }
// Implement Write so we can call fprintf on a P, for // Implement Write so we can call Fprintf on a pp (through State), for
// recursive use in custom verbs. // recursive use in custom verbs.
func (p *pp) Write(b []byte) (ret int, err os.Error) { func (p *pp) Write(b []byte) (ret int, err os.Error) {
p.addbytes(b, 0, len(b)); return p.buf.Write(b)
return len(b), nil;
} }
// These routines end in 'f' and take a format string. // These routines end in 'f' and take a format string.
...@@ -211,8 +203,9 @@ func Fprintf(w io.Writer, format string, a ...) (n int, error os.Error) { ...@@ -211,8 +203,9 @@ func Fprintf(w io.Writer, format string, a ...) (n int, error os.Error) {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprintf(format, v); p.doprintf(format, v);
n, error = w.Write(p.buf[0:p.n]); n64, error := p.buf.WriteTo(w);
return n, error; p.free();
return int(n64), error;
} }
// Printf formats according to a format specifier and writes to standard output. // Printf formats according to a format specifier and writes to standard output.
...@@ -226,7 +219,8 @@ func Sprintf(format string, a ...) string { ...@@ -226,7 +219,8 @@ func Sprintf(format string, a ...) string {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprintf(format, v); p.doprintf(format, v);
s := string(p.buf)[0:p.n]; s := p.buf.String();
p.free();
return s; return s;
} }
...@@ -238,8 +232,9 @@ func Fprint(w io.Writer, a ...) (n int, error os.Error) { ...@@ -238,8 +232,9 @@ func Fprint(w io.Writer, a ...) (n int, error os.Error) {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprint(v, false, false); p.doprint(v, false, false);
n, error = w.Write(p.buf[0:p.n]); n64, error := p.buf.WriteTo(w);
return n, error; p.free();
return int(n64), error;
} }
// Print formats using the default formats for its operands and writes to standard output. // Print formats using the default formats for its operands and writes to standard output.
...@@ -255,8 +250,8 @@ func Sprint(a ...) string { ...@@ -255,8 +250,8 @@ func Sprint(a ...) string {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprint(v, false, false); p.doprint(v, false, false);
s := string(p.buf)[0:p.n]; p.free();
return s; return p.buf.String();
} }
// These routines end in 'ln', do not take a format string, // These routines end in 'ln', do not take a format string,
...@@ -269,8 +264,9 @@ func Fprintln(w io.Writer, a ...) (n int, error os.Error) { ...@@ -269,8 +264,9 @@ func Fprintln(w io.Writer, a ...) (n int, error os.Error) {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprint(v, true, true); p.doprint(v, true, true);
n, error = w.Write(p.buf[0:p.n]); n64, error := p.buf.WriteTo(w);
return n, error; p.free();
return int(n64), error;
} }
// Println formats using the default formats for its operands and writes to standard output. // Println formats using the default formats for its operands and writes to standard output.
...@@ -286,7 +282,8 @@ func Sprintln(a ...) string { ...@@ -286,7 +282,8 @@ func Sprintln(a ...) string {
v := reflect.NewValue(a).(*reflect.StructValue); v := reflect.NewValue(a).(*reflect.StructValue);
p := newPrinter(); p := newPrinter();
p.doprint(v, true, true); p.doprint(v, true, true);
s := string(p.buf)[0:p.n]; s := p.buf.String();
p.free();
return s; return s;
} }
...@@ -409,121 +406,120 @@ func (p *pp) printField(field reflect.Value, plus, sharp bool, depth int) (was_s ...@@ -409,121 +406,120 @@ func (p *pp) printField(field reflect.Value, plus, sharp bool, depth int) (was_s
switch { switch {
default: default:
if stringer, ok := inter.(Stringer); ok { if stringer, ok := inter.(Stringer); ok {
p.addstr(stringer.String()); p.buf.WriteString(stringer.String());
return false; // this value is not a string return false; // this value is not a string
} }
case sharp: case sharp:
if stringer, ok := inter.(GoStringer); ok { if stringer, ok := inter.(GoStringer); ok {
p.addstr(stringer.GoString()); p.buf.WriteString(stringer.GoString());
return false; // this value is not a string return false; // this value is not a string
} }
} }
} }
s := "";
BigSwitch: BigSwitch:
switch f := field.(type) { switch f := field.(type) {
case *reflect.BoolValue: case *reflect.BoolValue:
s = p.fmt.Fmt_boolean(f.Get()).Str() p.fmt.Fmt_boolean(f.Get())
case *reflect.Float32Value: case *reflect.Float32Value:
s = p.fmt.Fmt_g32(f.Get()).Str() p.fmt.Fmt_g32(f.Get())
case *reflect.Float64Value: case *reflect.Float64Value:
s = p.fmt.Fmt_g64(f.Get()).Str() p.fmt.Fmt_g64(f.Get())
case *reflect.FloatValue: case *reflect.FloatValue:
if field.Type().Size()*8 == 32 { if field.Type().Size()*8 == 32 {
s = p.fmt.Fmt_g32(float32(f.Get())).Str() p.fmt.Fmt_g32(float32(f.Get()))
} else { } else {
s = p.fmt.Fmt_g64(float64(f.Get())).Str() p.fmt.Fmt_g64(float64(f.Get()))
} }
case *reflect.StringValue: case *reflect.StringValue:
if sharp { if sharp {
s = p.fmt.Fmt_q(f.Get()).Str() p.fmt.Fmt_q(f.Get())
} else { } else {
s = p.fmt.Fmt_s(f.Get()).Str(); p.fmt.Fmt_s(f.Get());
was_string = true; was_string = true;
} }
case *reflect.MapValue: case *reflect.MapValue:
if sharp { if sharp {
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr("{"); p.buf.WriteByte('{');
} else { } else {
p.addstr("map[") p.buf.Write(mapBytes)
} }
keys := f.Keys(); keys := f.Keys();
for i, key := range keys { for i, key := range keys {
if i > 0 { if i > 0 {
if sharp { if sharp {
p.addstr(", ") p.buf.Write(commaSpaceBytes)
} else { } else {
p.addstr(" ") p.buf.WriteByte(' ')
} }
} }
p.printField(key, plus, sharp, depth+1); p.printField(key, plus, sharp, depth+1);
p.addstr(":"); p.buf.WriteByte(':');
p.printField(f.Elem(key), plus, sharp, depth+1); p.printField(f.Elem(key), plus, sharp, depth+1);
} }
if sharp { if sharp {
p.addstr("}") p.buf.WriteByte('}')
} else { } else {
p.addstr("]") p.buf.WriteByte(']')
} }
case *reflect.StructValue: case *reflect.StructValue:
if sharp { if sharp {
p.addstr(field.Type().String()) p.buf.WriteString(field.Type().String())
} }
p.add('{'); p.add('{');
v := f; v := f;
t := v.Type().(*reflect.StructType); t := v.Type().(*reflect.StructType);
p.fmt.clearflags(); // clear flags for p.printField p.fmt.ClearFlags(); // clear flags for p.printField
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
if i > 0 { if i > 0 {
if sharp { if sharp {
p.addstr(", ") p.buf.Write(commaSpaceBytes)
} else { } else {
p.addstr(" ") p.buf.WriteByte(' ')
} }
} }
if plus || sharp { if plus || sharp {
if f := t.Field(i); f.Name != "" { if f := t.Field(i); f.Name != "" {
p.addstr(f.Name); p.buf.WriteString(f.Name);
p.add(':'); p.buf.WriteByte(':');
} }
} }
p.printField(getField(v, i), plus, sharp, depth+1); p.printField(getField(v, i), plus, sharp, depth+1);
} }
p.addstr("}"); p.buf.WriteByte('}');
case *reflect.InterfaceValue: case *reflect.InterfaceValue:
value := f.Elem(); value := f.Elem();
if value == nil { if value == nil {
if sharp { if sharp {
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr("(nil)"); p.buf.Write(nilParenBytes);
} else { } else {
s = "<nil>" p.buf.Write(nilAngleBytes)
} }
} else { } else {
return p.printField(value, plus, sharp, depth+1) return p.printField(value, plus, sharp, depth+1)
} }
case reflect.ArrayOrSliceValue: case reflect.ArrayOrSliceValue:
if sharp { if sharp {
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr("{"); p.buf.WriteByte('{');
} else { } else {
p.addstr("[") p.buf.WriteByte('[')
} }
for i := 0; i < f.Len(); i++ { for i := 0; i < f.Len(); i++ {
if i > 0 { if i > 0 {
if sharp { if sharp {
p.addstr(", ") p.buf.Write(commaSpaceBytes)
} else { } else {
p.addstr(" ") p.buf.WriteByte(' ')
} }
} }
p.printField(f.Elem(i), plus, sharp, depth+1); p.printField(f.Elem(i), plus, sharp, depth+1);
} }
if sharp { if sharp {
p.addstr("}") p.buf.WriteByte('}')
} else { } else {
p.addstr("]") p.buf.WriteByte(']')
} }
case *reflect.PtrValue: case *reflect.PtrValue:
v := f.Get(); v := f.Get();
...@@ -532,86 +528,92 @@ BigSwitch: ...@@ -532,86 +528,92 @@ BigSwitch:
if v != 0 && depth == 0 { if v != 0 && depth == 0 {
switch a := f.Elem().(type) { switch a := f.Elem().(type) {
case reflect.ArrayOrSliceValue: case reflect.ArrayOrSliceValue:
p.addstr("&"); p.buf.WriteByte('&');
p.printField(a, plus, sharp, depth+1); p.printField(a, plus, sharp, depth+1);
break BigSwitch; break BigSwitch;
case *reflect.StructValue: case *reflect.StructValue:
p.addstr("&"); p.buf.WriteByte('&');
p.printField(a, plus, sharp, depth+1); p.printField(a, plus, sharp, depth+1);
break BigSwitch; break BigSwitch;
} }
} }
if sharp { if sharp {
p.addstr("("); p.buf.WriteByte('(');
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr(")("); p.buf.WriteByte(')');
p.buf.WriteByte('(');
if v == 0 { if v == 0 {
p.addstr("nil") p.buf.Write(nilBytes)
} else { } else {
p.fmt.sharp = true; p.fmt.sharp = true;
p.addstr(p.fmt.Fmt_ux64(uint64(v)).Str()); p.fmt.Fmt_ux64(uint64(v));
} }
p.addstr(")"); p.buf.WriteByte(')');
break; break;
} }
if v == 0 { if v == 0 {
s = "<nil>"; p.buf.Write(nilAngleBytes);
break; break;
} }
p.fmt.sharp = true; // turn 0x on p.fmt.sharp = true; // turn 0x on
s = p.fmt.Fmt_ux64(uint64(v)).Str(); p.fmt.Fmt_ux64(uint64(v));
case uintptrGetter: case uintptrGetter:
v := f.Get(); v := f.Get();
if sharp { if sharp {
p.addstr("("); p.buf.WriteByte('(');
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr(")("); p.buf.WriteByte(')');
p.buf.WriteByte('(');
if v == 0 { if v == 0 {
p.addstr("nil") p.buf.Write(nilBytes)
} else { } else {
p.fmt.sharp = true; p.fmt.sharp = true;
p.addstr(p.fmt.Fmt_ux64(uint64(v)).Str()); p.fmt.Fmt_ux64(uint64(v));
} }
p.addstr(")"); p.buf.WriteByte(')');
} else { } else {
p.fmt.sharp = true; // turn 0x on p.fmt.sharp = true; // turn 0x on
p.addstr(p.fmt.Fmt_ux64(uint64(f.Get())).Str()); p.fmt.Fmt_ux64(uint64(f.Get()));
} }
default: default:
v, signed, ok := getInt(field); v, signed, ok := getInt(field);
if ok { if ok {
if signed { if signed {
s = p.fmt.Fmt_d64(v).Str() p.fmt.Fmt_d64(v)
} else { } else {
if sharp { if sharp {
p.fmt.sharp = true; // turn on 0x p.fmt.sharp = true; // turn on 0x
s = p.fmt.Fmt_ux64(uint64(v)).Str(); p.fmt.Fmt_ux64(uint64(v));
} else { } else {
s = p.fmt.Fmt_ud64(uint64(v)).Str() p.fmt.Fmt_ud64(uint64(v))
} }
} }
break; break;
} }
s = "?" + field.Type().String() + "?"; p.buf.WriteByte('?');
p.buf.WriteString(field.Type().String());
p.buf.WriteByte('?');
} }
p.addstr(s);
return was_string; return was_string;
} }
func (p *pp) doprintf(format string, v *reflect.StructValue) { func (p *pp) doprintf(format string, v *reflect.StructValue) {
p.ensure(len(format)); // a good starting size
end := len(format) - 1; end := len(format) - 1;
fieldnum := 0; // we process one field per non-trivial format fieldnum := 0; // we process one field per non-trivial format
for i := 0; i <= end; { for i := 0; i <= end; {
c, w := utf8.DecodeRuneInString(format[i:]); c, w := utf8.DecodeRuneInString(format[i:]);
if c != '%' || i == end { if c != '%' || i == end {
p.add(c); if w == 1 {
p.buf.WriteByte(byte(c))
} else {
p.buf.WriteString(format[i : i+w])
}
i += w; i += w;
continue; continue;
} }
i++; i++;
// flags and widths // flags and widths
p.fmt.clearflags(); p.fmt.ClearFlags();
F: for ; i < end; i++ { F: for ; i < end; i++ {
switch format[i] { switch format[i] {
case '#': case '#':
...@@ -629,22 +631,22 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -629,22 +631,22 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
} }
} }
// do we have 20 (width)? // do we have 20 (width)?
p.fmt.wid, p.fmt.wid_present, i = parsenum(format, i, end); p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end);
// do we have .20 (precision)? // do we have .20 (precision)?
if i < end && format[i] == '.' { if i < end && format[i] == '.' {
p.fmt.prec, p.fmt.prec_present, i = parsenum(format, i+1, end) p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
} }
c, w = utf8.DecodeRuneInString(format[i:]); c, w = utf8.DecodeRuneInString(format[i:]);
i += w; i += w;
// percent is special - absorbs no operand // percent is special - absorbs no operand
if c == '%' { if c == '%' {
p.add('%'); // TODO: should we bother with width & prec? p.buf.WriteByte('%'); // TODO: should we bother with width & prec?
continue; continue;
} }
if fieldnum >= v.NumField() { // out of operands if fieldnum >= v.NumField() { // out of operands
p.add('%'); p.buf.WriteByte('%');
p.add(c); p.add(c);
p.addstr("(missing)"); p.buf.Write(missingBytes);
continue; continue;
} }
field := getField(v, fieldnum); field := getField(v, fieldnum);
...@@ -660,15 +662,14 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -660,15 +662,14 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
} }
} }
s := "";
switch c { switch c {
// bool // bool
case 't': case 't':
if v, ok := getBool(field); ok { if v, ok := getBool(field); ok {
if v { if v {
s = "true" p.buf.Write(trueBytes)
} else { } else {
s = "false" p.buf.Write(falseBytes)
} }
} else { } else {
goto badtype goto badtype
...@@ -677,26 +678,26 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -677,26 +678,26 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
// int // int
case 'b': case 'b':
if v, _, ok := getInt(field); ok { if v, _, ok := getInt(field); ok {
s = p.fmt.Fmt_b64(uint64(v)).Str() // always unsigned p.fmt.Fmt_b64(uint64(v)) // always unsigned
} else if v, ok := getFloat32(field); ok { } else if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_fb32(v).Str() p.fmt.Fmt_fb32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_fb64(v).Str() p.fmt.Fmt_fb64(v)
} else { } else {
goto badtype goto badtype
} }
case 'c': case 'c':
if v, _, ok := getInt(field); ok { if v, _, ok := getInt(field); ok {
s = p.fmt.Fmt_c(int(v)).Str() p.fmt.Fmt_c(int(v))
} else { } else {
goto badtype goto badtype
} }
case 'd': case 'd':
if v, signed, ok := getInt(field); ok { if v, signed, ok := getInt(field); ok {
if signed { if signed {
s = p.fmt.Fmt_d64(v).Str() p.fmt.Fmt_d64(v)
} else { } else {
s = p.fmt.Fmt_ud64(uint64(v)).Str() p.fmt.Fmt_ud64(uint64(v))
} }
} else { } else {
goto badtype goto badtype
...@@ -704,9 +705,9 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -704,9 +705,9 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
case 'o': case 'o':
if v, signed, ok := getInt(field); ok { if v, signed, ok := getInt(field); ok {
if signed { if signed {
s = p.fmt.Fmt_o64(v).Str() p.fmt.Fmt_o64(v)
} else { } else {
s = p.fmt.Fmt_uo64(uint64(v)).Str() p.fmt.Fmt_uo64(uint64(v))
} }
} else { } else {
goto badtype goto badtype
...@@ -714,24 +715,24 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -714,24 +715,24 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
case 'x': case 'x':
if v, signed, ok := getInt(field); ok { if v, signed, ok := getInt(field); ok {
if signed { if signed {
s = p.fmt.Fmt_x64(v).Str() p.fmt.Fmt_x64(v)
} else { } else {
s = p.fmt.Fmt_ux64(uint64(v)).Str() p.fmt.Fmt_ux64(uint64(v))
} }
} else if v, ok := getString(field); ok { } else if v, ok := getString(field); ok {
s = p.fmt.Fmt_sx(v).Str() p.fmt.Fmt_sx(v)
} else { } else {
goto badtype goto badtype
} }
case 'X': case 'X':
if v, signed, ok := getInt(field); ok { if v, signed, ok := getInt(field); ok {
if signed { if signed {
s = p.fmt.Fmt_X64(v).Str() p.fmt.Fmt_X64(v)
} else { } else {
s = p.fmt.Fmt_uX64(uint64(v)).Str() p.fmt.Fmt_uX64(uint64(v))
} }
} else if v, ok := getString(field); ok { } else if v, ok := getString(field); ok {
s = p.fmt.Fmt_sX(v).Str() p.fmt.Fmt_sX(v)
} else { } else {
goto badtype goto badtype
} }
...@@ -739,41 +740,41 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -739,41 +740,41 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
// float // float
case 'e': case 'e':
if v, ok := getFloat32(field); ok { if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_e32(v).Str() p.fmt.Fmt_e32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_e64(v).Str() p.fmt.Fmt_e64(v)
} else { } else {
goto badtype goto badtype
} }
case 'E': case 'E':
if v, ok := getFloat32(field); ok { if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_E32(v).Str() p.fmt.Fmt_E32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_E64(v).Str() p.fmt.Fmt_E64(v)
} else { } else {
goto badtype goto badtype
} }
case 'f': case 'f':
if v, ok := getFloat32(field); ok { if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_f32(v).Str() p.fmt.Fmt_f32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_f64(v).Str() p.fmt.Fmt_f64(v)
} else { } else {
goto badtype goto badtype
} }
case 'g': case 'g':
if v, ok := getFloat32(field); ok { if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_g32(v).Str() p.fmt.Fmt_g32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_g64(v).Str() p.fmt.Fmt_g64(v)
} else { } else {
goto badtype goto badtype
} }
case 'G': case 'G':
if v, ok := getFloat32(field); ok { if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_G32(v).Str() p.fmt.Fmt_G32(v)
} else if v, ok := getFloat64(field); ok { } else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_G64(v).Str() p.fmt.Fmt_G64(v)
} else { } else {
goto badtype goto badtype
} }
...@@ -783,18 +784,18 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -783,18 +784,18 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
if inter != nil { if inter != nil {
// if object implements String, use the result. // if object implements String, use the result.
if stringer, ok := inter.(Stringer); ok { if stringer, ok := inter.(Stringer); ok {
s = p.fmt.Fmt_s(stringer.String()).Str(); p.fmt.Fmt_s(stringer.String());
break; break;
} }
} }
if v, ok := getString(field); ok { if v, ok := getString(field); ok {
s = p.fmt.Fmt_s(v).Str() p.fmt.Fmt_s(v)
} else { } else {
goto badtype goto badtype
} }
case 'q': case 'q':
if v, ok := getString(field); ok { if v, ok := getString(field); ok {
s = p.fmt.Fmt_q(v).Str() p.fmt.Fmt_q(v)
} else { } else {
goto badtype goto badtype
} }
...@@ -803,9 +804,10 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -803,9 +804,10 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
case 'p': case 'p':
if v, ok := getPtr(field); ok { if v, ok := getPtr(field); ok {
if v == 0 { if v == 0 {
s = "<nil>" p.buf.Write(nilAngleBytes)
} else { } else {
s = "0x" + p.fmt.Fmt_uX64(uint64(v)).Str() p.fmt.Fmt_s("0x");
p.fmt.Fmt_uX64(uint64(v));
} }
} else { } else {
goto badtype goto badtype
...@@ -820,29 +822,31 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) { ...@@ -820,29 +822,31 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
// the value's type // the value's type
case 'T': case 'T':
s = field.Type().String() p.buf.WriteString(field.Type().String())
default: default:
badtype: badtype:
s = "%" + string(c) + "(" + field.Type().String() + "="; p.buf.WriteByte('%');
p.addstr(s); p.add(c);
p.buf.WriteByte('(');
p.buf.WriteString(field.Type().String());
p.buf.WriteByte('=');
p.printField(field, false, false, 0); p.printField(field, false, false, 0);
s = ")"; p.buf.WriteByte(')');
} }
p.addstr(s);
} }
if fieldnum < v.NumField() { if fieldnum < v.NumField() {
p.addstr("?(extra "); p.buf.Write(extraBytes);
for ; fieldnum < v.NumField(); fieldnum++ { for ; fieldnum < v.NumField(); fieldnum++ {
field := getField(v, fieldnum); field := getField(v, fieldnum);
p.addstr(field.Type().String()); p.buf.WriteString(field.Type().String());
p.addstr("="); p.buf.WriteByte('=');
p.printField(field, false, false, 0); p.printField(field, false, false, 0);
if fieldnum+1 < v.NumField() { if fieldnum+1 < v.NumField() {
p.addstr(", ") p.buf.Write(commaSpaceBytes)
} }
} }
p.addstr(")"); p.buf.WriteByte(')');
} }
} }
...@@ -854,12 +858,12 @@ func (p *pp) doprint(v *reflect.StructValue, addspace, addnewline bool) { ...@@ -854,12 +858,12 @@ func (p *pp) doprint(v *reflect.StructValue, addspace, addnewline bool) {
if fieldnum > 0 { if fieldnum > 0 {
_, is_string := field.(*reflect.StringValue); _, is_string := field.(*reflect.StringValue);
if addspace || !is_string && !prev_string { if addspace || !is_string && !prev_string {
p.add(' ') p.buf.WriteByte(' ')
} }
} }
prev_string = p.printField(field, false, false, 0); prev_string = p.printField(field, false, false, 0);
} }
if addnewline { if addnewline {
p.add('\n') p.buf.WriteByte('\n')
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment