diff options
Diffstat (limited to 'libgo/go/fmt')
-rw-r--r-- | libgo/go/fmt/example_test.go | 362 | ||||
-rw-r--r-- | libgo/go/fmt/fmt_test.go | 69 | ||||
-rw-r--r-- | libgo/go/fmt/format.go | 33 | ||||
-rw-r--r-- | libgo/go/fmt/gostringer_example_test.go | 61 | ||||
-rw-r--r-- | libgo/go/fmt/print.go | 31 | ||||
-rw-r--r-- | libgo/go/fmt/stringer_example_test.go | 31 |
6 files changed, 535 insertions, 52 deletions
diff --git a/libgo/go/fmt/example_test.go b/libgo/go/fmt/example_test.go index aa3cd05e3ef..64821e0dacc 100644 --- a/libgo/go/fmt/example_test.go +++ b/libgo/go/fmt/example_test.go @@ -8,24 +8,360 @@ package fmt_test import ( "fmt" + "io" + "math" + "os" + "strings" + "time" ) -// Animal has a Name and an Age to represent an animal. -type Animal struct { - Name string - Age uint +// The Errorf function lets us use formatting features +// to create descriptive error messages. +func ExampleErrorf() { + const name, id = "bueller", 17 + err := fmt.Errorf("user %q (id %d) not found", name, id) + fmt.Println(err.Error()) + + // Output: user "bueller" (id 17) not found +} + +func ExampleFscanf() { + var ( + i int + b bool + s string + ) + r := strings.NewReader("5 true gophers") + n, err := fmt.Fscanf(r, "%d %t %s", &i, &b, &s) + if err != nil { + fmt.Fprintf(os.Stderr, "Fscanf: %v\n", err) + } + fmt.Println(i, b, s) + fmt.Println(n) + // Output: + // 5 true gophers + // 3 +} + +func ExampleFscanln() { + s := `dmr 1771 1.61803398875 + ken 271828 3.14159` + r := strings.NewReader(s) + var a string + var b int + var c float64 + for { + n, err := fmt.Fscanln(r, &a, &b, &c) + if err == io.EOF { + break + } + if err != nil { + panic(err) + } + fmt.Printf("%d: %s, %d, %f\n", n, a, b, c) + } + // Output: + // 3: dmr, 1771, 1.618034 + // 3: ken, 271828, 3.141590 +} + +func ExampleSscanf() { + var name string + var age int + n, err := fmt.Sscanf("Kim is 22 years old", "%s is %d years old", &name, &age) + if err != nil { + panic(err) + } + fmt.Printf("%d: %s, %d\n", n, name, age) + + // Output: + // 2: Kim, 22 +} + +func ExamplePrint() { + const name, age = "Kim", 22 + fmt.Print(name, " is ", age, " years old.\n") + + // It is conventional not to worry about any + // error returned by Print. + + // Output: + // Kim is 22 years old. +} + +func ExamplePrintln() { + const name, age = "Kim", 22 + fmt.Println(name, "is", age, "years old.") + + // It is conventional not to worry about any + // error returned by Println. + + // Output: + // Kim is 22 years old. +} + +func ExamplePrintf() { + const name, age = "Kim", 22 + fmt.Printf("%s is %d years old.\n", name, age) + + // It is conventional not to worry about any + // error returned by Printf. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprint() { + const name, age = "Kim", 22 + s := fmt.Sprint(name, " is ", age, " years old.\n") + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprintln() { + const name, age = "Kim", 22 + s := fmt.Sprintln(name, "is", age, "years old.") + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprintf() { + const name, age = "Kim", 22 + s := fmt.Sprintf("%s is %d years old.\n", name, age) + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. } -// String makes Animal satisfy the Stringer interface. -func (a Animal) String() string { - return fmt.Sprintf("%v (%d)", a.Name, a.Age) +func ExampleFprint() { + const name, age = "Kim", 22 + n, err := fmt.Fprint(os.Stdout, name, " is ", age, " years old.\n") + + // The n and err return values from Fprint are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprint: %v\n", err) + } + fmt.Print(n, " bytes written.\n") + + // Output: + // Kim is 22 years old. + // 21 bytes written. } -func ExampleStringer() { - a := Animal{ - Name: "Gopher", - Age: 2, +func ExampleFprintln() { + const name, age = "Kim", 22 + n, err := fmt.Fprintln(os.Stdout, name, "is", age, "years old.") + + // The n and err return values from Fprintln are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprintln: %v\n", err) + } + fmt.Println(n, "bytes written.") + + // Output: + // Kim is 22 years old. + // 21 bytes written. +} + +func ExampleFprintf() { + const name, age = "Kim", 22 + n, err := fmt.Fprintf(os.Stdout, "%s is %d years old.\n", name, age) + + // The n and err return values from Fprintf are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprintf: %v\n", err) } - fmt.Println(a) - // Output: Gopher (2) + fmt.Printf("%d bytes written.\n", n) + + // Output: + // Kim is 22 years old. + // 21 bytes written. +} + +// Print, Println, and Printf lay out their arguments differently. In this example +// we can compare their behaviors. Println always adds blanks between the items it +// prints, while Print adds blanks only between non-string arguments and Printf +// does exactly what it is told. +// Sprint, Sprintln, Sprintf, Fprint, Fprintln, and Fprintf behave the same as +// their corresponding Print, Println, and Printf functions shown here. +func Example_printers() { + a, b := 3.0, 4.0 + h := math.Hypot(a, b) + + // Print inserts blanks between arguments when neither is a string. + // It does not add a newline to the output, so we add one explicitly. + fmt.Print("The vector (", a, b, ") has length ", h, ".\n") + + // Println always inserts spaces between its arguments, + // so it cannot be used to produce the same output as Print in this case; + // its output has extra spaces. + // Also, Println always adds a newline to the output. + fmt.Println("The vector (", a, b, ") has length", h, ".") + + // Printf provides complete control but is more complex to use. + // It does not add a newline to the output, so we add one explicitly + // at the end of the format specifier string. + fmt.Printf("The vector (%g %g) has length %g.\n", a, b, h) + + // Output: + // The vector (3 4) has length 5. + // The vector ( 3 4 ) has length 5 . + // The vector (3 4) has length 5. +} + +// These examples demonstrate the basics of printing using a format string. Printf, +// Sprintf, and Fprintf all take a format string that specifies how to format the +// subsequent arguments. For example, %d (we call that a 'verb') says to print the +// corresponding argument, which must be an integer (or something containing an +// integer, such as a slice of ints) in decimal. The verb %v ('v' for 'value') +// always formats the argument in its default form, just how Print or Println would +// show it. The special verb %T ('T' for 'Type') prints the type of the argument +// rather than its value. The examples are not exhaustive; see the package comment +// for all the details. +func Example_formats() { + // A basic set of examples showing that %v is the default format, in this + // case decimal for integers, which can be explicitly requested with %d; + // the output is just what Println generates. + integer := 23 + // Each of these prints "23" (without the quotes). + fmt.Println(integer) + fmt.Printf("%v\n", integer) + fmt.Printf("%d\n", integer) + + // The special verb %T shows the type of an item rather than its value. + fmt.Printf("%T %T\n", integer, &integer) + // Result: int *int + + // Println(x) is the same as Printf("%v\n", x) so we will use only Printf + // in the following examples. Each one demonstrates how to format values of + // a particular type, such as integers or strings. We start each format + // string with %v to show the default output and follow that with one or + // more custom formats. + + // Booleans print as "true" or "false" with %v or %t. + truth := true + fmt.Printf("%v %t\n", truth, truth) + // Result: true true + + // Integers print as decimals with %v and %d, + // or in hex with %x, octal with %o, or binary with %b. + answer := 42 + fmt.Printf("%v %d %x %o %b\n", answer, answer, answer, answer, answer) + // Result: 42 42 2a 52 101010 + + // Floats have multiple formats: %v and %g print a compact representation, + // while %f prints a decimal point and %e uses exponential notation. The + // format %6.2f used here shows how to set the width and precision to + // control the appearance of a floating-point value. In this instance, 6 is + // the total width of the printed text for the value (note the extra spaces + // in the output) and 2 is the number of decimal places to show. + pi := math.Pi + fmt.Printf("%v %g %.2f (%6.2f) %e\n", pi, pi, pi, pi, pi) + // Result: 3.141592653589793 3.141592653589793 3.14 ( 3.14) 3.141593e+00 + + // Complex numbers format as parenthesized pairs of floats, with an 'i' + // after the imaginary part. + point := 110.7 + 22.5i + fmt.Printf("%v %g %.2f %.2e\n", point, point, point, point) + // Result: (110.7+22.5i) (110.7+22.5i) (110.70+22.50i) (1.11e+02+2.25e+01i) + + // Runes are integers but when printed with %c show the character with that + // Unicode value. The %q verb shows them as quoted characters, %U as a + // hex Unicode code point, and %#U as both a code point and a quoted + // printable form if the rune is printable. + smile := '😀' + fmt.Printf("%v %d %c %q %U %#U\n", smile, smile, smile, smile, smile, smile) + // Result: 128512 128512 😀 '😀' U+1F600 U+1F600 '😀' + + // Strings are formatted with %v and %s as-is, with %q as quoted strings, + // and %#q as backquoted strings. + placeholders := `foo "bar"` + fmt.Printf("%v %s %q %#q\n", placeholders, placeholders, placeholders, placeholders) + // Result: foo "bar" foo "bar" "foo \"bar\"" `foo "bar"` + + // Maps formatted with %v show keys and values in their default formats. + // The %#v form (the # is called a "flag" in this context) shows the map in + // the Go source format. Maps are printed in a consistent order, sorted + // by the values of the keys. + isLegume := map[string]bool{ + "peanut": true, + "dachshund": false, + } + fmt.Printf("%v %#v\n", isLegume, isLegume) + // Result: map[dachshund:false peanut:true] map[string]bool{"dachshund":false, "peanut":true} + + // Structs formatted with %v show field values in their default formats. + // The %+v form shows the fields by name, while %#v formats the struct in + // Go source format. + person := struct { + Name string + Age int + }{"Kim", 22} + fmt.Printf("%v %+v %#v\n", person, person, person) + // Result: {Kim 22} {Name:Kim Age:22} struct { Name string; Age int }{Name:"Kim", Age:22} + + // The default format for a pointer shows the underlying value preceded by + // an ampersand. The %p verb prints the pointer value in hex. We use a + // typed nil for the argument to %p here because the value of any non-nil + // pointer would change from run to run; run the commented-out Printf + // call yourself to see. + pointer := &person + fmt.Printf("%v %p\n", pointer, (*int)(nil)) + // Result: &{Kim 22} 0x0 + // fmt.Printf("%v %p\n", pointer, pointer) + // Result: &{Kim 22} 0x010203 // See comment above. + + // Arrays and slices are formatted by applying the format to each element. + greats := [5]string{"Katano", "Kobayashi", "Kurosawa", "Miyazaki", "Ozu"} + fmt.Printf("%v %q\n", greats, greats) + // Result: [Katano Kobayashi Kurosawa Miyazaki Ozu] ["Katano" "Kobayashi" "Kurosawa" "Miyazaki" "Ozu"] + + kGreats := greats[:3] + fmt.Printf("%v %q %#v\n", kGreats, kGreats, kGreats) + // Result: [Katano Kobayashi Kurosawa] ["Katano" "Kobayashi" "Kurosawa"] []string{"Katano", "Kobayashi", "Kurosawa"} + + // Byte slices are special. Integer verbs like %d print the elements in + // that format. The %s and %q forms treat the slice like a string. The %x + // verb has a special form with the space flag that puts a space between + // the bytes. + cmd := []byte("a⌘") + fmt.Printf("%v %d %s %q %x % x\n", cmd, cmd, cmd, cmd, cmd, cmd) + // Result: [97 226 140 152] [97 226 140 152] a⌘ "a⌘" 61e28c98 61 e2 8c 98 + + // Types that implement Stringer are printed the same as strings. Because + // Stringers return a string, we can print them using a string-specific + // verb such as %q. + now := time.Unix(123456789, 0).UTC() // time.Time implements fmt.Stringer. + fmt.Printf("%v %q\n", now, now) + // Result: 1973-11-29 21:33:09 +0000 UTC "1973-11-29 21:33:09 +0000 UTC" + + // Output: + // 23 + // 23 + // 23 + // int *int + // true true + // 42 42 2a 52 101010 + // 3.141592653589793 3.141592653589793 3.14 ( 3.14) 3.141593e+00 + // (110.7+22.5i) (110.7+22.5i) (110.70+22.50i) (1.11e+02+2.25e+01i) + // 128512 128512 😀 '😀' U+1F600 U+1F600 '😀' + // foo "bar" foo "bar" "foo \"bar\"" `foo "bar"` + // map[dachshund:false peanut:true] map[string]bool{"dachshund":false, "peanut":true} + // {Kim 22} {Name:Kim Age:22} struct { Name string; Age int }{Name:"Kim", Age:22} + // &{Kim 22} 0x0 + // [Katano Kobayashi Kurosawa Miyazaki Ozu] ["Katano" "Kobayashi" "Kurosawa" "Miyazaki" "Ozu"] + // [Katano Kobayashi Kurosawa] ["Katano" "Kobayashi" "Kurosawa"] []string{"Katano", "Kobayashi", "Kurosawa"} + // [97 226 140 152] [97 226 140 152] a⌘ "a⌘" 61e28c98 61 e2 8c 98 + // 1973-11-29 21:33:09 +0000 UTC "1973-11-29 21:33:09 +0000 UTC" } diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index b56b84fd9c0..bc184c307ea 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -131,15 +131,10 @@ func (byteFormatter) Format(f State, _ rune) { var byteFormatterSlice = []byteFormatter{'h', 'e', 'l', 'l', 'o'} -// Copy of io.stringWriter interface used by writeStringFormatter for type assertion. -type stringWriter interface { - WriteString(s string) (n int, err error) -} - type writeStringFormatter string func (sf writeStringFormatter) Format(f State, c rune) { - if sw, ok := f.(stringWriter); ok { + if sw, ok := f.(io.StringWriter); ok { sw.WriteString("***" + string(sf) + "***") } } @@ -303,20 +298,30 @@ var fmtTests = []struct { // width {"%5s", "abc", " abc"}, + {"%5s", []byte("abc"), " abc"}, {"%2s", "\u263a", " ☺"}, + {"%2s", []byte("\u263a"), " ☺"}, {"%-5s", "abc", "abc "}, - {"%-8q", "abc", `"abc" `}, + {"%-5s", []byte("abc"), "abc "}, {"%05s", "abc", "00abc"}, - {"%08q", "abc", `000"abc"`}, + {"%05s", []byte("abc"), "00abc"}, {"%5s", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}, + {"%5s", []byte("abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz"}, {"%.5s", "abcdefghijklmnopqrstuvwxyz", "abcde"}, + {"%.5s", []byte("abcdefghijklmnopqrstuvwxyz"), "abcde"}, {"%.0s", "日本語日本語", ""}, + {"%.0s", []byte("日本語日本語"), ""}, {"%.5s", "日本語日本語", "日本語日本"}, - {"%.10s", "日本語日本語", "日本語日本語"}, {"%.5s", []byte("日本語日本語"), "日本語日本"}, + {"%.10s", "日本語日本語", "日本語日本語"}, + {"%.10s", []byte("日本語日本語"), "日本語日本語"}, + {"%08q", "abc", `000"abc"`}, + {"%08q", []byte("abc"), `000"abc"`}, + {"%-8q", "abc", `"abc" `}, + {"%-8q", []byte("abc"), `"abc" `}, {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, - {"%.5x", "abcdefghijklmnopqrstuvwxyz", "6162636465"}, {"%.5q", []byte("abcdefghijklmnopqrstuvwxyz"), `"abcde"`}, + {"%.5x", "abcdefghijklmnopqrstuvwxyz", "6162636465"}, {"%.5x", []byte("abcdefghijklmnopqrstuvwxyz"), "6162636465"}, {"%.3q", "日本語日本語", `"日本語"`}, {"%.3q", []byte("日本語日本語"), `"日本語"`}, @@ -325,6 +330,7 @@ var fmtTests = []struct { {"%.1x", "日本語", "e6"}, {"%.1X", []byte("日本語"), "E6"}, {"%10.1q", "日本語日本語", ` "日"`}, + {"%10.1q", []byte("日本語日本語"), ` "日"`}, {"%10v", nil, " <nil>"}, {"%-10v", nil, "<nil> "}, @@ -690,6 +696,13 @@ var fmtTests = []struct { {"%#v", []int32(nil), "[]int32(nil)"}, {"%#v", 1.2345678, "1.2345678"}, {"%#v", float32(1.2345678), "1.2345678"}, + + // Whole number floats are printed without decimals. See Issue 27634. + {"%#v", 1.0, "1"}, + {"%#v", 1000000.0, "1e+06"}, + {"%#v", float32(1.0), "1"}, + {"%#v", float32(1000000.0), "1e+06"}, + // Only print []byte and []uint8 as type []byte if they appear at the top level. {"%#v", []byte(nil), "[]byte(nil)"}, {"%#v", []uint8(nil), "[]byte(nil)"}, @@ -861,13 +874,8 @@ var fmtTests = []struct { // Extra argument errors should format without flags set. {"%010.2", "12345", "%!(NOVERB)%!(EXTRA string=12345)"}, - // The "<nil>" show up because maps are printed by - // first obtaining a list of keys and then looking up - // each key. Since NaNs can be map keys but cannot - // be fetched directly, the lookup fails and returns a - // zero reflect.Value, which formats as <nil>. - // This test is just to check that it shows the two NaNs at all. - {"%v", map[float64]int{NaN: 1, NaN: 2}, "map[NaN:<nil> NaN:<nil>]"}, + // Test that maps with non-reflexive keys print all keys and values. + {"%v", map[float64]int{NaN: 1, NaN: 1}, "map[NaN:1 NaN:1]"}, // Comparison of padding rules with C printf. /* @@ -1033,7 +1041,7 @@ var fmtTests = []struct { {"%☠", &[]interface{}{I(1), G(2)}, "&[%!☠(fmt_test.I=1) %!☠(fmt_test.G=2)]"}, {"%☠", SI{&[]interface{}{I(1), G(2)}}, "{%!☠(*[]interface {}=&[1 2])}"}, {"%☠", reflect.Value{}, "<invalid reflect.Value>"}, - {"%☠", map[float64]int{NaN: 1}, "map[%!☠(float64=NaN):%!☠(<nil>)]"}, + {"%☠", map[float64]int{NaN: 1}, "map[%!☠(float64=NaN):%!☠(int=1)]"}, } // zeroFill generates zero-filled strings of the specified width. The length @@ -1214,7 +1222,16 @@ func BenchmarkSprintfString(b *testing.B) { func BenchmarkSprintfTruncateString(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - Sprintf("%.3s", "日本語日本語日本語") + Sprintf("%.3s", "日本語日本語日本語日本語") + } + }) +} + +func BenchmarkSprintfTruncateBytes(b *testing.B) { + var bytes interface{} = []byte("日本語日本語日本語日本語") + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + Sprintf("%.3s", bytes) } }) } @@ -1668,18 +1685,18 @@ var panictests = []struct { }{ // String {"%s", (*PanicS)(nil), "<nil>"}, // nil pointer special case - {"%s", PanicS{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"}, - {"%s", PanicS{3}, "%!s(PANIC=3)"}, + {"%s", PanicS{io.ErrUnexpectedEOF}, "%!s(PANIC=String method: unexpected EOF)"}, + {"%s", PanicS{3}, "%!s(PANIC=String method: 3)"}, // GoString {"%#v", (*PanicGo)(nil), "<nil>"}, // nil pointer special case - {"%#v", PanicGo{io.ErrUnexpectedEOF}, "%!v(PANIC=unexpected EOF)"}, - {"%#v", PanicGo{3}, "%!v(PANIC=3)"}, + {"%#v", PanicGo{io.ErrUnexpectedEOF}, "%!v(PANIC=GoString method: unexpected EOF)"}, + {"%#v", PanicGo{3}, "%!v(PANIC=GoString method: 3)"}, // Issue 18282. catchPanic should not clear fmtFlags permanently. - {"%#v", []interface{}{PanicGo{3}, PanicGo{3}}, "[]interface {}{%!v(PANIC=3), %!v(PANIC=3)}"}, + {"%#v", []interface{}{PanicGo{3}, PanicGo{3}}, "[]interface {}{%!v(PANIC=GoString method: 3), %!v(PANIC=GoString method: 3)}"}, // Format {"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case - {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"}, - {"%s", PanicF{3}, "%!s(PANIC=3)"}, + {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=Format method: unexpected EOF)"}, + {"%s", PanicF{3}, "%!s(PANIC=Format method: 3)"}, } func TestPanics(t *testing.T) { diff --git a/libgo/go/fmt/format.go b/libgo/go/fmt/format.go index 91103f2c07f..d6da8aed1e3 100644 --- a/libgo/go/fmt/format.go +++ b/libgo/go/fmt/format.go @@ -308,8 +308,8 @@ func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, digits string) { f.zero = oldZero } -// truncate truncates the string to the specified precision, if present. -func (f *fmt) truncate(s string) string { +// truncate truncates the string s to the specified precision, if present. +func (f *fmt) truncateString(s string) string { if f.precPresent { n := f.prec for i := range s { @@ -322,12 +322,37 @@ func (f *fmt) truncate(s string) string { return s } +// truncate truncates the byte slice b as a string of the specified precision, if present. +func (f *fmt) truncate(b []byte) []byte { + if f.precPresent { + n := f.prec + for i := 0; i < len(b); { + n-- + if n < 0 { + return b[:i] + } + wid := 1 + if b[i] >= utf8.RuneSelf { + _, wid = utf8.DecodeRune(b[i:]) + } + i += wid + } + } + return b +} + // fmtS formats a string. func (f *fmt) fmtS(s string) { - s = f.truncate(s) + s = f.truncateString(s) f.padString(s) } +// fmtBs formats the byte slice b as if it was formatted as string with fmtS. +func (f *fmt) fmtBs(b []byte) { + b = f.truncate(b) + f.pad(b) +} + // fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. func (f *fmt) fmtSbx(s string, b []byte, digits string) { length := len(b) @@ -408,7 +433,7 @@ func (f *fmt) fmtBx(b []byte, digits string) { // If f.sharp is set a raw (backquoted) string may be returned instead // if the string does not contain any control characters other than tab. func (f *fmt) fmtQ(s string) { - s = f.truncate(s) + s = f.truncateString(s) if f.sharp && strconv.CanBackquote(s) { f.padString("`" + s + "`") return diff --git a/libgo/go/fmt/gostringer_example_test.go b/libgo/go/fmt/gostringer_example_test.go new file mode 100644 index 00000000000..7c6b9f05453 --- /dev/null +++ b/libgo/go/fmt/gostringer_example_test.go @@ -0,0 +1,61 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package fmt_test + +import ( + "fmt" +) + +// Address has a City, State and a Country. +type Address struct { + City string + State string + Country string +} + +// Person has a Name, Age and Address. +type Person struct { + Name string + Age uint + Addr *Address +} + +// GoString makes Person satisfy the GoStringer interface. +// The return value is valid Go code that can be used to reproduce the Person struct. +func (p Person) GoString() string { + if p.Addr != nil { + return fmt.Sprintf("Person{Name: %q, Age: %d, Addr: &Address{City: %q, State: %q, Country: %q}}", p.Name, int(p.Age), p.Addr.City, p.Addr.State, p.Addr.Country) + } + return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, int(p.Age)) +} + +func ExampleGoStringer() { + p1 := Person{ + Name: "Warren", + Age: 31, + Addr: &Address{ + City: "Denver", + State: "CO", + Country: "U.S.A.", + }, + } + // If GoString() wasn't implemented, the output of `fmt.Printf("%#v", p1)` would be similar to + // Person{Name:"Warren", Age:0x1f, Addr:(*main.Address)(0x10448240)} + fmt.Printf("%#v\n", p1) + + p2 := Person{ + Name: "Theia", + Age: 4, + } + // If GoString() wasn't implemented, the output of `fmt.Printf("%#v", p2)` would be similar to + // Person{Name:"Theia", Age:0x4, Addr:(*main.Address)(nil)} + fmt.Printf("%#v\n", p2) + + // Output: + // Person{Name: "Warren", Age: 31, Addr: &Address{City: "Denver", State: "CO", Country: "U.S.A."}} + // Person{Name: "Theia", Age: 4} +} diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go index f67f8056037..42fcd8b979b 100644 --- a/libgo/go/fmt/print.go +++ b/libgo/go/fmt/print.go @@ -6,6 +6,7 @@ package fmt import ( "errors" + "internal/fmtsort" "io" "os" "reflect" @@ -139,6 +140,16 @@ func newPrinter() *pp { // free saves used pp structs in ppFree; avoids an allocation per invocation. func (p *pp) free() { + // Proper usage of a sync.Pool requires each entry to have approximately + // the same memory cost. To obtain this property when the stored type + // contains a variably-sized buffer, we add a hard limit on the maximum buffer + // to place back in the pool. + // + // See https://golang.org/issue/23199 + if cap(p.buf) > 64<<10 { + return + } + p.buf = p.buf[:0] p.arg = nil p.value = reflect.Value{} @@ -477,7 +488,7 @@ func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { p.buf.WriteByte(']') } case 's': - p.fmt.fmtS(string(v)) + p.fmt.fmtBs(v) case 'x': p.fmt.fmtBx(v, ldigits) case 'X': @@ -527,7 +538,7 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) { } } -func (p *pp) catchPanic(arg interface{}, verb rune) { +func (p *pp) catchPanic(arg interface{}, verb rune, method string) { if err := recover(); err != nil { // If it's a nil pointer, just say "<nil>". The likeliest causes are a // Stringer that fails to guard against nil or a nil pointer for a @@ -550,6 +561,8 @@ func (p *pp) catchPanic(arg interface{}, verb rune) { p.buf.WriteString(percentBangString) p.buf.WriteRune(verb) p.buf.WriteString(panicString) + p.buf.WriteString(method) + p.buf.WriteString(" method: ") p.panicking = true p.printArg(err, 'v') p.panicking = false @@ -566,7 +579,7 @@ func (p *pp) handleMethods(verb rune) (handled bool) { // Is it a Formatter? if formatter, ok := p.arg.(Formatter); ok { handled = true - defer p.catchPanic(p.arg, verb) + defer p.catchPanic(p.arg, verb, "Format") formatter.Format(p, verb) return } @@ -575,7 +588,7 @@ func (p *pp) handleMethods(verb rune) (handled bool) { if p.fmt.sharpV { if stringer, ok := p.arg.(GoStringer); ok { handled = true - defer p.catchPanic(p.arg, verb) + defer p.catchPanic(p.arg, verb, "GoString") // Print the result of GoString unadorned. p.fmt.fmtS(stringer.GoString()) return @@ -593,13 +606,13 @@ func (p *pp) handleMethods(verb rune) (handled bool) { switch v := p.arg.(type) { case error: handled = true - defer p.catchPanic(p.arg, verb) + defer p.catchPanic(p.arg, verb, "Error") p.fmtString(v.Error(), verb) return case Stringer: handled = true - defer p.catchPanic(p.arg, verb) + defer p.catchPanic(p.arg, verb, "String") p.fmtString(v.String(), verb) return } @@ -743,8 +756,8 @@ func (p *pp) printValue(value reflect.Value, verb rune, depth int) { } else { p.buf.WriteString(mapString) } - keys := f.MapKeys() - for i, key := range keys { + sorted := fmtsort.Sort(f) + for i, key := range sorted.Key { if i > 0 { if p.fmt.sharpV { p.buf.WriteString(commaSpaceString) @@ -754,7 +767,7 @@ func (p *pp) printValue(value reflect.Value, verb rune, depth int) { } p.printValue(key, verb, depth+1) p.buf.WriteByte(':') - p.printValue(f.MapIndex(key), verb, depth+1) + p.printValue(sorted.Value[i], verb, depth+1) } if p.fmt.sharpV { p.buf.WriteByte('}') diff --git a/libgo/go/fmt/stringer_example_test.go b/libgo/go/fmt/stringer_example_test.go new file mode 100644 index 00000000000..aa3cd05e3ef --- /dev/null +++ b/libgo/go/fmt/stringer_example_test.go @@ -0,0 +1,31 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package fmt_test + +import ( + "fmt" +) + +// Animal has a Name and an Age to represent an animal. +type Animal struct { + Name string + Age uint +} + +// String makes Animal satisfy the Stringer interface. +func (a Animal) String() string { + return fmt.Sprintf("%v (%d)", a.Name, a.Age) +} + +func ExampleStringer() { + a := Animal{ + Name: "Gopher", + Age: 2, + } + fmt.Println(a) + // Output: Gopher (2) +} |