diff options
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r-- | src/pkg/fmt/doc.go | 31 | ||||
-rw-r--r-- | src/pkg/fmt/fmt_test.go | 107 | ||||
-rw-r--r-- | src/pkg/fmt/format.go | 82 | ||||
-rw-r--r-- | src/pkg/fmt/print.go | 106 | ||||
-rw-r--r-- | src/pkg/fmt/scan.go | 21 | ||||
-rw-r--r-- | src/pkg/fmt/scan_test.go | 43 |
6 files changed, 269 insertions, 121 deletions
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index a9b9c9d0c..3cd02d7ed 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -31,8 +31,8 @@ %X base 16, with upper-case letters for A-F %U Unicode format: U+1234; same as "U+%04X" Floating-point and complex constituents: - %b decimalless scientific notation with exponent a power of two, - in the manner of strconv.FormatFloat with the 'b' format, + %b decimalless scientific notation with exponent a power of two, + in the manner of strconv.FormatFloat with the 'b' format, e.g. -123456p-78 %e scientific notation, e.g. -1234.456e+78 %E scientific notation, e.g. -1234.456E+78 @@ -56,13 +56,17 @@ character '*', causing their values to be obtained from the next operand, which must be of type int. - For numeric values, width sets the width of the field and precision - sets the number of places after the decimal, if appropriate. For - example, the format %6.2f prints 123.45. + For numeric 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 strings, width is the minimum number of characters to output, - padding with spaces if necessary, and precision is the maximum - number of characters to output, truncating if necessary. + 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. Other flags: + always print a sign for numeric values; @@ -70,11 +74,17 @@ - pad with spaces on the right rather than the left (left-justify the field) # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); 0X for hex (%#X); suppress 0x for %p (%#p); - print a raw (backquoted) string if possible for %q (%#q); + for %q, print a raw (backquoted) string if strconv.CanBackquote + returns true; write e.g. U+0078 'x' if the character is printable for %U (%#U). ' ' (space) leave a space for elided sign in numbers (% d); put spaces between bytes printing strings or slices in hex (% x, % X) - 0 pad with leading zeros rather than spaces + 0 pad with leading zeros rather than spaces; + for numbers, this moves the padding after the sign + + Flags are ignored by verbs that do not expect them. + For example there is no alternate decimal format, so %#d and %d + behave identically. For each Printf-like function, there is also a Print function that takes no format and is equivalent to saying %v for every @@ -152,6 +162,7 @@ %T is not implemented %e %E %f %F %g %G are all equivalent and scan any floating point or complex value %s and %v on strings scan a space-delimited token + Flags # and + are not implemented. The familiar base-setting prefixes 0 (octal) and 0x (hexadecimal) are accepted when scanning integers without a diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index de0342967..af4b5c8f8 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -9,7 +9,6 @@ import ( . "fmt" "io" "math" - "runtime" // for the malloc count test only "strings" "testing" "time" @@ -96,7 +95,7 @@ type SI struct { I interface{} } -// A type with a String method with pointer receiver for testing %p +// P is a type with a String method with pointer receiver for testing %p. type P int var pValue P @@ -105,6 +104,9 @@ func (p *P) String() string { return "String(p)" } +var barray = [5]renamedUint8{1, 2, 3, 4, 5} +var bslice = barray[:] + var b byte var fmttests = []struct { @@ -127,6 +129,10 @@ var fmttests = []struct { {"%s", []byte("abc"), "abc"}, {"%x", []byte("abc"), "616263"}, {"% x", []byte("abc\xff"), "61 62 63 ff"}, + {"%#x", []byte("abc\xff"), "0x610x620x630xff"}, + {"%#X", []byte("abc\xff"), "0X610X620X630XFF"}, + {"%# x", []byte("abc\xff"), "0x61 0x62 0x63 0xff"}, + {"%# X", []byte("abc\xff"), "0X61 0X62 0X63 0XFF"}, {"% X", []byte("abc\xff"), "61 62 63 FF"}, {"%x", []byte("xyz"), "78797a"}, {"%X", []byte("xyz"), "78797A"}, @@ -172,6 +178,8 @@ var fmttests = []struct { {"%.3q", "日本語日本語", `"日本語"`}, {"%.3q", []byte("日本語日本語"), `"日本語"`}, {"%10.1q", "日本語日本語", ` "日"`}, + {"%10v", nil, " <nil>"}, + {"%-10v", nil, "<nil> "}, // integers {"%d", 12345, "12345"}, @@ -328,14 +336,18 @@ var fmttests = []struct { // arrays {"%v", array, "[1 2 3 4 5]"}, {"%v", iarray, "[1 hello 2.5 <nil>]"}, + {"%v", barray, "[1 2 3 4 5]"}, {"%v", &array, "&[1 2 3 4 5]"}, {"%v", &iarray, "&[1 hello 2.5 <nil>]"}, + {"%v", &barray, "&[1 2 3 4 5]"}, // slices {"%v", slice, "[1 2 3 4 5]"}, {"%v", islice, "[1 hello 2.5 <nil>]"}, + {"%v", bslice, "[1 2 3 4 5]"}, {"%v", &slice, "&[1 2 3 4 5]"}, {"%v", &islice, "&[1 hello 2.5 <nil>]"}, + {"%v", &bslice, "&[1 2 3 4 5]"}, // complexes with %v {"%v", 1 + 2i, "(1+2i)"}, @@ -350,10 +362,12 @@ var fmttests = []struct { {"%+v", B{1, 2}, `{I:<1> j:2}`}, {"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`}, - // q on Stringable items + // other formats on Stringable items {"%s", I(23), `<23>`}, {"%q", I(23), `"<23>"`}, {"%x", I(23), `3c32333e`}, + {"%#x", I(23), `0x3c0x320x330x3e`}, + {"%# x", I(23), `0x3c 0x32 0x33 0x3e`}, {"%d", I(23), `23`}, // Stringer applies only to string formats. // go syntax @@ -375,6 +389,9 @@ var fmttests = []struct { {"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, {"%#v", map[int]byte(nil), `map[int]uint8(nil)`}, {"%#v", map[int]byte{}, `map[int]uint8{}`}, + {"%#v", "foo", `"foo"`}, + {"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, + {"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, // slices with other formats {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, @@ -400,6 +417,9 @@ var fmttests = []struct { {"%x", renamedString("thing"), "7468696e67"}, {"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`}, {"%q", renamedBytes([]byte("hello")), `"hello"`}, + {"%x", []renamedUint8{'a', 'b', 'c'}, "616263"}, + {"%s", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "hello"}, + {"%q", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, `"hello"`}, {"%v", renamedFloat32(22), "22"}, {"%v", renamedFloat64(33), "33"}, {"%v", renamedComplex64(3 + 4i), "(3+4i)"}, @@ -419,6 +439,8 @@ var fmttests = []struct { {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, {"%T", intVal, "int"}, {"%6T", &intVal, " *int"}, + {"%10T", nil, " <nil>"}, + {"%-10T", nil, "<nil> "}, // %p {"p0=%p", new(int), "p0=0xPTR"}, @@ -441,6 +463,11 @@ var fmttests = []struct { {"%v", (*int)(nil), "<nil>"}, {"%v", new(int), "0xPTR"}, + // %d etc. pointers use specified base. + {"%d", new(int), "PTR_d"}, + {"%o", new(int), "PTR_o"}, + {"%x", new(int), "PTR_x"}, + // %d on Stringer should give integer if possible {"%s", time.Time{}.Month(), "January"}, {"%d", time.Time{}.Month(), "1"}, @@ -464,20 +491,37 @@ var fmttests = []struct { // Used to crash because nByte didn't allow for a sign. {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"}, + + // 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. + {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, + {"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, } func TestSprintf(t *testing.T) { for _, tt := range fmttests { s := Sprintf(tt.fmt, tt.val) if i := strings.Index(tt.out, "PTR"); i >= 0 { + pattern := "PTR" + chars := "0123456789abcdefABCDEF" + switch { + case strings.HasPrefix(tt.out[i:], "PTR_d"): + pattern = "PTR_d" + chars = chars[:10] + case strings.HasPrefix(tt.out[i:], "PTR_o"): + pattern = "PTR_o" + chars = chars[:8] + case strings.HasPrefix(tt.out[i:], "PTR_x"): + pattern = "PTR_x" + } j := i for ; j < len(s); j++ { c := s[j] - if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') { + if !strings.ContainsRune(chars, rune(c)) { break } } - s = s[0:i] + "PTR" + s[j:] + s = s[0:i] + pattern + s[j:] } if s != tt.out { if _, ok := tt.val.(string); ok { @@ -527,6 +571,14 @@ func BenchmarkSprintfFloat(b *testing.B) { } } +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") + } +} + var mallocBuf bytes.Buffer var mallocTest = []struct { @@ -550,17 +602,9 @@ var _ bytes.Buffer func TestCountMallocs(t *testing.T) { for _, mt := range mallocTest { - const N = 100 - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - mallocs := 0 - memstats.Mallocs - for i := 0; i < N; i++ { - mt.fn() - } - runtime.ReadMemStats(memstats) - mallocs += memstats.Mallocs - if mallocs/N > uint64(mt.count) { - t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N) + mallocs := testing.AllocsPerRun(100, mt.fn) + if got, max := mallocs, float64(mt.count); got > max { + t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max) } } } @@ -636,7 +680,8 @@ func TestStructPrinter(t *testing.T) { } } -// Check map printing using substrings so we don't depend on the print order. +// presentInMap checks map printing using substrings so we don't depend on the +// print order. func presentInMap(s string, a []string, t *testing.T) { for i := 0; i < len(a); i++ { loc := strings.Index(s, a[i]) @@ -677,8 +722,8 @@ func TestEmptyMap(t *testing.T) { } } -// Check that Sprint (and hence Print, Fprint) puts spaces in the right places, -// that is, between arg pairs in which neither is a string. +// TestBlank checks that Sprint (and hence Print, Fprint) puts spaces in the +// right places, that is, between arg pairs in which neither is a string. func TestBlank(t *testing.T) { got := Sprint("<", 1, ">:", 1, 2, 3, "!") expect := "<1>:1 2 3!" @@ -687,8 +732,8 @@ func TestBlank(t *testing.T) { } } -// Check that Sprintln (and hence Println, Fprintln) puts spaces in the right places, -// that is, between all arg pairs. +// TestBlankln checks that Sprintln (and hence Println, Fprintln) puts spaces in +// the right places, that is, between all arg pairs. func TestBlankln(t *testing.T) { got := Sprintln("<", 1, ">:", 1, 2, 3, "!") expect := "< 1 >: 1 2 3 !\n" @@ -697,7 +742,7 @@ func TestBlankln(t *testing.T) { } } -// Check Formatter with Sprint, Sprintln, Sprintf +// TestFormatterPrintln checks Formatter with Sprint, Sprintln, Sprintf. func TestFormatterPrintln(t *testing.T) { f := F(1) expect := "<v=F(1)>\n" @@ -746,7 +791,7 @@ func TestWidthAndPrecision(t *testing.T) { } } -// A type that panics in String. +// Panic is a type that panics in String. type Panic struct { message interface{} } @@ -761,7 +806,7 @@ func (p Panic) String() string { panic(p.message) } -// A type that panics in Format. +// PanicF is a type that panics in Format. type PanicF struct { message interface{} } @@ -799,7 +844,7 @@ func TestPanics(t *testing.T) { } } -// Test that erroneous String routine doesn't cause fatal recursion. +// recurCount tests that erroneous String routine doesn't cause fatal recursion. var recurCount = 0 type Recur struct { @@ -842,3 +887,15 @@ func TestIsSpace(t *testing.T) { } } } + +func TestNilDoesNotBecomeTyped(t *testing.T) { + type A struct{} + type B struct{} + var a *A = nil + var b B = B{} + got := Sprintf("%s %s %s %s %s", nil, a, nil, b, nil) + const expect = "%!s(<nil>) %!s(*fmt_test.A=<nil>) %!s(<nil>) {} %!s(<nil>)" + if got != expect { + t.Errorf("expected:\n\t%q\ngot:\n\t%q", expect, got) + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index caf900d5c..5665db12c 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -72,7 +72,7 @@ func (f *fmt) init(buf *buffer) { f.clearflags() } -// Compute left and right padding widths (only one will be non-zero). +// computePadding computes left and right padding widths (only one will be non-zero). func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) { left := !f.minus w := f.wid @@ -95,7 +95,7 @@ func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth i return } -// Generate n bytes of padding. +// writePadding generates n bytes of padding. func (f *fmt) writePadding(n int, padding []byte) { for n > 0 { m := n @@ -107,14 +107,13 @@ func (f *fmt) writePadding(n int, padding []byte) { } } -// Append b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus) -// clear flags afterwards. +// pad appends b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus). func (f *fmt) pad(b []byte) { - var padding []byte - var left, right int - if f.widPresent && f.wid != 0 { - padding, left, right = f.computePadding(len(b)) + if !f.widPresent || f.wid == 0 { + f.buf.Write(b) + return } + padding, left, right := f.computePadding(len(b)) if left > 0 { f.writePadding(left, padding) } @@ -124,14 +123,13 @@ func (f *fmt) pad(b []byte) { } } -// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus). -// clear flags afterwards. +// padString appends s to buf, padded on left (w > 0) or right (w < 0 or f.minus). func (f *fmt) padString(s string) { - var padding []byte - var left, right int - if f.widPresent && f.wid != 0 { - padding, left, right = f.computePadding(utf8.RuneCountInString(s)) + if !f.widPresent || f.wid == 0 { + f.buf.WriteString(s) + return } + padding, left, right := f.computePadding(utf8.RuneCountInString(s)) if left > 0 { f.writePadding(left, padding) } @@ -141,17 +139,6 @@ func (f *fmt) padString(s string) { } } -func putint(buf []byte, base, val uint64, digits string) int { - i := len(buf) - 1 - for val >= base { - buf[i] = digits[val%base] - i-- - val /= base - } - buf[i] = digits[val] - return i - 1 -} - var ( trueBytes = []byte("true") falseBytes = []byte("false") @@ -285,18 +272,41 @@ func (f *fmt) fmt_s(s string) { f.padString(s) } -// fmt_sx formats a string as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_sx(s, digits string) { +// fmt_sbx formats a string or byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_sbx(s string, b []byte, digits string) { + n := len(b) + if b == nil { + n = len(s) + } + x := digits[10] - 'a' + 'x' // TODO: Avoid buffer by pre-padding. - var b []byte - for i := 0; i < len(s); i++ { + var buf []byte + for i := 0; i < n; i++ { if i > 0 && f.space { - b = append(b, ' ') + buf = append(buf, ' ') } - v := s[i] - b = append(b, digits[v>>4], digits[v&0xF]) + if f.sharp { + buf = append(buf, '0', x) + } + var c byte + if b == nil { + c = s[i] + } else { + c = b[i] + } + buf = append(buf, digits[c>>4], digits[c&0xF]) } - f.pad(b) + f.pad(buf) +} + +// fmt_sx formats a string as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_sx(s, digits string) { + f.fmt_sbx(s, nil, digits) +} + +// fmt_bx formats a byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_bx(b []byte, digits string) { + f.fmt_sbx("", b, digits) } // fmt_q formats a string as a double-quoted, escaped Go string constant. @@ -373,7 +383,7 @@ func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) } // fmt_g64 formats a float64 in the 'f' or 'e' form according to size. func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) } -// 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) { f.formatFloat(v, 'G', doPrec(f, -1), 64) } // fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2). @@ -405,6 +415,7 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) } 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 'e': @@ -424,6 +435,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) { f.plus = true r = imag(v) } + f.plus = oldPlus f.buf.Write(irparenBytes) } @@ -431,6 +443,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) { func (f *fmt) fmt_c128(v complex128, verb rune) { f.buf.WriteByte('(') r := real(v) + oldPlus := f.plus for i := 0; ; i++ { switch verb { case 'e': @@ -450,5 +463,6 @@ func (f *fmt) fmt_c128(v complex128, verb rune) { f.plus = true r = imag(v) } + f.plus = oldPlus f.buf.Write(irparenBytes) } diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 13438243c..7d7aa93b2 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -26,8 +26,8 @@ var ( extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") bytesBytes = []byte("[]byte{") - widthBytes = []byte("%!(BADWIDTH)") - precBytes = []byte("%!(BADPREC)") + badWidthBytes = []byte("%!(BADWIDTH)") + badPrecBytes = []byte("%!(BADPREC)") noVerbBytes = []byte("%!(NOVERB)") ) @@ -153,7 +153,7 @@ func newCache(f func() interface{}) *cache { var ppFree = newCache(func() interface{} { return new(pp) }) -// Allocate a new pp struct or grab a cached one. +// newPrinter allocates a new pp struct or grab a cached one. func newPrinter() *pp { p := ppFree.get().(*pp) p.panicking = false @@ -162,7 +162,7 @@ func newPrinter() *pp { return p } -// Save used pp structs in ppFree; avoids an allocation per invocation. +// free saves used pp structs in ppFree; avoids an allocation per invocation. func (p *pp) free() { // Don't hold on to pp structs with large buffers. if cap(p.buf) > 1024 { @@ -231,7 +231,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // as a value that satisfies error. func Errorf(format string, a ...interface{}) error { return errors.New(Sprintf(format, a...)) @@ -299,7 +299,7 @@ func Sprintln(a ...interface{}) string { return s } -// Get the i'th arg of the struct value. +// getField gets the i'th arg of the struct value. // If the arg itself is an interface, return a value for // the thing inside the interface, not the interface itself. func getField(v reflect.Value, i int) reflect.Value { @@ -310,7 +310,7 @@ func getField(v reflect.Value, i int) reflect.Value { return val } -// Convert ASCII to integer. n is 0 (and got is false) if no number present. +// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. func parsenum(s string, start, end int) (num int, isnum bool, newi int) { if start >= end { return 0, false, end @@ -545,10 +545,15 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) { } } -func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) { +func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, depth int) { if verb == 'v' || verb == 'd' { if goSyntax { - p.buf.Write(bytesBytes) + if typ == nil { + p.buf.Write(bytesBytes) + } else { + p.buf.WriteString(typ.String()) + p.buf.WriteByte('{') + } } else { p.buf.WriteByte('[') } @@ -569,24 +574,27 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) { } return } - s := string(v) switch verb { case 's': - p.fmt.fmt_s(s) + p.fmt.fmt_s(string(v)) case 'x': - p.fmt.fmt_sx(s, ldigits) + p.fmt.fmt_bx(v, ldigits) case 'X': - p.fmt.fmt_sx(s, udigits) + p.fmt.fmt_bx(v, udigits) case 'q': - p.fmt.fmt_q(s) + p.fmt.fmt_q(string(v)) default: p.badVerb(verb) } } func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) { + use0x64 := true switch verb { - case 'p', 'v', 'b', 'd', 'o', 'x', 'X': + case 'p', 'v': + // ok + case 'b', 'd', 'o', 'x', 'X': + use0x64 = false // ok default: p.badVerb(verb) @@ -616,7 +624,11 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) { } else if verb == 'v' && u == 0 { p.buf.Write(nilAngleBytes) } else { - p.fmt0x64(uint64(u), !p.fmt.sharp) + if use0x64 { + p.fmt0x64(uint64(u), !p.fmt.sharp) + } else { + p.fmtUint64(uint64(u), verb, false) + } } } @@ -712,17 +724,18 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString } func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) { + p.field = field + p.value = reflect.Value{} + if field == nil { if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) + p.fmt.pad(nilAngleBytes) } else { p.badVerb(verb) } return false } - p.field = field - p.value = reflect.Value{} // Special processing considerations. // %T (the value's type) and %p (its address) are special; we always do them first. switch verb { @@ -734,8 +747,17 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth return false } - if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { - return wasString + // Clear flags for base formatters. + // handleMethods needs them, so we must restore them later. + // We could call handleMethods here and avoid this work, but + // handleMethods is expensive enough to be worth delaying. + oldPlus := p.fmt.plus + oldSharp := p.fmt.sharp + if plus { + p.fmt.plus = false + } + if goSyntax { + p.fmt.sharp = false } // Some types can be done without reflection. @@ -776,9 +798,16 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth p.fmtString(f, verb, goSyntax) wasString = verb == 's' || verb == 'v' case []byte: - p.fmtBytes(f, verb, goSyntax, depth) + p.fmtBytes(f, verb, goSyntax, nil, depth) wasString = verb == 's' default: + // Restore flags in case handleMethods finds a Formatter. + p.fmt.plus = oldPlus + p.fmt.sharp = oldSharp + // If the type is not simple, it might have methods. + if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { + return wasString + } // Need to use reflection return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth) } @@ -915,19 +944,22 @@ BigSwitch: } case reflect.Array, reflect.Slice: // Byte slices are special. - if f.Type().Elem().Kind() == reflect.Uint8 { - // We know it's a slice of bytes, but we also know it does not have static type - // []byte, or it would have been caught above. Therefore we cannot convert - // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have - // that type, and we can't write an expression of the right type and do a - // conversion because we don't have a static way to write the right type. - // So we build a slice by hand. This is a rare case but it would be nice - // if reflection could help a little more. - bytes := make([]byte, f.Len()) - for i := range bytes { - bytes[i] = byte(f.Index(i).Uint()) + if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 { + var bytes []byte + if f.Kind() == reflect.Slice { + bytes = f.Bytes() + } else if f.CanAddr() { + bytes = f.Slice(0, f.Len()).Bytes() + } else { + // We have an array, but we cannot Slice() a non-addressable array, + // so we build a slice by hand. This is a rare case but it would be nice + // if reflection could help a little more. + bytes = make([]byte, f.Len()) + for i := range bytes { + bytes[i] = byte(f.Index(i).Uint()) + } } - p.fmtBytes(bytes, verb, goSyntax, depth) + p.fmtBytes(bytes, verb, goSyntax, typ, depth) wasString = verb == 's' break } @@ -1033,7 +1065,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { if i < end && format[i] == '*' { p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) if !p.fmt.widPresent { - p.buf.Write(widthBytes) + p.buf.Write(badWidthBytes) } } else { p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) @@ -1043,7 +1075,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { if format[i+1] == '*' { p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) if !p.fmt.precPresent { - p.buf.Write(precBytes) + p.buf.Write(badPrecBytes) } } else { p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) @@ -1099,7 +1131,7 @@ func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { prevString := false for fieldnum := 0; fieldnum < len(a); fieldnum++ { p.fmt.clearflags() - // always add spaces if we're doing println + // always add spaces if we're doing Println field := a[fieldnum] if fieldnum > 0 { isString := field != nil && reflect.TypeOf(field).Kind() == reflect.String diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index 0b3e04069..bf888c4d8 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -33,8 +33,8 @@ type ScanState interface { ReadRune() (r rune, size int, err error) // UnreadRune causes the next call to ReadRune to return the same rune. UnreadRune() error - // SkipSpace skips space in the input. Newlines are treated as space - // unless the scan operation is Scanln, Fscanln or Sscanln, in which case + // SkipSpace skips space in the input. Newlines are treated as space + // unless the scan operation is Scanln, Fscanln or Sscanln, in which case // a newline is treated as EOF. SkipSpace() // Token skips space in the input if skipSpace is true, then returns the @@ -312,8 +312,9 @@ func notSpace(r rune) bool { return !isSpace(r) } -// skipSpace provides Scan() methods the ability to skip space and newline characters -// in keeping with the current scanning mode set by format strings and Scan()/Scanln(). +// SkipSpace provides Scan methods the ability to skip space and newline +// characters in keeping with the current scanning mode set by format strings +// and Scan/Scanln. func (s *ss) SkipSpace() { s.skipSpace(false) } @@ -337,7 +338,10 @@ func (r *readRune) readByte() (b byte, err error) { r.pending-- return } - _, err = r.reader.Read(r.pendBuf[0:1]) + n, err := io.ReadFull(r.reader, r.pendBuf[0:1]) + if n != 1 { + return 0, err + } return r.pendBuf[0], err } @@ -378,7 +382,7 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) { var ssFree = newCache(func() interface{} { return new(ss) }) -// Allocate a new ss struct or grab a cached one. +// newScanState allocates a new ss struct or grab a cached one. func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { // If the reader is a *ss, then we've got a recursive // call to Scan, so re-use the scan state. @@ -410,7 +414,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { return } -// Save used ss structs in ssFree; avoid an allocation per invocation. +// free saves used ss structs in ssFree; avoid an allocation per invocation. func (s *ss) free(old ssave) { // If it was used recursively, just restore the old state. if old.validSave { @@ -1090,7 +1094,8 @@ func (s *ss) advance(format string) (i int) { // There was space in the format, so there should be space (EOF) // in the input. inputc := s.getRune() - if inputc == eof { + if inputc == eof || inputc == '\n' { + // If we've reached a newline, stop now; don't read ahead. return } if !isSpace(inputc) { diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 320857b73..4e2c0feb2 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -626,7 +626,7 @@ func TestScanlnWithMiddleNewline(t *testing.T) { } } -// Special Reader that counts reads at end of file. +// eofCounter is a special Reader that counts reads at end of file. type eofCounter struct { reader *strings.Reader eofCount int @@ -640,8 +640,8 @@ func (ec *eofCounter) Read(b []byte) (n int, err error) { return } -// Verify that when we scan, we see at most EOF once per call to a Scan function, -// and then only when it's really an EOF +// TestEOF verifies that when we scan, we see at most EOF once per call to a +// Scan function, and then only when it's really an EOF. func TestEOF(t *testing.T) { ec := &eofCounter{strings.NewReader("123\n"), 0} var a int @@ -668,7 +668,7 @@ func TestEOF(t *testing.T) { } } -// Verify that we see an EOF error if we run out of input. +// TestEOFAtEndOfInput verifies that we see an EOF error if we run out of input. // This was a buglet: we used to get "expected integer". func TestEOFAtEndOfInput(t *testing.T) { var i, j int @@ -730,7 +730,8 @@ func TestEOFAllTypes(t *testing.T) { } } -// Verify that, at least when using bufio, successive calls to Fscan do not lose runes. +// TestUnreadRuneWithBufio verifies that, at least when using bufio, successive +// calls to Fscan do not lose runes. func TestUnreadRuneWithBufio(t *testing.T) { r := bufio.NewReader(strings.NewReader("123αb")) var i int @@ -753,7 +754,7 @@ func TestUnreadRuneWithBufio(t *testing.T) { type TwoLines string -// Attempt to read two lines into the object. Scanln should prevent this +// Scan attempts to read two lines into the object. Scanln should prevent this // because it stops at newline; Scan and Scanf should be fine. func (t *TwoLines) Scan(state ScanState, verb rune) error { chars := make([]rune, 0, 100) @@ -810,6 +811,34 @@ func TestMultiLine(t *testing.T) { } } +// simpleReader is a strings.Reader that implements only Read, not ReadRune. +// Good for testing readahead. +type simpleReader struct { + sr *strings.Reader +} + +func (s *simpleReader) Read(b []byte) (n int, err error) { + return s.sr.Read(b) +} + +// TestLineByLineFscanf tests that Fscanf does not read past newline. Issue +// 3481. +func TestLineByLineFscanf(t *testing.T) { + r := &simpleReader{strings.NewReader("1\n2\n")} + var i, j int + n, err := Fscanf(r, "%v\n", &i) + if n != 1 || err != nil { + t.Fatalf("first read: %d %q", n, err) + } + n, err = Fscanf(r, "%v\n", &j) + if n != 1 || err != nil { + t.Fatalf("second read: %d %q", n, err) + } + if i != 1 || j != 2 { + t.Errorf("wrong values; wanted 1 2 got %d %d", i, j) + } +} + // RecursiveInt accepts a string matching %d.%d.%d.... // and parses it into a linked list. // It allows us to benchmark recursive descent style scanners. @@ -835,7 +864,7 @@ func (r *RecursiveInt) Scan(state ScanState, verb rune) (err error) { return } -// Perform the same scanning task as RecursiveInt.Scan +// scanInts performs the same scanning task as RecursiveInt.Scan // but without recurring through scanner, so we can compare // performance more directly. func scanInts(r *RecursiveInt, b *bytes.Buffer) (err error) { |