diff --git a/src/text/tabwriter/tabwriter.go b/src/text/tabwriter/tabwriter.go index 36d999b411..bd45cddecb 100644 --- a/src/text/tabwriter/tabwriter.go +++ b/src/text/tabwriter/tabwriter.go @@ -473,8 +473,12 @@ func (b *Writer) terminateCell(htab bool) int { return len(*line) } -func handlePanic(err *error, op string) { +func (b *Writer) handlePanic(err *error, op string) { if e := recover(); e != nil { + if op == "Flush" { + // If Flush ran into a panic, we still need to reset. + b.reset() + } if nerr, ok := e.(osError); ok { *err = nerr.err return @@ -491,10 +495,17 @@ func (b *Writer) Flush() error { return b.flush() } +// flush is the internal version of Flush, with a named return value which we +// don't want to expose. func (b *Writer) flush() (err error) { - defer b.reset() // even in the presence of errors - defer handlePanic(&err, "Flush") + defer b.handlePanic(&err, "Flush") + return b.flushNoDefers() +} +// flushNoDefers is like flush, but without a deferred handlePanic call. This +// can be called from other methods which already have their own deferred +// handlePanic calls, such as Write, and avoid the extra defer work. +func (b *Writer) flushNoDefers() (err error) { // add current cell if not empty if b.cell.size > 0 { if b.endChar != 0 { @@ -506,6 +517,7 @@ func (b *Writer) flush() (err error) { // format contents of buffer b.format(0, 0, len(b.lines)) + b.reset() return nil } @@ -516,7 +528,7 @@ var hbar = []byte("---\n") // while writing to the underlying output stream. // func (b *Writer) Write(buf []byte) (n int, err error) { - defer handlePanic(&err, "Write") + defer b.handlePanic(&err, "Write") // split text into cells n = 0 @@ -539,7 +551,7 @@ func (b *Writer) Write(buf []byte) (n int, err error) { // the formatting of the following lines (the last cell per // line is ignored by format()), thus we can flush the // Writer contents. - if err = b.Flush(); err != nil { + if err = b.flushNoDefers(); err != nil { return } if ch == '\f' && b.flags&Debug != 0 { diff --git a/src/text/tabwriter/tabwriter_test.go b/src/text/tabwriter/tabwriter_test.go index 07bae0ca0b..6a97d4c427 100644 --- a/src/text/tabwriter/tabwriter_test.go +++ b/src/text/tabwriter/tabwriter_test.go @@ -729,3 +729,27 @@ func BenchmarkRagged(b *testing.B) { }) } } + +const codeSnippet = ` +some command + +foo # aligned +barbaz # comments + +but +mostly +single +cell +lines +` + +func BenchmarkCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings + // The code is small, so it's reasonable for the tabwriter user + // to write it all at once, or buffer the writes. + w.Write([]byte(codeSnippet)) + w.Flush() + } +}