summaryrefslogtreecommitdiff
path: root/libgo/go/fmt
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/fmt')
-rw-r--r--libgo/go/fmt/example_test.go362
-rw-r--r--libgo/go/fmt/fmt_test.go69
-rw-r--r--libgo/go/fmt/format.go33
-rw-r--r--libgo/go/fmt/gostringer_example_test.go61
-rw-r--r--libgo/go/fmt/print.go31
-rw-r--r--libgo/go/fmt/stringer_example_test.go31
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)
+}