summaryrefslogtreecommitdiff
path: root/src/pkg/fmt
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2014-06-19 09:22:53 +0200
committerMichael Stapelberg <stapelberg@debian.org>2014-06-19 09:22:53 +0200
commit8a39ee361feb9bf46d728ff1ba4f07ca1d9610b1 (patch)
tree4449f2036cccf162e8417cc5841a35815b3e7ac5 /src/pkg/fmt
parentc8bf49ef8a92e2337b69c14b9b88396efe498600 (diff)
downloadgolang-upstream/1.3.tar.gz
Imported Upstream version 1.3upstream/1.3
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r--src/pkg/fmt/doc.go58
-rw-r--r--src/pkg/fmt/fmt_test.go234
-rw-r--r--src/pkg/fmt/format.go139
-rw-r--r--src/pkg/fmt/print.go53
-rw-r--r--src/pkg/fmt/scan.go10
5 files changed, 342 insertions, 152 deletions
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index 095fd03b2..02642d6ae 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -37,6 +37,7 @@
%e scientific notation, e.g. -1234.456e+78
%E scientific notation, e.g. -1234.456E+78
%f decimal point but no exponent, e.g. 123.456
+ %F synonym for %f
%g whichever of %e or %f produces more compact output
%G whichever of %E or %f produces more compact output
String and slice of bytes:
@@ -50,23 +51,39 @@
There is no 'u' flag. Integers are printed unsigned if they have unsigned type.
Similarly, there is no need to specify the size of the operand (int8, int64).
- The width and precision control formatting and are in units of Unicode
- code points. (This differs from C's printf where the units are numbers
+ Width is specified by an optional decimal number immediately following the verb.
+ If absent, the width is whatever is necessary to represent the value.
+ Precision is specified after the (optional) width by a period followed by a
+ decimal number. If no period is present, a default precision is used.
+ A period with no following number specifies a precision of zero.
+ Examples:
+ %f: default width, default precision
+ %9f width 9, default precision
+ %.2f default width, precision 2
+ %9.2f width 9, precision 2
+ %9.f width 9, precision 0
+
+ Width and precision are measured in units of Unicode code points.
+ (This differs from C's printf where the units are numbers
of bytes.) Either or both of the flags may be replaced with the
character '*', causing their values to be obtained from the next
operand, which must be of type int.
- For numeric values, width sets the minimum width of the field and
+ For most values, width is the minimum number of characters to output,
+ padding the formatted form with spaces if necessary.
+ For strings, precision is the maximum number of characters to output,
+ truncating if necessary.
+
+ For floating-point values, width sets the minimum width of the field and
precision sets the number of places after the decimal, if appropriate,
except that for %g/%G it sets the total number of digits. For example,
given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5.
The default precision for %e and %f is 6; for %g it is the smallest
number of digits necessary to identify the value uniquely.
- For most values, width is the minimum number of characters to output,
- padding the formatted form with spaces if necessary.
- For strings, precision is the maximum number of characters to output,
- truncating if necessary.
+ For complex numbers, the width and precision apply to the two
+ components independently and the result is parenthesized, so %f applied
+ to 1.2+3.4i produces (1.200000+3.400000i).
Other flags:
+ always print a sign for numeric values;
@@ -98,20 +115,33 @@
fmt.Printf("%v\n", i)
will print 23.
- If an operand implements interface Formatter, that interface
- can be used for fine control of formatting.
+ Except when printed using the verbs %T and %p, special
+ formatting considerations apply for operands that implement
+ certain interfaces. In order of application:
+
+ 1. If an operand implements the Formatter interface, it will
+ be invoked. Formatter provides fine control of formatting.
+
+ 2. If the %v verb is used with the # flag (%#v) and the operand
+ implements the GoStringer interface, that will be invoked.
If the format (which is implicitly %v for Println etc.) is valid
- for a string (%s %q %v %x %X), the following two rules also apply:
+ for a string (%s %q %v %x %X), the following two rules apply:
- 1. If an operand implements the error interface, the Error method
- will be used to convert the object to a string, which will then
+ 3. If an operand implements the error interface, the Error method
+ will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
- 2. If an operand implements method String() string, that method
- will be used to convert the object to a string, which will then
+ 4. If an operand implements method String() string, that method
+ will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
+ For compound operands such as slices and structs, the format
+ applies to the elements of each operand, recursively, not to the
+ operand as a whole. Thus %q will quote each element of a slice
+ of strings, and %6.2f will control formatting for each element
+ of a floating-point array.
+
To avoid recursion in cases such as
type X string
func (x X) String() string { return Sprintf("<%s>", x) }
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index bf50675f5..7e3d06b9f 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -220,6 +220,12 @@ var fmtTests = []struct {
{"%+.3e", 0.0, "+0.000e+00"},
{"%+.3e", 1.0, "+1.000e+00"},
{"%+.3f", -1.0, "-1.000"},
+ {"%+.3F", -1.0, "-1.000"},
+ {"%+.3F", float32(-1.0), "-1.000"},
+ {"%+07.2f", 1.0, "+001.00"},
+ {"%+07.2f", -1.0, "-001.00"},
+ {"%+10.2f", +1.0, " +1.00"},
+ {"%+10.2f", -1.0, " -1.00"},
{"% .3E", -1.0, "-1.000E+00"},
{"% .3e", 1.0, " 1.000e+00"},
{"%+.3g", 0.0, "+0"},
@@ -239,6 +245,8 @@ var fmtTests = []struct {
{"%+.3g", 1 + 2i, "(+1+2i)"},
{"%.3e", 0i, "(0.000e+00+0.000e+00i)"},
{"%.3f", 0i, "(0.000+0.000i)"},
+ {"%.3F", 0i, "(0.000+0.000i)"},
+ {"%.3F", complex64(0i), "(0.000+0.000i)"},
{"%.3g", 0i, "(0+0i)"},
{"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"},
{"%.3f", 1 + 2i, "(1.000+2.000i)"},
@@ -397,6 +405,8 @@ var fmtTests = []struct {
{"%#v", "foo", `"foo"`},
{"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
{"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
+ {"%#v", []byte(nil), "[]byte(nil)"},
+ {"%#v", []int32(nil), "[]int32(nil)"},
// slices with other formats
{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`},
@@ -495,18 +505,85 @@ var fmtTests = []struct {
{"%v", map[float64]int{math.NaN(): 1, math.NaN(): 2}, "map[NaN:<nil> NaN:<nil>]"},
// Used to crash because nByte didn't allow for a sign.
- {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
+ {"%b", int64(-1 << 63), zeroFill("-1", 63, "")},
// Used to panic.
- {"%0100d", 1, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
- {"%0100d", -1, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
- {"%0.100f", 1.0, "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
- {"%0.100f", -1.0, "-1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ {"%0100d", 1, zeroFill("", 100, "1")},
+ {"%0100d", -1, zeroFill("-", 99, "1")},
+ {"%0.100f", 1.0, zeroFill("1.", 100, "")},
+ {"%0.100f", -1.0, zeroFill("-1.", 100, "")},
+
+ // Comparison of padding rules with C printf.
+ /*
+ C program:
+ #include <stdio.h>
+
+ char *format[] = {
+ "[%.2f]",
+ "[% .2f]",
+ "[%+.2f]",
+ "[%7.2f]",
+ "[% 7.2f]",
+ "[%+7.2f]",
+ "[%07.2f]",
+ "[% 07.2f]",
+ "[%+07.2f]",
+ };
+
+ int main(void) {
+ int i;
+ for(i = 0; i < 9; i++) {
+ printf("%s: ", format[i]);
+ printf(format[i], 1.0);
+ printf(" ");
+ printf(format[i], -1.0);
+ printf("\n");
+ }
+ }
- // Zero padding floats used to put the minus sign in the middle.
- {"%020f", -1.0, "-000000000001.000000"},
+ Output:
+ [%.2f]: [1.00] [-1.00]
+ [% .2f]: [ 1.00] [-1.00]
+ [%+.2f]: [+1.00] [-1.00]
+ [%7.2f]: [ 1.00] [ -1.00]
+ [% 7.2f]: [ 1.00] [ -1.00]
+ [%+7.2f]: [ +1.00] [ -1.00]
+ [%07.2f]: [0001.00] [-001.00]
+ [% 07.2f]: [ 001.00] [-001.00]
+ [%+07.2f]: [+001.00] [-001.00]
+ */
+ {"%.2f", 1.0, "1.00"},
+ {"%.2f", -1.0, "-1.00"},
+ {"% .2f", 1.0, " 1.00"},
+ {"% .2f", -1.0, "-1.00"},
+ {"%+.2f", 1.0, "+1.00"},
+ {"%+.2f", -1.0, "-1.00"},
+ {"%7.2f", 1.0, " 1.00"},
+ {"%7.2f", -1.0, " -1.00"},
+ {"% 7.2f", 1.0, " 1.00"},
+ {"% 7.2f", -1.0, " -1.00"},
+ {"%+7.2f", 1.0, " +1.00"},
+ {"%+7.2f", -1.0, " -1.00"},
+ {"%07.2f", 1.0, "0001.00"},
+ {"%07.2f", -1.0, "-001.00"},
+ {"% 07.2f", 1.0, " 001.00"},
+ {"% 07.2f", -1.0, "-001.00"},
+ {"%+07.2f", 1.0, "+001.00"},
+ {"%+07.2f", -1.0, "-001.00"},
+
+ // Complex numbers: exhaustively tested in TestComplexFormatting.
+ {"%7.2f", 1 + 2i, "( 1.00 +2.00i)"},
+ {"%+07.2f", -1 - 2i, "(-001.00-002.00i)"},
+ // Zero padding does not apply to infinities.
+ {"%020f", math.Inf(-1), " -Inf"},
+ {"%020f", math.Inf(+1), " +Inf"},
+ {"% 020f", math.Inf(-1), " -Inf"},
+ {"% 020f", math.Inf(+1), " Inf"},
+ {"%+020f", math.Inf(-1), " -Inf"},
+ {"%+020f", math.Inf(+1), " +Inf"},
{"%20f", -1.0, " -1.000000"},
- {"%0100f", -1.0, "-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.000000"},
+ // Make sure we can handle very large widths.
+ {"%0100f", -1.0, zeroFill("-", 99, "1.000000")},
// Complex fmt used to leave the plus flag set for future entries in the array
// causing +2+0i and +3+0i instead of 2+0i and 3+0i.
@@ -515,6 +592,43 @@ var fmtTests = []struct {
// Incomplete format specification caused crash.
{"%.", 3, "%!.(int=3)"},
+
+ // Used to panic with out-of-bounds for very large numeric representations.
+ // nByte is set to handle one bit per uint64 in %b format, with a negative number.
+ // See issue 6777.
+ {"%#064x", 1, zeroFill("0x", 64, "1")},
+ {"%#064x", -1, zeroFill("-0x", 63, "1")},
+ {"%#064b", 1, zeroFill("", 64, "1")},
+ {"%#064b", -1, zeroFill("-", 63, "1")},
+ {"%#064o", 1, zeroFill("", 64, "1")},
+ {"%#064o", -1, zeroFill("-", 63, "1")},
+ {"%#064d", 1, zeroFill("", 64, "1")},
+ {"%#064d", -1, zeroFill("-", 63, "1")},
+ // Test that we handle the crossover above the size of uint64
+ {"%#072x", 1, zeroFill("0x", 72, "1")},
+ {"%#072x", -1, zeroFill("-0x", 71, "1")},
+ {"%#072b", 1, zeroFill("", 72, "1")},
+ {"%#072b", -1, zeroFill("-", 71, "1")},
+ {"%#072o", 1, zeroFill("", 72, "1")},
+ {"%#072o", -1, zeroFill("-", 71, "1")},
+ {"%#072d", 1, zeroFill("", 72, "1")},
+ {"%#072d", -1, zeroFill("-", 71, "1")},
+
+ // Padding for complex numbers. Has been bad, then fixed, then bad again.
+ {"%+10.2f", +104.66 + 440.51i, "( +104.66 +440.51i)"},
+ {"%+10.2f", -104.66 + 440.51i, "( -104.66 +440.51i)"},
+ {"%+10.2f", +104.66 - 440.51i, "( +104.66 -440.51i)"},
+ {"%+10.2f", -104.66 - 440.51i, "( -104.66 -440.51i)"},
+ {"%+010.2f", +104.66 + 440.51i, "(+000104.66+000440.51i)"},
+ {"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"},
+ {"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"},
+ {"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"},
+}
+
+// zeroFill generates zero-filled strings of the specified width. The length
+// of the suffix (but not the prefix) is compensated for in the width calculation.
+func zeroFill(prefix string, width int, suffix string) string {
+ return prefix + strings.Repeat("0", width-len(suffix)) + suffix
}
func TestSprintf(t *testing.T) {
@@ -554,6 +668,50 @@ func TestSprintf(t *testing.T) {
}
}
+// TestComplexFormatting checks that a complex always formats to the same
+// thing as if done by hand with two singleton prints.
+func TestComplexFormatting(t *testing.T) {
+ var yesNo = []bool{true, false}
+ var signs = []float64{1, 0, -1}
+ for _, plus := range yesNo {
+ for _, zero := range yesNo {
+ for _, space := range yesNo {
+ for _, char := range "fFeEgG" {
+ realFmt := "%"
+ if zero {
+ realFmt += "0"
+ }
+ if space {
+ realFmt += " "
+ }
+ if plus {
+ realFmt += "+"
+ }
+ realFmt += "10.2"
+ realFmt += string(char)
+ // Imaginary part always has a sign, so force + and ignore space.
+ imagFmt := "%"
+ if zero {
+ imagFmt += "0"
+ }
+ imagFmt += "+"
+ imagFmt += "10.2"
+ imagFmt += string(char)
+ for _, realSign := range signs {
+ for _, imagSign := range signs {
+ one := Sprintf(realFmt, complex(realSign, imagSign))
+ two := Sprintf("("+realFmt+imagFmt+"i)", realSign, imagSign)
+ if one != two {
+ t.Error(f, one, two)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
type SE []interface{} // slice of empty; notational compactness.
var reorderTests = []struct {
@@ -604,47 +762,61 @@ func TestReorder(t *testing.T) {
}
func BenchmarkSprintfEmpty(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("")
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("")
+ }
+ })
}
func BenchmarkSprintfString(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("%s", "hello")
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%s", "hello")
+ }
+ })
}
func BenchmarkSprintfInt(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("%d", 5)
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%d", 5)
+ }
+ })
}
func BenchmarkSprintfIntInt(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("%d %d", 5, 6)
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%d %d", 5, 6)
+ }
+ })
}
func BenchmarkSprintfPrefixedInt(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
+ }
+ })
}
func BenchmarkSprintfFloat(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Sprintf("%g", 5.23184)
- }
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%g", 5.23184)
+ }
+ })
}
func BenchmarkManyArgs(b *testing.B) {
- var buf bytes.Buffer
- for i := 0; i < b.N; i++ {
- buf.Reset()
- Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
- }
+ b.RunParallel(func(pb *testing.PB) {
+ var buf bytes.Buffer
+ for pb.Next() {
+ buf.Reset()
+ Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
+ }
+ })
}
var mallocBuf bytes.Buffer
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index 2e2b0716e..a89c542cf 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -5,12 +5,15 @@
package fmt
import (
+ "math"
"strconv"
"unicode/utf8"
)
const (
- nByte = 65 // %b of an int64, plus a sign.
+ // %b of an int64, plus a sign.
+ // Hex can add 0x and we handle it specially.
+ nByte = 65
ldigits = "0123456789abcdef"
udigits = "0123456789ABCDEF"
@@ -160,9 +163,16 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
}
var buf []byte = f.intbuf[0:]
- if f.widPresent && f.wid > nByte {
- // We're going to need a bigger boat.
- buf = make([]byte, f.wid)
+ if f.widPresent {
+ width := f.wid
+ if base == 16 && f.sharp {
+ // Also adds "0x".
+ width += 2
+ }
+ if width > nByte {
+ // We're going to need a bigger boat.
+ buf = make([]byte, width)
+ }
}
negative := signedness == signed && a < 0
@@ -351,35 +361,48 @@ func doPrec(f *fmt, def int) int {
// formatFloat formats a float64; it is an efficient equivalent to f.pad(strconv.FormatFloat()...).
func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
- // We leave one byte at the beginning of f.intbuf for a sign if needed,
- // and make it a space, which we might be able to use.
- f.intbuf[0] = ' '
- slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
- // Add a plus sign or space to the floating-point string representation if missing and required.
- // The formatted number starts at slice[1].
- switch slice[1] {
- case '-', '+':
- // If we're zero padding, want the sign before the leading zeros.
- // Achieve this by writing the sign out and padding the postive number.
- if f.zero && f.widPresent && f.wid > len(slice) {
- f.buf.WriteByte(slice[1])
- f.wid--
- f.pad(slice[2:])
- return
+ // Format number, reserving space for leading + sign if needed.
+ num := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
+ if num[1] == '-' || num[1] == '+' {
+ num = num[1:]
+ } else {
+ num[0] = '+'
+ }
+ // Special handling for infinity, which doesn't look like a number so shouldn't be padded with zeros.
+ if math.IsInf(v, 0) {
+ if f.zero {
+ defer func() { f.zero = true }()
+ f.zero = false
}
- // We're set; drop the leading space.
- slice = slice[1:]
- default:
- // There's no sign, but we might need one.
- if f.plus {
- slice[0] = '+'
- } else if f.space {
- // space is already there
- } else {
- slice = slice[1:]
+ }
+ // num is now a signed version of the number.
+ // If we're zero padding, want the sign before the leading zeros.
+ // Achieve this by writing the sign out and then padding the unsigned number.
+ if f.zero && f.widPresent && f.wid > len(num) {
+ if f.space && v >= 0 {
+ f.buf.WriteByte(' ') // This is what C does: even with zero, f.space means space.
+ f.wid--
+ } else if f.plus || v < 0 {
+ f.buf.WriteByte(num[0])
+ f.wid--
}
+ f.pad(num[1:])
+ return
+ }
+ // f.space says to replace a leading + with a space.
+ if f.space && num[0] == '+' {
+ num[0] = ' '
+ f.pad(num)
+ return
}
- f.pad(slice)
+ // Now we know the sign is attached directly to the number, if present at all.
+ // We want a sign if asked for, if it's negative, or if it's infinity (+Inf vs. -Inf).
+ if f.plus || num[0] == '-' || math.IsInf(v, 0) {
+ f.pad(num)
+ return
+ }
+ // No sign to show and the number is positive; just print the unsigned number.
+ f.pad(num[1:])
}
// fmt_e64 formats a float64 in the form -1.23e+12.
@@ -424,60 +447,46 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
// fmt_c64 formats a complex64 according to the verb.
func (f *fmt) fmt_c64(v complex64, verb rune) {
- f.buf.WriteByte('(')
- r := real(v)
- oldPlus := f.plus
- for i := 0; ; i++ {
- switch verb {
- case 'b':
- f.fmt_fb32(r)
- case 'e':
- f.fmt_e32(r)
- case 'E':
- f.fmt_E32(r)
- case 'f':
- f.fmt_f32(r)
- case 'g':
- f.fmt_g32(r)
- case 'G':
- f.fmt_G32(r)
- }
- if i != 0 {
- break
- }
- f.plus = true
- r = imag(v)
- }
- f.plus = oldPlus
- f.buf.Write(irparenBytes)
+ f.fmt_complex(float64(real(v)), float64(imag(v)), 32, verb)
}
// fmt_c128 formats a complex128 according to the verb.
func (f *fmt) fmt_c128(v complex128, verb rune) {
+ f.fmt_complex(real(v), imag(v), 64, verb)
+}
+
+// fmt_complex formats a complex number as (r+ji).
+func (f *fmt) fmt_complex(r, j float64, size int, verb rune) {
f.buf.WriteByte('(')
- r := real(v)
oldPlus := f.plus
+ oldSpace := f.space
+ oldWid := f.wid
for i := 0; ; i++ {
switch verb {
case 'b':
- f.fmt_fb64(r)
+ f.formatFloat(r, 'b', 0, size)
case 'e':
- f.fmt_e64(r)
+ f.formatFloat(r, 'e', doPrec(f, 6), size)
case 'E':
- f.fmt_E64(r)
- case 'f':
- f.fmt_f64(r)
+ f.formatFloat(r, 'E', doPrec(f, 6), size)
+ case 'f', 'F':
+ f.formatFloat(r, 'f', doPrec(f, 6), size)
case 'g':
- f.fmt_g64(r)
+ f.formatFloat(r, 'g', doPrec(f, -1), size)
case 'G':
- f.fmt_G64(r)
+ f.formatFloat(r, 'G', doPrec(f, -1), size)
}
if i != 0 {
break
}
+ // Imaginary part always has a sign.
f.plus = true
- r = imag(v)
+ f.space = false
+ f.wid = oldWid
+ r = j
}
+ f.space = oldSpace
f.plus = oldPlus
+ f.wid = oldWid
f.buf.Write(irparenBytes)
}
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 1ea816d6d..302661f4c 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -124,45 +124,13 @@ type pp struct {
fmt fmt
}
-// A cache holds a set of reusable objects.
-// The slice is a stack (LIFO).
-// If more are needed, the cache creates them by calling new.
-type cache struct {
- mu sync.Mutex
- saved []interface{}
- new func() interface{}
+var ppFree = sync.Pool{
+ New: func() interface{} { return new(pp) },
}
-func (c *cache) put(x interface{}) {
- c.mu.Lock()
- if len(c.saved) < cap(c.saved) {
- c.saved = append(c.saved, x)
- }
- c.mu.Unlock()
-}
-
-func (c *cache) get() interface{} {
- c.mu.Lock()
- n := len(c.saved)
- if n == 0 {
- c.mu.Unlock()
- return c.new()
- }
- x := c.saved[n-1]
- c.saved = c.saved[0 : n-1]
- c.mu.Unlock()
- return x
-}
-
-func newCache(f func() interface{}) *cache {
- return &cache{saved: make([]interface{}, 0, 100), new: f}
-}
-
-var ppFree = newCache(func() interface{} { return new(pp) })
-
// newPrinter allocates a new pp struct or grab a cached one.
func newPrinter() *pp {
- p := ppFree.get().(*pp)
+ p := ppFree.Get().(*pp)
p.panicking = false
p.erroring = false
p.fmt.init(&p.buf)
@@ -178,7 +146,7 @@ func (p *pp) free() {
p.buf = p.buf[:0]
p.arg = nil
p.value = reflect.Value{}
- ppFree.put(p)
+ ppFree.Put(p)
}
func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
@@ -479,7 +447,7 @@ func (p *pp) fmtFloat32(v float32, verb rune) {
p.fmt.fmt_e32(v)
case 'E':
p.fmt.fmt_E32(v)
- case 'f':
+ case 'f', 'F':
p.fmt.fmt_f32(v)
case 'g', 'v':
p.fmt.fmt_g32(v)
@@ -498,7 +466,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) {
p.fmt.fmt_e64(v)
case 'E':
p.fmt.fmt_E64(v)
- case 'f':
+ case 'f', 'F':
p.fmt.fmt_f64(v)
case 'g', 'v':
p.fmt.fmt_g64(v)
@@ -555,6 +523,15 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) {
func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, depth int) {
if verb == 'v' || verb == 'd' {
if goSyntax {
+ if v == nil {
+ if typ == nil {
+ p.buf.WriteString("[]byte(nil)")
+ } else {
+ p.buf.WriteString(typ.String())
+ p.buf.Write(nilParenBytes)
+ }
+ return
+ }
if typ == nil {
p.buf.Write(bytesBytes)
} else {
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index 5b1be5891..8a337e479 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -11,6 +11,7 @@ import (
"os"
"reflect"
"strconv"
+ "sync"
"unicode/utf8"
)
@@ -283,7 +284,6 @@ var space = [][2]uint16{
{0x0085, 0x0085},
{0x00a0, 0x00a0},
{0x1680, 0x1680},
- {0x180e, 0x180e},
{0x2000, 0x200a},
{0x2028, 0x2029},
{0x202f, 0x202f},
@@ -380,7 +380,9 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) {
return
}
-var ssFree = newCache(func() interface{} { return new(ss) })
+var ssFree = sync.Pool{
+ New: func() interface{} { return new(ss) },
+}
// newScanState allocates a new ss struct or grab a cached one.
func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
@@ -395,7 +397,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
return
}
- s = ssFree.get().(*ss)
+ s = ssFree.Get().(*ss)
if rr, ok := r.(io.RuneReader); ok {
s.rr = rr
} else {
@@ -427,7 +429,7 @@ func (s *ss) free(old ssave) {
}
s.buf = s.buf[:0]
s.rr = nil
- ssFree.put(s)
+ ssFree.Put(s)
}
// skipSpace skips spaces and maybe newlines.