message: use merged in fmt code
Differences in copied code:
- fmt renamed to formatInfo (avoid conflict with package)
- pp renamed to and merged with printer (existing type)
- printer supports Language: this gets rid of bind code!!!
- allow arguments with empty format string (required)
- added args as field to printer (allows incremental argument
counting for Render)
- use inlined bytes.Buffer
Expected modifications in future CLs and for which this merge
was necessary:
- use localized number formatting
- different handling of lists
- localized formatting of values within complex data types
- non-number argument references.
Change-Id: I497053e5a343ed0bc5694d68e7d04b3cefe9eae2
Edit
Reviewed-on: https://go-review.googlesource.com/44955
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/message/fmt_test.go b/message/fmt_test.go
old mode 100644
new mode 100755
index 7f2db5e..d02808a
--- a/message/fmt_test.go
+++ b/message/fmt_test.go
@@ -1,21 +1,21 @@
-// +build ignore
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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.
-package fmt_test
+package message
import (
"bytes"
- . "fmt"
- "internal/race"
+ "fmt"
+ "io"
"math"
"reflect"
"runtime"
"strings"
"testing"
"time"
- "unicode"
+
+ "golang.org/x/text/language"
)
type (
@@ -40,9 +40,10 @@
)
func TestFmtInterface(t *testing.T) {
+ p := NewPrinter(language.Und)
var i1 interface{}
i1 = "abc"
- s := Sprintf("%s", i1)
+ s := p.Sprintf("%s", i1)
if s != "abc" {
t.Errorf(`Sprintf("%%s", empty("abc")) = %q want %q`, s, "abc")
}
@@ -70,7 +71,10 @@
type I int
-func (i I) String() string { return Sprintf("<%d>", int(i)) }
+func (i I) String() string {
+ p := NewPrinter(language.Und)
+ return p.Sprintf("<%d>", int(i))
+}
type B struct {
I I
@@ -84,14 +88,16 @@
type F int
-func (f F) Format(s State, c rune) {
- Fprintf(s, "<%c=F(%d)>", c, int(f))
+func (f F) Format(s fmt.State, c rune) {
+ p := NewPrinter(language.Und)
+ p.Fprintf(s, "<%c=F(%d)>", c, int(f))
}
type G int
func (g G) GoString() string {
- return Sprintf("GoString(%d)", int(g))
+ p := NewPrinter(language.Und)
+ return p.Sprintf("GoString(%d)", int(g))
}
type S struct {
@@ -125,8 +131,9 @@
type byteFormatter byte
-func (byteFormatter) Format(f State, _ rune) {
- Fprint(f, "X")
+func (byteFormatter) Format(f fmt.State, _ rune) {
+ p := NewPrinter(language.Und)
+ p.Fprint(f, "X")
}
var byteFormatterSlice = []byteFormatter{'h', 'e', 'l', 'l', 'o'}
@@ -136,6 +143,22 @@
val interface{}
out string
}{
+ // The behavior of the following tests differs from that of the fmt package.
+
+ // Unlike with the fmt package, it is okay to have extra arguments for
+ // strings without format parameters. This is because it is impossible to
+ // distinguish between reordered or ordered format strings in this case.
+ // (For reordered format strings it is okay to not use arguments.)
+ {"", nil, ""},
+ {"", 2, ""},
+ {"no args", "hello", "no args"},
+
+ {"%017091901790959340919092959340919017929593813360", 0, "%!(NOVERB)"},
+ {"%184467440737095516170v", 0, "%!(NOVERB)"},
+ // Extra argument errors should format without flags set.
+ {"%010.2", "12345", "%!(NOVERB)"},
+
+ // All following tests are identical to that of the fmt package.
{"%d", 12345, "12345"},
{"%v", 12345, "12345"},
{"%t", true, "true"},
@@ -653,16 +676,16 @@
{"%s", reflect.ValueOf(I(23)), `<23>`},
// go syntax
- {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
+ {"%#v", A{1, 2, "a", []int{1, 2}}, `message.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
{"%#v", new(byte), "(*uint8)(0xPTR)"},
{"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"},
{"%#v", make(chan int), "(chan int)(0xPTR)"},
{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
{"%#v", 1000000000, "1000000000"},
{"%#v", map[string]int{"a": 1}, `map[string]int{"a":1}`},
- {"%#v", map[string]B{"a": {1, 2}}, `map[string]fmt_test.B{"a":fmt_test.B{I:1, j:2}}`},
+ {"%#v", map[string]B{"a": {1, 2}}, `map[string]message.B{"a":message.B{I:1, j:2}}`},
{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`},
- {"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`},
+ {"%#v", SI{}, `message.SI{I:interface {}(nil)}`},
{"%#v", []int(nil), `[]int(nil)`},
{"%#v", []int{}, `[]int{}`},
{"%#v", array, `[5]int{1, 2, 3, 4, 5}`},
@@ -672,8 +695,8 @@
{"%#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}`},
+ {"%#v", barray, `[5]message.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
+ {"%#v", bslice, `[]message.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
{"%#v", []int32(nil), "[]int32(nil)"},
{"%#v", 1.2345678, "1.2345678"},
{"%#v", float32(1.2345678), "1.2345678"},
@@ -755,7 +778,7 @@
// renamings
{"%v", renamedBool(true), "true"},
- {"%d", renamedBool(true), "%!d(fmt_test.renamedBool=true)"},
+ {"%d", renamedBool(true), "%!d(message.renamedBool=true)"},
{"%o", renamedInt(8), "10"},
{"%d", renamedInt8(-9), "-9"},
{"%v", renamedInt16(10), "10"},
@@ -786,13 +809,13 @@
// GoStringer
{"%#v", G(6), "GoString(6)"},
- {"%#v", S{F(7), G(8)}, "fmt_test.S{F:<v=F(7)>, G:GoString(8)}"},
+ {"%#v", S{F(7), G(8)}, "message.S{F:<v=F(7)>, G:GoString(8)}"},
// %T
{"%T", byte(0), "uint8"},
{"%T", reflect.ValueOf(nil), "reflect.Value"},
{"%T", (4 - 3i), "complex128"},
- {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"},
+ {"%T", renamedComplex128(4 - 3i), "message.renamedComplex128"},
{"%T", intVar, "int"},
{"%6T", &intVar, " *int"},
{"%10T", nil, " <nil>"},
@@ -838,15 +861,8 @@
{"%d", time.Time{}.Month(), "1"},
// erroneous things
- {"", nil, "%!(EXTRA <nil>)"},
- {"", 2, "%!(EXTRA int=2)"},
- {"no args", "hello", "no args%!(EXTRA string=hello)"},
{"%s %", "hello", "hello %!(NOVERB)"},
{"%s %.2", "hello", "hello %!(NOVERB)"},
- {"%017091901790959340919092959340919017929593813360", 0, "%!(NOVERB)%!(EXTRA int=0)"},
- {"%184467440737095516170v", 0, "%!(NOVERB)%!(EXTRA int=0)"},
- // 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
@@ -966,7 +982,7 @@
{"%q", byteStringerSlice, "\"hello\""},
{"%x", byteStringerSlice, "68656c6c6f"},
{"%X", byteStringerSlice, "68656C6C6F"},
- {"%#v", byteStringerSlice, "[]fmt_test.byteStringer{0x68, 0x65, 0x6c, 0x6c, 0x6f}"},
+ {"%#v", byteStringerSlice, "[]message.byteStringer{0x68, 0x65, 0x6c, 0x6c, 0x6f}"},
// And the same for Formatter.
{"%v", byteFormatterSlice, "[X X X X X]"},
@@ -975,7 +991,7 @@
{"%x", byteFormatterSlice, "68656c6c6f"},
{"%X", byteFormatterSlice, "68656C6C6F"},
// This next case seems wrong, but the docs say the Formatter wins here.
- {"%#v", byteFormatterSlice, "[]fmt_test.byteFormatter{X, X, X, X, X}"},
+ {"%#v", byteFormatterSlice, "[]message.byteFormatter{X, X, X, X, X}"},
// reflect.Value handled specially in Go 1.5, making it possible to
// see inside non-exported fields (which cannot be accessed with Interface()).
@@ -1010,9 +1026,9 @@
{"%☠", &intVar, "%!☠(*int=0xPTR)"},
{"%☠", make(chan int), "%!☠(chan int=0xPTR)"},
{"%☠", func() {}, "%!☠(func()=0xPTR)"},
- {"%☠", reflect.ValueOf(renamedInt(0)), "%!☠(fmt_test.renamedInt=0)"},
- {"%☠", SI{renamedInt(0)}, "{%!☠(fmt_test.renamedInt=0)}"},
- {"%☠", &[]interface{}{I(1), G(2)}, "&[%!☠(fmt_test.I=1) %!☠(fmt_test.G=2)]"},
+ {"%☠", reflect.ValueOf(renamedInt(0)), "%!☠(message.renamedInt=0)"},
+ {"%☠", SI{renamedInt(0)}, "{%!☠(message.renamedInt=0)}"},
+ {"%☠", &[]interface{}{I(1), G(2)}, "&[%!☠(message.I=1) %!☠(message.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>)]"},
@@ -1025,57 +1041,63 @@
}
func TestSprintf(t *testing.T) {
+ p := NewPrinter(language.Und)
for _, tt := range fmtTests {
- s := Sprintf(tt.fmt, tt.val)
- i := strings.Index(tt.out, "PTR")
- if i >= 0 && i < len(s) {
- var pattern, chars string
- switch {
- case strings.HasPrefix(tt.out[i:], "PTR_b"):
- pattern = "PTR_b"
- chars = "01"
- case strings.HasPrefix(tt.out[i:], "PTR_o"):
- pattern = "PTR_o"
- chars = "01234567"
- case strings.HasPrefix(tt.out[i:], "PTR_d"):
- pattern = "PTR_d"
- chars = "0123456789"
- case strings.HasPrefix(tt.out[i:], "PTR_x"):
- pattern = "PTR_x"
- chars = "0123456789abcdef"
- case strings.HasPrefix(tt.out[i:], "PTR_X"):
- pattern = "PTR_X"
- chars = "0123456789ABCDEF"
- default:
- pattern = "PTR"
- chars = "0123456789abcdefABCDEF"
+ t.Run(fmt.Sprint(tt.fmt, tt.val), func(t *testing.T) {
+ s := p.Sprintf(tt.fmt, tt.val)
+ i := strings.Index(tt.out, "PTR")
+ if i >= 0 && i < len(s) {
+ var pattern, chars string
+ switch {
+ case strings.HasPrefix(tt.out[i:], "PTR_b"):
+ pattern = "PTR_b"
+ chars = "01"
+ case strings.HasPrefix(tt.out[i:], "PTR_o"):
+ pattern = "PTR_o"
+ chars = "01234567"
+ case strings.HasPrefix(tt.out[i:], "PTR_d"):
+ pattern = "PTR_d"
+ chars = "0123456789"
+ case strings.HasPrefix(tt.out[i:], "PTR_x"):
+ pattern = "PTR_x"
+ chars = "0123456789abcdef"
+ case strings.HasPrefix(tt.out[i:], "PTR_X"):
+ pattern = "PTR_X"
+ chars = "0123456789ABCDEF"
+ default:
+ pattern = "PTR"
+ chars = "0123456789abcdefABCDEF"
+ }
+ p := s[:i] + pattern
+ for j := i; j < len(s); j++ {
+ if !strings.ContainsRune(chars, rune(s[j])) {
+ p += s[j:]
+ break
+ }
+ }
+ s = p
}
- p := s[:i] + pattern
- for j := i; j < len(s); j++ {
- if !strings.ContainsRune(chars, rune(s[j])) {
- p += s[j:]
- break
+ if s != tt.out {
+ if _, ok := tt.val.(string); ok {
+ // Don't requote the already-quoted strings.
+ // It's too confusing to read the errors.
+ t.Errorf("Sprintf(%q, %q) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out)
+ } else {
+ t.Errorf("Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out)
}
}
- s = p
- }
- if s != tt.out {
- if _, ok := tt.val.(string); ok {
- // Don't requote the already-quoted strings.
- // It's too confusing to read the errors.
- t.Errorf("Sprintf(%q, %q) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out)
- } else {
- t.Errorf("Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out)
- }
- }
+ })
}
}
+var f float64
+
// 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 values = []float64{1, 0, -1, posInf, negInf, NaN}
+ p := NewPrinter(language.Und)
for _, plus := range yesNo {
for _, zero := range yesNo {
for _, space := range yesNo {
@@ -1102,8 +1124,8 @@
imagFmt += string(char)
for _, realValue := range values {
for _, imagValue := range values {
- one := Sprintf(realFmt, complex(realValue, imagValue))
- two := Sprintf("("+realFmt+imagFmt+"i)", realValue, imagValue)
+ one := p.Sprintf(realFmt, complex(realValue, imagValue))
+ two := p.Sprintf("("+realFmt+imagFmt+"i)", realValue, imagValue)
if one != two {
t.Error(f, one, two)
}
@@ -1118,9 +1140,9 @@
type SE []interface{} // slice of empty; notational compactness.
var reorderTests = []struct {
- fmt string
- val SE
- out string
+ format string
+ args SE
+ out string
}{
{"%[1]d", SE{1}, "1"},
{"%[2]d", SE{2, 1}, "1"},
@@ -1154,113 +1176,130 @@
{"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence.
{"%.[]", SE{}, "%!](BADINDEX)"}, // Issue 10675
{"%.-3d", SE{42}, "%!-(int=42)3d"}, // TODO: Should this set return better error messages?
- {"%2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
- {"%-2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
- {"%.2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"},
+ // The following messages are interpreted as if there is no substitution,
+ // in which case it is okay to have extra arguments. This is different
+ // semantics from the fmt package.
+ {"%2147483648d", SE{42}, "%!(NOVERB)"},
+ {"%-2147483648d", SE{42}, "%!(NOVERB)"},
+ {"%.2147483648d", SE{42}, "%!(NOVERB)"},
}
func TestReorder(t *testing.T) {
- for _, tt := range reorderTests {
- s := Sprintf(tt.fmt, tt.val...)
- if s != tt.out {
- t.Errorf("Sprintf(%q, %v) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out)
- } else {
- }
+ p := NewPrinter(language.Und)
+ for _, tc := range reorderTests {
+ t.Run(fmt.Sprint(tc.format, "/", tc.args), func(t *testing.T) {
+ s := p.Sprintf(tc.format, tc.args...)
+ if s != tc.out {
+ t.Errorf("Sprintf(%q, %v) = %q want %q", tc.format, tc.args, s, tc.out)
+ }
+ })
}
}
func BenchmarkSprintfPadding(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%16f", 1.0)
+ p.Sprintf("%16f", 1.0)
}
})
}
func BenchmarkSprintfEmpty(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("")
+ p.Sprintf("")
}
})
}
func BenchmarkSprintfString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%s", "hello")
+ p.Sprintf("%s", "hello")
}
})
}
func BenchmarkSprintfTruncateString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%.3s", "日本語日本語日本語")
+ p.Sprintf("%.3s", "日本語日本語日本語")
}
})
}
func BenchmarkSprintfQuoteString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%q", "日本語日本語日本語")
+ p.Sprintf("%q", "日本語日本語日本語")
}
})
}
func BenchmarkSprintfInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%d", 5)
+ p.Sprintf("%d", 5)
}
})
}
func BenchmarkSprintfIntInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%d %d", 5, 6)
+ p.Sprintf("%d %d", 5, 6)
}
})
}
func BenchmarkSprintfPrefixedInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
+ p.Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
}
})
}
func BenchmarkSprintfFloat(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%g", 5.23184)
+ p.Sprintf("%g", 5.23184)
}
})
}
func BenchmarkSprintfComplex(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%f", 5.23184+5.23184i)
+ p.Sprintf("%f", 5.23184+5.23184i)
}
})
}
func BenchmarkSprintfBoolean(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%t", true)
+ p.Sprintf("%t", true)
}
})
}
func BenchmarkSprintfHexString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("% #x", "0123456789abcdef")
+ p.Sprintf("% #x", "0123456789abcdef")
}
})
}
@@ -1268,8 +1307,9 @@
func BenchmarkSprintfHexBytes(b *testing.B) {
data := []byte("0123456789abcdef")
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("% #x", data)
+ p.Sprintf("% #x", data)
}
})
}
@@ -1277,8 +1317,9 @@
func BenchmarkSprintfBytes(b *testing.B) {
data := []byte("0123456789abcdef")
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%v", data)
+ p.Sprintf("%v", data)
}
})
}
@@ -1286,8 +1327,9 @@
func BenchmarkSprintfStringer(b *testing.B) {
stringer := I(12345)
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%v", stringer)
+ p.Sprintf("%v", stringer)
}
})
}
@@ -1295,8 +1337,9 @@
func BenchmarkSprintfStructure(b *testing.B) {
s := &[]interface{}{SI{12345}, map[int]string{0: "hello"}}
b.RunParallel(func(pb *testing.PB) {
+ p := NewPrinter(language.English)
for pb.Next() {
- Sprintf("%#v", s)
+ p.Sprintf("%#v", s)
}
})
}
@@ -1304,36 +1347,40 @@
func BenchmarkManyArgs(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
var buf bytes.Buffer
+ p := NewPrinter(language.English)
for pb.Next() {
buf.Reset()
- Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
+ p.Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
}
})
}
func BenchmarkFprintInt(b *testing.B) {
var buf bytes.Buffer
+ p := NewPrinter(language.English)
for i := 0; i < b.N; i++ {
buf.Reset()
- Fprint(&buf, 123456)
+ p.Fprint(&buf, 123456)
}
}
func BenchmarkFprintfBytes(b *testing.B) {
data := []byte(string("0123456789"))
var buf bytes.Buffer
+ p := NewPrinter(language.English)
for i := 0; i < b.N; i++ {
buf.Reset()
- Fprintf(&buf, "%s", data)
+ p.Fprintf(&buf, "%s", data)
}
}
func BenchmarkFprintIntNoAlloc(b *testing.B) {
var x interface{} = 123456
var buf bytes.Buffer
+ p := NewPrinter(language.English)
for i := 0; i < b.N; i++ {
buf.Reset()
- Fprint(&buf, x)
+ p.Fprint(&buf, x)
}
}
@@ -1343,19 +1390,19 @@
var mallocTest = []struct {
count int
desc string
- fn func()
+ fn func(p *Printer)
}{
- {0, `Sprintf("")`, func() { Sprintf("") }},
- {1, `Sprintf("xxx")`, func() { Sprintf("xxx") }},
- {2, `Sprintf("%x")`, func() { Sprintf("%x", 7) }},
- {2, `Sprintf("%s")`, func() { Sprintf("%s", "hello") }},
- {3, `Sprintf("%x %x")`, func() { Sprintf("%x %x", 7, 112) }},
- {2, `Sprintf("%g")`, func() { Sprintf("%g", float32(3.14159)) }}, // TODO: Can this be 1?
- {1, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }},
+ {0, `Sprintf("")`, func(p *Printer) { p.Sprintf("") }},
+ {1, `Sprintf("xxx")`, func(p *Printer) { p.Sprintf("xxx") }},
+ {2, `Sprintf("%x")`, func(p *Printer) { p.Sprintf("%x", 7) }},
+ {2, `Sprintf("%s")`, func(p *Printer) { p.Sprintf("%s", "hello") }},
+ {3, `Sprintf("%x %x")`, func(p *Printer) { p.Sprintf("%x %x", 7, 112) }},
+ {2, `Sprintf("%g")`, func(p *Printer) { p.Sprintf("%g", float32(3.14159)) }}, // TODO: Can this be 1?
+ {1, `Fprintf(buf, "%s")`, func(p *Printer) { mallocBuf.Reset(); p.Fprintf(&mallocBuf, "%s", "hello") }},
// If the interface value doesn't need to allocate, amortized allocation overhead should be zero.
- {0, `Fprintf(buf, "%x %x %x")`, func() {
+ {0, `Fprintf(buf, "%x %x %x")`, func(p *Printer) {
mallocBuf.Reset()
- Fprintf(&mallocBuf, "%x %x %x", mallocPointer, mallocPointer, mallocPointer)
+ p.Fprintf(&mallocBuf, "%x %x %x", mallocPointer, mallocPointer, mallocPointer)
}},
}
@@ -1367,11 +1414,13 @@
t.Skip("skipping malloc count in short mode")
case runtime.GOMAXPROCS(0) > 1:
t.Skip("skipping; GOMAXPROCS>1")
- case race.Enabled:
- t.Skip("skipping malloc count under race detector")
+ // TODO: detect race detecter enabled.
+ // case race.Enabled:
+ // t.Skip("skipping malloc count under race detector")
}
+ p := NewPrinter(language.English)
for _, mt := range mallocTest {
- mallocs := testing.AllocsPerRun(100, mt.fn)
+ mallocs := testing.AllocsPerRun(100, func() { mt.fn(p) })
if got, max := mallocs, float64(mt.count); got > max {
t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max)
}
@@ -1380,7 +1429,7 @@
type flagPrinter struct{}
-func (flagPrinter) Format(f State, c rune) {
+func (flagPrinter) Format(f fmt.State, c rune) {
s := "%"
for i := 0; i < 128; i++ {
if f.Flag(i) {
@@ -1388,10 +1437,10 @@
}
}
if w, ok := f.Width(); ok {
- s += Sprintf("%d", w)
+ s += fmt.Sprintf("%d", w)
}
if p, ok := f.Precision(); ok {
- s += Sprintf(".%d", p)
+ s += fmt.Sprintf(".%d", p)
}
s += string(c)
io.WriteString(f, "["+s+"]")
@@ -1418,7 +1467,7 @@
func TestFlagParser(t *testing.T) {
var flagprinter flagPrinter
for _, tt := range flagtests {
- s := Sprintf(tt.in, &flagprinter)
+ s := NewPrinter(language.Und).Sprintf(tt.in, &flagprinter)
if s != tt.out {
t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out)
}
@@ -1441,15 +1490,16 @@
}{
{"%v", "{abc def 123}"},
{"%+v", "{a:abc b:def c:123}"},
- {"%#v", `fmt_test.T{a:"abc", b:"def", c:123}`},
+ {"%#v", `message.T{a:"abc", b:"def", c:123}`},
}
+ p := NewPrinter(language.Und)
for _, tt := range tests {
- out := Sprintf(tt.fmt, s)
+ out := p.Sprintf(tt.fmt, s)
if out != tt.out {
t.Errorf("Sprintf(%q, s) = %#q, want %#q", tt.fmt, out, tt.out)
}
// The same but with a pointer.
- out = Sprintf(tt.fmt, &s)
+ out = p.Sprintf(tt.fmt, &s)
if out != "&"+tt.out {
t.Errorf("Sprintf(%q, &s) = %#q, want %#q", tt.fmt, out, "&"+tt.out)
}
@@ -1457,17 +1507,18 @@
}
func TestSlicePrinter(t *testing.T) {
+ p := NewPrinter(language.Und)
slice := []int{}
- s := Sprint(slice)
+ s := p.Sprint(slice)
if s != "[]" {
t.Errorf("empty slice printed as %q not %q", s, "[]")
}
slice = []int{1, 2, 3}
- s = Sprint(slice)
+ s = p.Sprint(slice)
if s != "[1 2 3]" {
t.Errorf("slice: got %q expected %q", s, "[1 2 3]")
}
- s = Sprint(&slice)
+ s = p.Sprint(&slice)
if s != "&[1 2 3]" {
t.Errorf("&slice: got %q expected %q", s, "&[1 2 3]")
}
@@ -1490,32 +1541,34 @@
}
func TestMapPrinter(t *testing.T) {
+ p := NewPrinter(language.Und)
m0 := make(map[int]string)
- s := Sprint(m0)
+ s := p.Sprint(m0)
if s != "map[]" {
t.Errorf("empty map printed as %q not %q", s, "map[]")
}
m1 := map[int]string{1: "one", 2: "two", 3: "three"}
a := []string{"1:one", "2:two", "3:three"}
- presentInMap(Sprintf("%v", m1), a, t)
- presentInMap(Sprint(m1), a, t)
+ presentInMap(p.Sprintf("%v", m1), a, t)
+ presentInMap(p.Sprint(m1), a, t)
// Pointer to map prints the same but with initial &.
- if !strings.HasPrefix(Sprint(&m1), "&") {
+ if !strings.HasPrefix(p.Sprint(&m1), "&") {
t.Errorf("no initial & for address of map")
}
- presentInMap(Sprintf("%v", &m1), a, t)
- presentInMap(Sprint(&m1), a, t)
+ presentInMap(p.Sprintf("%v", &m1), a, t)
+ presentInMap(p.Sprint(&m1), a, t)
}
func TestEmptyMap(t *testing.T) {
const emptyMapStr = "map[]"
var m map[string]int
- s := Sprint(m)
+ p := NewPrinter(language.Und)
+ s := p.Sprint(m)
if s != emptyMapStr {
t.Errorf("nil map printed as %q not %q", s, emptyMapStr)
}
m = make(map[string]int)
- s = Sprint(m)
+ s = p.Sprint(m)
if s != emptyMapStr {
t.Errorf("empty map printed as %q not %q", s, emptyMapStr)
}
@@ -1524,7 +1577,8 @@
// 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, "!")
+ p := NewPrinter(language.Und)
+ got := p.Sprint("<", 1, ">:", 1, 2, 3, "!")
expect := "<1>:1 2 3!"
if got != expect {
t.Errorf("got %q expected %q", got, expect)
@@ -1534,7 +1588,8 @@
// 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, "!")
+ p := NewPrinter(language.Und)
+ got := p.Sprintln("<", 1, ">:", 1, 2, 3, "!")
expect := "< 1 >: 1 2 3 !\n"
if got != expect {
t.Errorf("got %q expected %q", got, expect)
@@ -1543,17 +1598,18 @@
// TestFormatterPrintln checks Formatter with Sprint, Sprintln, Sprintf.
func TestFormatterPrintln(t *testing.T) {
+ p := NewPrinter(language.Und)
f := F(1)
expect := "<v=F(1)>\n"
- s := Sprint(f, "\n")
+ s := p.Sprint(f, "\n")
if s != expect {
t.Errorf("Sprint wrong with Formatter: expected %q got %q", expect, s)
}
- s = Sprintln(f)
+ s = p.Sprintln(f)
if s != expect {
t.Errorf("Sprintln wrong with Formatter: expected %q got %q", expect, s)
}
- s = Sprintf("%v\n", f)
+ s = p.Sprintf("%v\n", f)
if s != expect {
t.Errorf("Sprintf wrong with Formatter: expected %q got %q", expect, s)
}
@@ -1595,11 +1651,14 @@
}
func TestWidthAndPrecision(t *testing.T) {
+ p := NewPrinter(language.Und)
for i, tt := range startests {
- s := Sprintf(tt.fmt, tt.in...)
- if s != tt.out {
- t.Errorf("#%d: %q: got %q expected %q", i, tt.fmt, s, tt.out)
- }
+ t.Run(fmt.Sprint(tt.fmt, tt.in), func(t *testing.T) {
+ s := p.Sprintf(tt.fmt, tt.in...)
+ if s != tt.out {
+ t.Errorf("#%d: %q: got %q expected %q", i, tt.fmt, s, tt.out)
+ }
+ })
}
}
@@ -1629,37 +1688,41 @@
}
// Value receiver.
-func (p PanicF) Format(f State, c rune) {
+func (p PanicF) Format(f fmt.State, c rune) {
panic(p.message)
}
var panictests = []struct {
- fmt string
- in interface{}
- out string
+ desc string
+ fmt string
+ in interface{}
+ out string
}{
// String
- {"%s", (*PanicS)(nil), "<nil>"}, // nil pointer special case
- {"%s", PanicS{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
- {"%s", PanicS{3}, "%!s(PANIC=3)"},
+ {"String", "%s", (*PanicS)(nil), "<nil>"}, // nil pointer special case
+ {"String", "%s", PanicS{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
+ {"String", "%s", PanicS{3}, "%!s(PANIC=3)"},
// GoString
- {"%#v", (*PanicGo)(nil), "<nil>"}, // nil pointer special case
- {"%#v", PanicGo{io.ErrUnexpectedEOF}, "%!v(PANIC=unexpected EOF)"},
- {"%#v", PanicGo{3}, "%!v(PANIC=3)"},
+ {"GoString", "%#v", (*PanicGo)(nil), "<nil>"}, // nil pointer special case
+ {"GoString", "%#v", PanicGo{io.ErrUnexpectedEOF}, "%!v(PANIC=unexpected EOF)"},
+ {"GoString", "%#v", PanicGo{3}, "%!v(PANIC=3)"},
// Issue 18282. catchPanic should not clear fmtFlags permanently.
- {"%#v", []interface{}{PanicGo{3}, PanicGo{3}}, "[]interface {}{%!v(PANIC=3), %!v(PANIC=3)}"},
+ {"Issue 18282", "%#v", []interface{}{PanicGo{3}, PanicGo{3}}, "[]interface {}{%!v(PANIC=3), %!v(PANIC=3)}"},
// Format
- {"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case
- {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
- {"%s", PanicF{3}, "%!s(PANIC=3)"},
+ {"Format", "%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case
+ {"Format", "%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
+ {"Format", "%s", PanicF{3}, "%!s(PANIC=3)"},
}
func TestPanics(t *testing.T) {
+ p := NewPrinter(language.Und)
for i, tt := range panictests {
- s := Sprintf(tt.fmt, tt.in)
- if s != tt.out {
- t.Errorf("%d: %q: got %q expected %q", i, tt.fmt, s, tt.out)
- }
+ t.Run(fmt.Sprint(tt.desc, "/", tt.fmt, "/", tt.in), func(t *testing.T) {
+ s := p.Sprintf(tt.fmt, tt.in)
+ if s != tt.out {
+ t.Errorf("%d: %q: got %q expected %q", i, tt.fmt, s, tt.out)
+ }
+ })
}
}
@@ -1672,6 +1735,7 @@
}
func (r *Recur) String() string {
+ p := NewPrinter(language.Und)
if recurCount++; recurCount > 10 {
*r.failed = true
return "FAIL"
@@ -1679,41 +1743,33 @@
// This will call badVerb. Before the fix, that would cause us to recur into
// this routine to print %!p(value). Now we don't call the user's method
// during an error.
- return Sprintf("recur@%p value: %d", r, r.i)
+ return p.Sprintf("recur@%p value: %d", r, r.i)
}
func TestBadVerbRecursion(t *testing.T) {
+ p := NewPrinter(language.Und)
failed := false
r := &Recur{3, &failed}
- Sprintf("recur@%p value: %d\n", &r, r.i)
+ p.Sprintf("recur@%p value: %d\n", &r, r.i)
if failed {
t.Error("fail with pointer")
}
failed = false
r = &Recur{4, &failed}
- Sprintf("recur@%p, value: %d\n", r, r.i)
+ p.Sprintf("recur@%p, value: %d\n", r, r.i)
if failed {
t.Error("fail with value")
}
}
-func TestIsSpace(t *testing.T) {
- // This tests the internal isSpace function.
- // IsSpace = isSpace is defined in export_test.go.
- for i := rune(0); i <= unicode.MaxRune; i++ {
- if IsSpace(i) != unicode.IsSpace(i) {
- t.Errorf("isSpace(%U) = %v, want %v", i, IsSpace(i), unicode.IsSpace(i))
- }
- }
-}
-
func TestNilDoesNotBecomeTyped(t *testing.T) {
+ p := NewPrinter(language.Und)
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) // go vet should complain about this line.
- const expect = "%!s(<nil>) %!s(*fmt_test.A=<nil>) %!s(<nil>) {} %!s(<nil>)"
+ got := p.Sprintf("%s %s %s %s %s", nil, a, nil, b, nil) // go vet should complain about this line.
+ const expect = "%!s(<nil>) %!s(*message.A=<nil>) %!s(<nil>) {} %!s(<nil>)"
if got != expect {
t.Errorf("expected:\n\t%q\ngot:\n\t%q", expect, got)
}
@@ -1770,7 +1826,7 @@
{"%v", [1]flagPrinter{}, "[[%v]]"},
{"%-v", [1]flagPrinter{}, "[[%-v]]"},
{"%+v", [1]flagPrinter{}, "[[%+v]]"},
- {"%#v", [1]flagPrinter{}, "[1]fmt_test.flagPrinter{[%#v]}"},
+ {"%#v", [1]flagPrinter{}, "[1]message.flagPrinter{[%#v]}"},
{"% v", [1]flagPrinter{}, "[[% v]]"},
{"%0v", [1]flagPrinter{}, "[[%0v]]"},
{"%1.2v", [1]flagPrinter{}, "[[%1.2v]]"},
@@ -1782,8 +1838,9 @@
}
func TestFormatterFlags(t *testing.T) {
+ p := NewPrinter(language.Und)
for _, tt := range formatterFlagTests {
- s := Sprintf(tt.in, tt.val)
+ s := p.Sprintf(tt.in, tt.val)
if s != tt.out {
t.Errorf("Sprintf(%q, %T) = %q, want %q", tt.in, tt.val, s, tt.out)
}
@@ -1806,7 +1863,7 @@
{"1a234", 1, 3, 0, false, 1},
}
for _, tt := range testCases {
- num, isnum, newi := Parsenum(tt.s, tt.start, tt.end)
+ num, isnum, newi := parsenum(tt.s, tt.start, tt.end)
if num != tt.num || isnum != tt.isnum || newi != tt.newi {
t.Errorf("parsenum(%q, %d, %d) = %d, %v, %d, want %d, %v, %d", tt.s, tt.start, tt.end, num, isnum, newi, tt.num, tt.isnum, tt.newi)
}
diff --git a/message/format.go b/message/format.go
index f80082b..d3340d1 100644
--- a/message/format.go
+++ b/message/format.go
@@ -1,11 +1,11 @@
-// +build ignore
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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.
-package fmt
+package message
import (
+ "bytes"
"strconv"
"unicode/utf8"
)
@@ -37,10 +37,10 @@
sharpV bool
}
-// A fmt is the raw formatter used by Printf etc.
+// A formatInfo is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
-type fmt struct {
- buf *buffer
+type formatInfo struct {
+ buf *bytes.Buffer
fmtFlags
@@ -52,43 +52,34 @@
intbuf [68]byte
}
-func (f *fmt) clearflags() {
+func (f *formatInfo) clearflags() {
f.fmtFlags = fmtFlags{}
}
-func (f *fmt) init(buf *buffer) {
+func (f *formatInfo) init(buf *bytes.Buffer) {
f.buf = buf
f.clearflags()
}
// writePadding generates n bytes of padding.
-func (f *fmt) writePadding(n int) {
+func (f *formatInfo) writePadding(n int) {
if n <= 0 { // No padding bytes needed.
return
}
- buf := *f.buf
- oldLen := len(buf)
- newLen := oldLen + n
- // Make enough room for padding.
- if newLen > cap(buf) {
- buf = make(buffer, cap(buf)*2+n)
- copy(buf, *f.buf)
- }
+ f.buf.Grow(n)
// Decide which byte the padding should be filled with.
padByte := byte(' ')
if f.zero {
padByte = byte('0')
}
// Fill padding with padByte.
- padding := buf[oldLen:newLen]
- for i := range padding {
- padding[i] = padByte
+ for i := 0; i < n; i++ {
+ f.buf.WriteByte(padByte) // TODO: make more efficient.
}
- *f.buf = buf[:newLen]
}
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
-func (f *fmt) pad(b []byte) {
+func (f *formatInfo) pad(b []byte) {
if !f.widPresent || f.wid == 0 {
f.buf.Write(b)
return
@@ -106,7 +97,7 @@
}
// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
-func (f *fmt) padString(s string) {
+func (f *formatInfo) padString(s string) {
if !f.widPresent || f.wid == 0 {
f.buf.WriteString(s)
return
@@ -124,7 +115,7 @@
}
// fmt_boolean formats a boolean.
-func (f *fmt) fmt_boolean(v bool) {
+func (f *formatInfo) fmt_boolean(v bool) {
if v {
f.padString("true")
} else {
@@ -133,7 +124,7 @@
}
// fmt_unicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'".
-func (f *fmt) fmt_unicode(u uint64) {
+func (f *formatInfo) fmt_unicode(u uint64) {
buf := f.intbuf[0:]
// With default precision set the maximum needed buf length is 18
@@ -192,7 +183,7 @@
}
// fmt_integer formats signed and unsigned integers.
-func (f *fmt) fmt_integer(u uint64, base int, isSigned bool, digits string) {
+func (f *formatInfo) fmt_integer(u uint64, base int, isSigned bool, digits string) {
negative := isSigned && int64(u) < 0
if negative {
u = -u
@@ -310,7 +301,7 @@
}
// truncate truncates the string to the specified precision, if present.
-func (f *fmt) truncate(s string) string {
+func (f *formatInfo) truncate(s string) string {
if f.precPresent {
n := f.prec
for i := range s {
@@ -324,13 +315,13 @@
}
// fmt_s formats a string.
-func (f *fmt) fmt_s(s string) {
+func (f *formatInfo) fmt_s(s string) {
s = f.truncate(s)
f.padString(s)
}
// 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) {
+func (f *formatInfo) fmt_sbx(s string, b []byte, digits string) {
length := len(b)
if b == nil {
// No byte slice present. Assume string s should be encoded.
@@ -365,19 +356,21 @@
f.writePadding(f.wid - width)
}
// Write the encoding directly into the output buffer.
- buf := *f.buf
+ buf := f.buf
if f.sharp {
// Add leading 0x or 0X.
- buf = append(buf, '0', digits[16])
+ buf.WriteByte('0')
+ buf.WriteByte(digits[16])
}
var c byte
for i := 0; i < length; i++ {
if f.space && i > 0 {
// Separate elements with a space.
- buf = append(buf, ' ')
+ buf.WriteByte(' ')
if f.sharp {
// Add leading 0x or 0X for each element.
- buf = append(buf, '0', digits[16])
+ buf.WriteByte('0')
+ buf.WriteByte(digits[16])
}
}
if b != nil {
@@ -386,9 +379,9 @@
c = s[i] // Take a byte from the input string.
}
// Encode each byte as two hexadecimal digits.
- buf = append(buf, digits[c>>4], digits[c&0xF])
+ buf.WriteByte(digits[c>>4])
+ buf.WriteByte(digits[c&0xF])
}
- *f.buf = buf
// Handle padding to the right.
if f.widPresent && f.wid > width && f.minus {
f.writePadding(f.wid - width)
@@ -396,19 +389,19 @@
}
// fmt_sx formats a string as a hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sx(s, digits string) {
+func (f *formatInfo) 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) {
+func (f *formatInfo) fmt_bx(b []byte, digits string) {
f.fmt_sbx("", b, digits)
}
// fmt_q formats a string as a double-quoted, escaped Go string constant.
// 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) fmt_q(s string) {
+func (f *formatInfo) fmt_q(s string) {
s = f.truncate(s)
if f.sharp && strconv.CanBackquote(s) {
f.padString("`" + s + "`")
@@ -424,7 +417,7 @@
// fmt_c formats an integer as a Unicode character.
// If the character is not valid Unicode, it will print '\ufffd'.
-func (f *fmt) fmt_c(c uint64) {
+func (f *formatInfo) fmt_c(c uint64) {
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
@@ -436,7 +429,7 @@
// fmt_qc formats an integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'.
-func (f *fmt) fmt_qc(c uint64) {
+func (f *formatInfo) fmt_qc(c uint64) {
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
@@ -451,7 +444,7 @@
// fmt_float formats a float64. It assumes that verb is a valid format specifier
// for strconv.AppendFloat and therefore fits into a byte.
-func (f *fmt) fmt_float(v float64, size int, verb rune, prec int) {
+func (f *formatInfo) fmt_float(v float64, size int, verb rune, prec int) {
// Explicit precision in format specifier overrules default precision.
if f.precPresent {
prec = f.prec
diff --git a/message/message.go b/message/message.go
index 92c7a93..edcd3bf 100644
--- a/message/message.go
+++ b/message/message.go
@@ -10,13 +10,9 @@
package message // import "golang.org/x/text/message"
import (
- "bytes"
- "fmt"
"io"
"os"
- "strings"
- "golang.org/x/text/internal/format"
"golang.org/x/text/language"
"golang.org/x/text/message/catalog"
)
@@ -32,16 +28,6 @@
// road if it the benefits do not seem to outweigh the disadvantages.
}
-type printer struct {
- tag language.Tag
-
- catContext *catalog.Context
-
- buf bytes.Buffer
-
- args []interface{}
-}
-
// NewPrinter returns a Printer that formats messages tailored to language t.
func NewPrinter(t language.Tag) *Printer {
p := &Printer{printer{
@@ -53,55 +39,65 @@
// Sprint is like fmt.Sprint, but using language-specific formatting.
func (p *Printer) Sprint(a ...interface{}) string {
- return fmt.Sprint(p.bindArgs(a)...)
+ p.printer.reset()
+ p.printer.doPrint(a)
+ return p.printer.String()
}
// Fprint is like fmt.Fprint, but using language-specific formatting.
func (p *Printer) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
- return fmt.Fprint(w, p.bindArgs(a)...)
+ p.printer.reset()
+ p.printer.doPrint(a)
+ n64, err := io.Copy(w, &p.printer.Buffer)
+ return int(n64), err
}
// Print is like fmt.Print, but using language-specific formatting.
func (p *Printer) Print(a ...interface{}) (n int, err error) {
- return fmt.Print(p.bindArgs(a)...)
+ return p.Fprint(os.Stdout, a...)
}
// Sprintln is like fmt.Sprintln, but using language-specific formatting.
func (p *Printer) Sprintln(a ...interface{}) string {
- return fmt.Sprintln(p.bindArgs(a)...)
+ p.printer.reset()
+ p.printer.doPrintln(a)
+ return p.printer.String()
}
// Fprintln is like fmt.Fprintln, but using language-specific formatting.
func (p *Printer) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
- return fmt.Fprintln(w, p.bindArgs(a)...)
+ p.printer.reset()
+ p.printer.doPrintln(a)
+ n64, err := io.Copy(w, &p.printer.Buffer)
+ return int(n64), err
}
// Println is like fmt.Println, but using language-specific formatting.
func (p *Printer) Println(a ...interface{}) (n int, err error) {
- return fmt.Println(p.bindArgs(a)...)
+ return p.Fprintln(os.Stdout, a...)
}
// Sprintf is like fmt.Sprintf, but using language-specific formatting.
func (p *Printer) Sprintf(key Reference, a ...interface{}) string {
lookupAndFormat(p, key, a)
- return p.printer.buf.String()
+ return p.printer.String()
}
// Fprintf is like fmt.Fprintf, but using language-specific formatting.
func (p *Printer) Fprintf(w io.Writer, key Reference, a ...interface{}) (n int, err error) {
lookupAndFormat(p, key, a)
- return w.Write(p.printer.buf.Bytes())
+ return w.Write(p.printer.Bytes())
}
// Printf is like fmt.Printf, but using language-specific formatting.
func (p *Printer) Printf(key Reference, a ...interface{}) (n int, err error) {
lookupAndFormat(p, key, a)
- return os.Stdout.Write(p.printer.buf.Bytes())
+ return os.Stdout.Write(p.printer.Bytes())
}
func lookupAndFormat(p *Printer, r Reference, a []interface{}) {
- p.printer.buf.Reset()
- p.printer.args = p.bindArgs(a)
+ p.printer.reset()
+ p.printer.args = a
var id, msg string
switch v := r.(type) {
case string:
@@ -121,7 +117,7 @@
}
// Arg implements catmsg.Renderer.
-func (p *printer) Arg(i int) interface{} {
+func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool
if uint(i) < uint(len(p.args)) {
return p.args[i]
}
@@ -130,31 +126,7 @@
// Render implements catmsg.Renderer.
func (p *printer) Render(msg string) {
- // fmt does not allow all arguments to be dropped in a format string. It
- // only allows arguments to be dropped if at least one of the substitutions
- // uses the positional marker (e.g. %[1]s). This hack works around this.
- // TODO: This is only an approximation of the parsing of substitution
- // patterns. Make more precise once we know if we can get by with fmt's
- // formatting, which may not be the case.
- var hasSub bool
- for i := 0; i < len(msg)-1; i++ {
- if msg[i] == '%' {
- for i++; i < len(msg); i++ {
- if strings.IndexByte("[]#+- *01234567890.", msg[i]) < 0 {
- break
- }
- }
- if i < len(msg) && msg[i] != '%' {
- hasSub = true
- break
- }
- }
- }
- if !hasSub {
- fmt.Fprintf(&p.buf, msg)
- return
- }
- fmt.Fprintf(&p.buf, msg, p.args...)
+ p.doPrintf(msg)
}
// A Reference is a string or a message reference.
@@ -171,46 +143,3 @@
type key struct {
id, fallback string
}
-
-// bindArgs wraps arguments with implementation of fmt.Formatter, if needed.
-func (p *Printer) bindArgs(a []interface{}) []interface{} {
- out := make([]interface{}, len(a))
- for i, x := range a {
- switch v := x.(type) {
- case fmt.Formatter:
- // Wrap the value with a Formatter that augments the State with
- // language-specific attributes.
- out[i] = &value{v, p}
-
- // NOTE: as we use fmt.Formatter, we can't distinguish between
- // regular and localized formatters, so we always need to wrap it.
-
- // TODO: handle
- // - numbers
- // - lists
- // - time?
- default:
- out[i] = x
- }
- }
- return out
-}
-
-// state implements "golang.org/x/text/internal/format".State.
-type state struct {
- fmt.State
- p *Printer
-}
-
-func (s *state) Language() language.Tag { return s.p.printer.tag }
-
-var _ format.State = &state{}
-
-type value struct {
- x fmt.Formatter
- p *Printer
-}
-
-func (v *value) Format(s fmt.State, verb rune) {
- v.x.Format(&state{s, v.p}, verb)
-}
diff --git a/message/message_test.go b/message/message_test.go
index 73afd64..9c14813 100644
--- a/message/message_test.go
+++ b/message/message_test.go
@@ -132,24 +132,26 @@
cat, _ := initCat(tc.cat)
for i, pt := range tc.test {
- tag := language.MustParse(pt.tag)
- p := Printer{printer{
- tag: tag,
- }}
- p.printer.catContext = cat.Context(tag, &p.printer)
+ t.Run(fmt.Sprintf("%s:%d", tc.desc, i), func(t *testing.T) {
+ tag := language.MustParse(pt.tag)
+ p := Printer{printer{
+ tag: tag,
+ }}
+ p.printer.catContext = cat.Context(tag, &p.printer)
- if got := p.Sprintf(pt.key, pt.args...); got != pt.want {
- t.Errorf("%s:%d:Sprintf(%s, %v) = %s; want %s",
- tc.desc, i, pt.key, pt.args, got, pt.want)
- continue // Next error will likely be the same.
- }
+ if got := p.Sprintf(pt.key, pt.args...); got != pt.want {
+ t.Errorf("Sprintf(%q, %v) = %s; want %s",
+ pt.key, pt.args, got, pt.want)
+ return // Next error will likely be the same.
+ }
- w := &bytes.Buffer{}
- p.Fprintf(w, pt.key, pt.args...)
- if got := w.String(); got != pt.want {
- t.Errorf("%s:%d:Fprintf(%s, %v) = %s; want %s",
- tc.desc, i, pt.key, pt.args, got, pt.want)
- }
+ w := &bytes.Buffer{}
+ p.Fprintf(w, pt.key, pt.args...)
+ if got := w.String(); got != pt.want {
+ t.Errorf("Fprintf(%q, %v) = %s; want %s",
+ pt.key, pt.args, got, pt.want)
+ }
+ })
}
}
}
diff --git a/message/print.go b/message/print.go
index 305b7e5..caa50b6 100644
--- a/message/print.go
+++ b/message/print.go
@@ -1,17 +1,18 @@
-// +build ignore
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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.
-package fmt
+package message
import (
- "errors"
- "io"
- "os"
+ "bytes"
+ // TODO: consider copying interfaces from package fmt to avoid dependency.
+ "fmt"
"reflect"
- "sync"
"unicode/utf8"
+
+ "golang.org/x/text/language"
+ "golang.org/x/text/message/catalog"
)
// Strings for use with buffer.WriteString.
@@ -30,90 +31,32 @@
badWidthString = "%!(BADWIDTH)"
badPrecString = "%!(BADPREC)"
noVerbString = "%!(NOVERB)"
- invReflectString = "<invalid reflect.Value>"
+
+ invReflectString = "<invalid reflect.Value>"
)
-// State represents the printer state passed to custom formatters.
-// It provides access to the io.Writer interface plus information about
-// the flags and options for the operand's format specifier.
-type State interface {
- // Write is the function to call to emit formatted output to be printed.
- Write(b []byte) (n int, err error)
- // Width returns the value of the width option and whether it has been set.
- Width() (wid int, ok bool)
- // Precision returns the value of the precision option and whether it has been set.
- Precision() (prec int, ok bool)
+// printer is used to store a printer's state.
+// It implements "golang.org/x/text/internal/format".State.
+type printer struct {
+ // the context for looking up message translations
+ catContext *catalog.Context
+ // the language
+ tag language.Tag
- // Flag reports whether the flag c, a character, has been set.
- Flag(c int) bool
-}
+ // buffer for accumulating output.
+ bytes.Buffer
-// Formatter is the interface implemented by values with a custom formatter.
-// The implementation of Format may call Sprint(f) or Fprint(f) etc.
-// to generate its output.
-type Formatter interface {
- Format(f State, c rune)
-}
-
-// Stringer is implemented by any value that has a String method,
-// which defines the ``native'' format for that value.
-// The String method is used to print values passed as an operand
-// to any format that accepts a string or to an unformatted printer
-// such as Print.
-type Stringer interface {
- String() string
-}
-
-// GoStringer is implemented by any value that has a GoString method,
-// which defines the Go syntax for that value.
-// The GoString method is used to print values passed as an operand
-// to a %#v format.
-type GoStringer interface {
- GoString() string
-}
-
-// Use simple []byte instead of bytes.Buffer to avoid large dependency.
-type buffer []byte
-
-func (b *buffer) Write(p []byte) {
- *b = append(*b, p...)
-}
-
-func (b *buffer) WriteString(s string) {
- *b = append(*b, s...)
-}
-
-func (b *buffer) WriteByte(c byte) {
- *b = append(*b, c)
-}
-
-func (bp *buffer) WriteRune(r rune) {
- if r < utf8.RuneSelf {
- *bp = append(*bp, byte(r))
- return
- }
-
- b := *bp
- n := len(b)
- for n+utf8.UTFMax > cap(b) {
- b = append(b, 0)
- }
- w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
- *bp = b[:n+w]
-}
-
-// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
-type pp struct {
- buf buffer
-
+ // retain arguments across calls.
+ args []interface{}
+ // retain current argument number across calls
+ argNum int
// arg holds the current item, as an interface{}.
arg interface{}
-
// value is used instead of arg for reflect values.
value reflect.Value
// fmt is used to format basic items such as integers or strings.
- fmt fmt
+ fmt formatInfo
// reordered records whether the format string used argument reordering.
reordered bool
@@ -125,32 +68,23 @@
erroring bool
}
-var ppFree = sync.Pool{
- New: func() interface{} { return new(pp) },
-}
-
-// newPrinter allocates a new pp struct or grabs a cached one.
-func newPrinter() *pp {
- p := ppFree.Get().(*pp)
+func (p *printer) reset() {
+ p.Buffer.Reset()
+ p.argNum = 0
+ p.reordered = false
p.panicking = false
p.erroring = false
- p.fmt.init(&p.buf)
- return p
+ p.fmt.init(&p.Buffer)
}
-// free saves used pp structs in ppFree; avoids an allocation per invocation.
-func (p *pp) free() {
- p.buf = p.buf[:0]
- p.arg = nil
- p.value = reflect.Value{}
- ppFree.Put(p)
-}
+// Language implements "golang.org/x/text/internal/format".State.
+func (p *printer) Language() language.Tag { return p.tag }
-func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
+func (p *printer) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
-func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }
+func (p *printer) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }
-func (p *pp) Flag(b int) bool {
+func (p *printer) Flag(b int) bool {
switch b {
case '-':
return p.fmt.minus
@@ -166,108 +100,6 @@
return false
}
-// Implement Write so we can call Fprintf on a pp (through State), for
-// recursive use in custom verbs.
-func (p *pp) Write(b []byte) (ret int, err error) {
- p.buf.Write(b)
- return len(b), nil
-}
-
-// These routines end in 'f' and take a format string.
-
-// Fprintf formats according to a format specifier and writes to w.
-// It returns the number of bytes written and any write error encountered.
-func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
- p := newPrinter()
- p.doPrintf(format, a)
- n, err = w.Write(p.buf)
- p.free()
- return
-}
-
-// Printf formats according to a format specifier and writes to standard output.
-// It returns the number of bytes written and any write error encountered.
-func Printf(format string, a ...interface{}) (n int, err error) {
- return Fprintf(os.Stdout, format, a...)
-}
-
-// Sprintf formats according to a format specifier and returns the resulting string.
-func Sprintf(format string, a ...interface{}) string {
- p := newPrinter()
- p.doPrintf(format, a)
- s := string(p.buf)
- p.free()
- return s
-}
-
-// 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...))
-}
-
-// These routines do not take a format string
-
-// Fprint formats using the default formats for its operands and writes to w.
-// Spaces are added between operands when neither is a string.
-// It returns the number of bytes written and any write error encountered.
-func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
- p := newPrinter()
- p.doPrint(a)
- n, err = w.Write(p.buf)
- p.free()
- return
-}
-
-// Print formats using the default formats for its operands and writes to standard output.
-// Spaces are added between operands when neither is a string.
-// It returns the number of bytes written and any write error encountered.
-func Print(a ...interface{}) (n int, err error) {
- return Fprint(os.Stdout, a...)
-}
-
-// Sprint formats using the default formats for its operands and returns the resulting string.
-// Spaces are added between operands when neither is a string.
-func Sprint(a ...interface{}) string {
- p := newPrinter()
- p.doPrint(a)
- s := string(p.buf)
- p.free()
- return s
-}
-
-// These routines end in 'ln', do not take a format string,
-// always add spaces between operands, and add a newline
-// after the last operand.
-
-// Fprintln formats using the default formats for its operands and writes to w.
-// Spaces are always added between operands and a newline is appended.
-// It returns the number of bytes written and any write error encountered.
-func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
- p := newPrinter()
- p.doPrintln(a)
- n, err = w.Write(p.buf)
- p.free()
- return
-}
-
-// Println formats using the default formats for its operands and writes to standard output.
-// Spaces are always added between operands and a newline is appended.
-// It returns the number of bytes written and any write error encountered.
-func Println(a ...interface{}) (n int, err error) {
- return Fprintln(os.Stdout, a...)
-}
-
-// Sprintln formats using the default formats for its operands and returns the resulting string.
-// Spaces are always added between operands and a newline is appended.
-func Sprintln(a ...interface{}) string {
- p := newPrinter()
- p.doPrintln(a)
- s := string(p.buf)
- p.free()
- return s
-}
-
// getField gets the i'th field of the struct value.
// If the field is itself is an interface, return a value for
// the thing inside the interface, not the interface itself.
@@ -301,38 +133,38 @@
return
}
-func (p *pp) unknownType(v reflect.Value) {
+func (p *printer) unknownType(v reflect.Value) {
if !v.IsValid() {
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
return
}
- p.buf.WriteByte('?')
- p.buf.WriteString(v.Type().String())
- p.buf.WriteByte('?')
+ p.WriteByte('?')
+ p.WriteString(v.Type().String())
+ p.WriteByte('?')
}
-func (p *pp) badVerb(verb rune) {
+func (p *printer) badVerb(verb rune) {
p.erroring = true
- p.buf.WriteString(percentBangString)
- p.buf.WriteRune(verb)
- p.buf.WriteByte('(')
+ p.WriteString(percentBangString)
+ p.WriteRune(verb)
+ p.WriteByte('(')
switch {
case p.arg != nil:
- p.buf.WriteString(reflect.TypeOf(p.arg).String())
- p.buf.WriteByte('=')
+ p.WriteString(reflect.TypeOf(p.arg).String())
+ p.WriteByte('=')
p.printArg(p.arg, 'v')
case p.value.IsValid():
- p.buf.WriteString(p.value.Type().String())
- p.buf.WriteByte('=')
+ p.WriteString(p.value.Type().String())
+ p.WriteByte('=')
p.printValue(p.value, 'v', 0)
default:
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
}
- p.buf.WriteByte(')')
+ p.WriteByte(')')
p.erroring = false
}
-func (p *pp) fmtBool(v bool, verb rune) {
+func (p *printer) fmtBool(v bool, verb rune) {
switch verb {
case 't', 'v':
p.fmt.fmt_boolean(v)
@@ -343,7 +175,7 @@
// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or
// not, as requested, by temporarily setting the sharp flag.
-func (p *pp) fmt0x64(v uint64, leading0x bool) {
+func (p *printer) fmt0x64(v uint64, leading0x bool) {
sharp := p.fmt.sharp
p.fmt.sharp = leading0x
p.fmt.fmt_integer(v, 16, unsigned, ldigits)
@@ -351,7 +183,7 @@
}
// fmtInteger formats a signed or unsigned integer.
-func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) {
+func (p *printer) fmtInteger(v uint64, isSigned bool, verb rune) {
switch verb {
case 'v':
if p.fmt.sharpV && !isSigned {
@@ -386,7 +218,7 @@
// fmtFloat formats a float. The default precision for each verb
// is specified as last argument in the call to fmt_float.
-func (p *pp) fmtFloat(v float64, size int, verb rune) {
+func (p *printer) fmtFloat(v float64, size int, verb rune) {
switch verb {
case 'v':
p.fmt.fmt_float(v, size, 'g', -1)
@@ -404,25 +236,25 @@
// fmtComplex formats a complex number v with
// r = real(v) and j = imag(v) as (r+ji) using
// fmtFloat for r and j formatting.
-func (p *pp) fmtComplex(v complex128, size int, verb rune) {
+func (p *printer) fmtComplex(v complex128, size int, verb rune) {
// Make sure any unsupported verbs are found before the
// calls to fmtFloat to not generate an incorrect error string.
switch verb {
case 'v', 'b', 'g', 'G', 'f', 'F', 'e', 'E':
oldPlus := p.fmt.plus
- p.buf.WriteByte('(')
+ p.WriteByte('(')
p.fmtFloat(real(v), size/2, verb)
// Imaginary part always has a sign.
p.fmt.plus = true
p.fmtFloat(imag(v), size/2, verb)
- p.buf.WriteString("i)")
+ p.WriteString("i)")
p.fmt.plus = oldPlus
default:
p.badVerb(verb)
}
}
-func (p *pp) fmtString(v string, verb rune) {
+func (p *printer) fmtString(v string, verb rune) {
switch verb {
case 'v':
if p.fmt.sharpV {
@@ -443,32 +275,32 @@
}
}
-func (p *pp) fmtBytes(v []byte, verb rune, typeString string) {
+func (p *printer) fmtBytes(v []byte, verb rune, typeString string) {
switch verb {
case 'v', 'd':
if p.fmt.sharpV {
- p.buf.WriteString(typeString)
+ p.WriteString(typeString)
if v == nil {
- p.buf.WriteString(nilParenString)
+ p.WriteString(nilParenString)
return
}
- p.buf.WriteByte('{')
+ p.WriteByte('{')
for i, c := range v {
if i > 0 {
- p.buf.WriteString(commaSpaceString)
+ p.WriteString(commaSpaceString)
}
p.fmt0x64(uint64(c), true)
}
- p.buf.WriteByte('}')
+ p.WriteByte('}')
} else {
- p.buf.WriteByte('[')
+ p.WriteByte('[')
for i, c := range v {
if i > 0 {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
p.fmt.fmt_integer(uint64(c), 10, unsigned, ldigits)
}
- p.buf.WriteByte(']')
+ p.WriteByte(']')
}
case 's':
p.fmt.fmt_s(string(v))
@@ -483,7 +315,7 @@
}
}
-func (p *pp) fmtPointer(value reflect.Value, verb rune) {
+func (p *printer) fmtPointer(value reflect.Value, verb rune) {
var u uintptr
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
@@ -496,15 +328,15 @@
switch verb {
case 'v':
if p.fmt.sharpV {
- p.buf.WriteByte('(')
- p.buf.WriteString(value.Type().String())
- p.buf.WriteString(")(")
+ p.WriteByte('(')
+ p.WriteString(value.Type().String())
+ p.WriteString(")(")
if u == 0 {
- p.buf.WriteString(nilString)
+ p.WriteString(nilString)
} else {
p.fmt0x64(uint64(u), true)
}
- p.buf.WriteByte(')')
+ p.WriteByte(')')
} else {
if u == 0 {
p.fmt.padString(nilAngleString)
@@ -521,13 +353,13 @@
}
}
-func (p *pp) catchPanic(arg interface{}, verb rune) {
+func (p *printer) catchPanic(arg interface{}, verb rune) {
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
// value receiver, and in either case, "<nil>" is a nice result.
if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() {
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
return
}
// Otherwise print a concise panic message. Most of the time the panic
@@ -541,24 +373,24 @@
// For this output we want default behavior.
p.fmt.clearflags()
- p.buf.WriteString(percentBangString)
- p.buf.WriteRune(verb)
- p.buf.WriteString(panicString)
+ p.WriteString(percentBangString)
+ p.WriteRune(verb)
+ p.WriteString(panicString)
p.panicking = true
p.printArg(err, 'v')
p.panicking = false
- p.buf.WriteByte(')')
+ p.WriteByte(')')
p.fmt.fmtFlags = oldFlags
}
}
-func (p *pp) handleMethods(verb rune) (handled bool) {
+func (p *printer) handleMethods(verb rune) (handled bool) {
if p.erroring {
return
}
// Is it a Formatter?
- if formatter, ok := p.arg.(Formatter); ok {
+ if formatter, ok := p.arg.(fmt.Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb)
formatter.Format(p, verb)
@@ -567,7 +399,7 @@
// If we're doing Go syntax and the argument knows how to supply it, take care of it now.
if p.fmt.sharpV {
- if stringer, ok := p.arg.(GoStringer); ok {
+ if stringer, ok := p.arg.(fmt.GoStringer); ok {
handled = true
defer p.catchPanic(p.arg, verb)
// Print the result of GoString unadorned.
@@ -591,7 +423,7 @@
p.fmtString(v.Error(), verb)
return
- case Stringer:
+ case fmt.Stringer:
handled = true
defer p.catchPanic(p.arg, verb)
p.fmtString(v.String(), verb)
@@ -602,7 +434,7 @@
return false
}
-func (p *pp) printArg(arg interface{}, verb rune) {
+func (p *printer) printArg(arg interface{}, verb rune) {
p.arg = arg
p.value = reflect.Value{}
@@ -687,7 +519,7 @@
// printValue is similar to printArg but starts with a reflect value, not an interface{} value.
// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
-func (p *pp) printValue(value reflect.Value, verb rune, depth int) {
+func (p *printer) printValue(value reflect.Value, verb rune, depth int) {
// Handle values with special methods if not already handled by printArg (depth == 0).
if depth > 0 && value.IsValid() && value.CanInterface() {
p.arg = value.Interface()
@@ -701,11 +533,11 @@
switch f := value; value.Kind() {
case reflect.Invalid:
if depth == 0 {
- p.buf.WriteString(invReflectString)
+ p.WriteString(invReflectString)
} else {
switch verb {
case 'v':
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
default:
p.badVerb(verb)
}
@@ -728,63 +560,63 @@
p.fmtString(f.String(), verb)
case reflect.Map:
if p.fmt.sharpV {
- p.buf.WriteString(f.Type().String())
+ p.WriteString(f.Type().String())
if f.IsNil() {
- p.buf.WriteString(nilParenString)
+ p.WriteString(nilParenString)
return
}
- p.buf.WriteByte('{')
+ p.WriteByte('{')
} else {
- p.buf.WriteString(mapString)
+ p.WriteString(mapString)
}
keys := f.MapKeys()
for i, key := range keys {
if i > 0 {
if p.fmt.sharpV {
- p.buf.WriteString(commaSpaceString)
+ p.WriteString(commaSpaceString)
} else {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
}
p.printValue(key, verb, depth+1)
- p.buf.WriteByte(':')
+ p.WriteByte(':')
p.printValue(f.MapIndex(key), verb, depth+1)
}
if p.fmt.sharpV {
- p.buf.WriteByte('}')
+ p.WriteByte('}')
} else {
- p.buf.WriteByte(']')
+ p.WriteByte(']')
}
case reflect.Struct:
if p.fmt.sharpV {
- p.buf.WriteString(f.Type().String())
+ p.WriteString(f.Type().String())
}
- p.buf.WriteByte('{')
+ p.WriteByte('{')
for i := 0; i < f.NumField(); i++ {
if i > 0 {
if p.fmt.sharpV {
- p.buf.WriteString(commaSpaceString)
+ p.WriteString(commaSpaceString)
} else {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
}
if p.fmt.plusV || p.fmt.sharpV {
if name := f.Type().Field(i).Name; name != "" {
- p.buf.WriteString(name)
- p.buf.WriteByte(':')
+ p.WriteString(name)
+ p.WriteByte(':')
}
}
p.printValue(getField(f, i), verb, depth+1)
}
- p.buf.WriteByte('}')
+ p.WriteByte('}')
case reflect.Interface:
value := f.Elem()
if !value.IsValid() {
if p.fmt.sharpV {
- p.buf.WriteString(f.Type().String())
- p.buf.WriteString(nilParenString)
+ p.WriteString(f.Type().String())
+ p.WriteString(nilParenString)
} else {
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
}
} else {
p.printValue(value, verb, depth+1)
@@ -814,28 +646,28 @@
}
}
if p.fmt.sharpV {
- p.buf.WriteString(f.Type().String())
+ p.WriteString(f.Type().String())
if f.Kind() == reflect.Slice && f.IsNil() {
- p.buf.WriteString(nilParenString)
+ p.WriteString(nilParenString)
return
}
- p.buf.WriteByte('{')
+ p.WriteByte('{')
for i := 0; i < f.Len(); i++ {
if i > 0 {
- p.buf.WriteString(commaSpaceString)
+ p.WriteString(commaSpaceString)
}
p.printValue(f.Index(i), verb, depth+1)
}
- p.buf.WriteByte('}')
+ p.WriteByte('}')
} else {
- p.buf.WriteByte('[')
+ p.WriteByte('[')
for i := 0; i < f.Len(); i++ {
if i > 0 {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
p.printValue(f.Index(i), verb, depth+1)
}
- p.buf.WriteByte(']')
+ p.WriteByte(']')
}
case reflect.Ptr:
// pointer to array or slice or struct? ok at top level
@@ -843,7 +675,7 @@
if depth == 0 && f.Pointer() != 0 {
switch a := f.Elem(); a.Kind() {
case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
- p.buf.WriteByte('&')
+ p.WriteByte('&')
p.printValue(a, verb, depth+1)
return
}
@@ -857,13 +689,13 @@
}
// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type.
-func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) {
- newArgNum = argNum
- if argNum < len(a) {
- num, isInt = a[argNum].(int) // Almost always OK.
+func (p *printer) intFromArg() (num int, isInt bool) {
+ if p.argNum < len(p.args) {
+ arg := p.args[p.argNum]
+ num, isInt = arg.(int) // Almost always OK.
if !isInt {
// Work harder.
- switch v := reflect.ValueOf(a[argNum]); v.Kind() {
+ switch v := reflect.ValueOf(arg); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n := v.Int()
if int64(int(n)) == n {
@@ -880,7 +712,7 @@
// Already 0, false.
}
}
- newArgNum = argNum + 1
+ p.argNum++
if tooLarge(num) {
num = 0
isInt = false
@@ -914,39 +746,38 @@
return 0, 1, false
}
-// argNumber returns the next argument to evaluate, which is either the value of the passed-in
+// updateArgNumber returns the next argument to evaluate, which is either the value of the passed-in
// argNum or the value of the bracketed integer that begins format[i:]. It also returns
// the new value of i, that is, the index of the next byte of the format to process.
-func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
+func (p *printer) updateArgNumber(format string, i int) (newi int, found bool) {
if len(format) <= i || format[i] != '[' {
- return argNum, i, false
+ return i, false
}
p.reordered = true
index, wid, ok := parseArgNumber(format[i:])
- if ok && 0 <= index && index < numArgs {
- return index, i + wid, true
+ if ok && 0 <= index && index < len(p.args) {
+ p.argNum = index
+ return i + wid, true
}
p.goodArgNum = false
- return argNum, i + wid, ok
+ return i + wid, ok
}
-func (p *pp) badArgNum(verb rune) {
- p.buf.WriteString(percentBangString)
- p.buf.WriteRune(verb)
- p.buf.WriteString(badIndexString)
+func (p *printer) badArgNum(verb rune) {
+ p.WriteString(percentBangString)
+ p.WriteRune(verb)
+ p.WriteString(badIndexString)
}
-func (p *pp) missingArg(verb rune) {
- p.buf.WriteString(percentBangString)
- p.buf.WriteRune(verb)
- p.buf.WriteString(missingString)
+func (p *printer) missingArg(verb rune) {
+ p.WriteString(percentBangString)
+ p.WriteRune(verb)
+ p.WriteString(missingString)
}
-func (p *pp) doPrintf(format string, a []interface{}) {
+func (p *printer) doPrintf(format string) {
end := len(format)
- argNum := 0 // we process one argument per non-trivial format
afterIndex := false // previous item in format was an index like [3].
- p.reordered = false
formatLoop:
for i := 0; i < end; {
p.goodArgNum = true
@@ -955,7 +786,7 @@
i++
}
if i > lasti {
- p.buf.WriteString(format[lasti:i])
+ p.WriteString(format[lasti:i])
}
if i >= end {
// done processing format string
@@ -985,7 +816,7 @@
default:
// Fast path for common case of ascii lower case simple verbs
// without precision or width or argument indices.
- if 'a' <= c && c <= 'z' && argNum < len(a) {
+ if 'a' <= c && c <= 'z' && p.argNum < len(p.args) {
if c == 'v' {
// Go syntax
p.fmt.sharpV = p.fmt.sharp
@@ -994,8 +825,8 @@
p.fmt.plusV = p.fmt.plus
p.fmt.plus = false
}
- p.printArg(a[argNum], rune(c))
- argNum++
+ p.printArg(p.Arg(p.argNum), rune(c))
+ p.argNum++
i++
continue formatLoop
}
@@ -1005,15 +836,15 @@
}
// Do we have an explicit argument index?
- argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ i, afterIndex = p.updateArgNumber(format, i)
// Do we have width?
if i < end && format[i] == '*' {
i++
- p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
+ p.fmt.wid, p.fmt.widPresent = p.intFromArg()
if !p.fmt.widPresent {
- p.buf.WriteString(badWidthString)
+ p.WriteString(badWidthString)
}
// We have a negative width, so take its value and ensure
@@ -1037,17 +868,17 @@
if afterIndex { // "%[3].2d"
p.goodArgNum = false
}
- argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ i, afterIndex = p.updateArgNumber(format, i)
if i < end && format[i] == '*' {
i++
- p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
+ p.fmt.prec, p.fmt.precPresent = p.intFromArg()
// Negative precision arguments don't make sense
if p.fmt.prec < 0 {
p.fmt.prec = 0
p.fmt.precPresent = false
}
if !p.fmt.precPresent {
- p.buf.WriteString(badPrecString)
+ p.WriteString(badPrecString)
}
afterIndex = false
} else {
@@ -1060,11 +891,11 @@
}
if !afterIndex {
- argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ i, afterIndex = p.updateArgNumber(format, i)
}
if i >= end {
- p.buf.WriteString(noVerbString)
+ p.WriteString(noVerbString)
break
}
@@ -1073,10 +904,10 @@
switch {
case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
- p.buf.WriteByte('%')
+ p.WriteByte('%')
case !p.goodArgNum:
p.badArgNum(verb)
- case argNum >= len(a): // No argument left over to print for the current verb.
+ case p.argNum >= len(p.args): // No argument left over to print for the current verb.
p.missingArg(verb)
case verb == 'v':
// Go syntax
@@ -1087,40 +918,41 @@
p.fmt.plus = false
fallthrough
default:
- p.printArg(a[argNum], verb)
- argNum++
+ p.printArg(p.args[p.argNum], verb)
+ p.argNum++
}
}
- // Check for extra arguments unless the call accessed the arguments
- // out of order, in which case it's too expensive to detect if they've all
- // been used and arguably OK if they're not.
- if !p.reordered && argNum < len(a) {
+ // Check for extra arguments, but only if there was at least one ordered
+ // argument. Note that this behavior is necessarily different from fmt:
+ // different variants of messages may opt to drop some or all of the
+ // arguments.
+ if !p.reordered && p.argNum < len(p.args) && p.argNum != 0 {
p.fmt.clearflags()
- p.buf.WriteString(extraString)
- for i, arg := range a[argNum:] {
+ p.WriteString(extraString)
+ for i, arg := range p.args[p.argNum:] {
if i > 0 {
- p.buf.WriteString(commaSpaceString)
+ p.WriteString(commaSpaceString)
}
if arg == nil {
- p.buf.WriteString(nilAngleString)
+ p.WriteString(nilAngleString)
} else {
- p.buf.WriteString(reflect.TypeOf(arg).String())
- p.buf.WriteByte('=')
+ p.WriteString(reflect.TypeOf(arg).String())
+ p.WriteByte('=')
p.printArg(arg, 'v')
}
}
- p.buf.WriteByte(')')
+ p.WriteByte(')')
}
}
-func (p *pp) doPrint(a []interface{}) {
+func (p *printer) doPrint(a []interface{}) {
prevString := false
for argNum, arg := range a {
isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
// Add a space between two non-string arguments.
if argNum > 0 && !isString && !prevString {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
p.printArg(arg, 'v')
prevString = isString
@@ -1129,12 +961,12 @@
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
-func (p *pp) doPrintln(a []interface{}) {
+func (p *printer) doPrintln(a []interface{}) {
for argNum, arg := range a {
if argNum > 0 {
- p.buf.WriteByte(' ')
+ p.WriteByte(' ')
}
p.printArg(arg, 'v')
}
- p.buf.WriteByte('\n')
+ p.WriteByte('\n')
}