1
0
mirror of https://github.com/golang/go synced 2024-11-25 19:37:58 -07:00

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
This commit is contained in:
Rob Pike 2009-12-06 12:03:52 -08:00
parent ed6fd1bcbe
commit 4c0e51cd43
4 changed files with 424 additions and 416 deletions

View File

@ -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) {
// 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) }
// Resize buffer to guarantee enough space for n more bytes.
// After this call, the state of b.buf is inconsistent.
// It must be fixed up as is done in Write and WriteString.
func (b *Buffer) resize(n int) {
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) {
// not enough space anywhere
buf = make([]byte, 2*cap(b.buf)+n)
}
copy(buf, b.buf[b.off:]);
}
b.buf = buf;
b.off = 0;
}
// Write appends the contents of p to the buffer. The return // Write appends the contents of p to the buffer. The return
// value n is the length of p; err is always nil. // value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) { func (b *Buffer) Write(p []byte) (n int, err os.Error) {
m := b.Len(); m := b.Len();
n = len(p); n = len(p);
if len(b.buf)+n > cap(b.buf) { if len(b.buf)+n > cap(b.buf) {
// not enough space at end b.resize(n)
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]; 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) {
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.

View File

@ -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) {
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) {
} }
} }
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) {

View File

@ -5,6 +5,7 @@
package fmt package fmt
import ( import (
"bytes";
"strconv"; "strconv";
) )
@ -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 {
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() {
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 (f *Fmt) Reset() { f.ClearFlags() }
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) 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 {
} }
// 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
// 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
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 {
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 {
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 {
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 {
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));
} }

View File

@ -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
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) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.prec_present } 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.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 {
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) {
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 {
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) {
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 {
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) {
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 {
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
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:
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) {
} }
} }
// 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) {
} }
} }
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) {
// 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) {
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) {
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) {
// 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) {
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) {
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) {
// 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) {
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')
} }
} }