]> Cypherpunks repositories - gostls13.git/commitdiff
- interpret form feed char as newline + flush
authorRobert Griesemer <gri@golang.org>
Thu, 4 Jun 2009 22:47:57 +0000 (15:47 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 4 Jun 2009 22:47:57 +0000 (15:47 -0700)
- cleanups:
  - replaced internal byte buffer implementation with io.ByteBuffer (now that we have one)
  - removed all uses of goto statements in favor of structured code
  - converted tests into a table-driven test

R=r
DELTA=277  (48 added, 67 deleted, 162 changed)
OCL=29890
CL=29901

src/lib/tabwriter/tabwriter.go
src/lib/tabwriter/tabwriter_test.go

index 88da7da2b67df2c6c13addaa03d693c62ae36569..6799f72d1089599f443bd996494e3d5d4d5fd403 100644 (file)
@@ -10,7 +10,6 @@
 package tabwriter
 
 import (
-       "bytes";
        "container/vector";
        "io";
        "os";
@@ -18,57 +17,6 @@ import (
 )
 
 
-// ----------------------------------------------------------------------------
-// Basic byteArray support
-
-type byteArray struct {
-       a []byte;
-}
-
-
-func (b *byteArray) init(initial_size int) {
-       b.a = make([]byte, initial_size)[0 : 0];
-}
-
-
-func (b *byteArray) len() int {
-       return len(b.a);
-}
-
-
-func (b *byteArray) clear() {
-       b.a = b.a[0 : 0];
-}
-
-
-func (b *byteArray) slice(i, j int) []byte {
-       return b.a[i : j];  // BUG should really be &b.a[i : j]
-}
-
-
-func (b *byteArray) append(s []byte) {
-       a := b.a;
-       n := len(a);
-       m := n + len(s);
-
-       if m > cap(a) {
-               n2 := 2*n;
-               if m > n2 {
-                       n2 = m;
-               }
-               b := make([]byte, n2);
-               bytes.Copy(b, a);
-               a = b;
-       }
-
-       a = a[0 : m];
-       for i := len(s) - 1; i >= 0; i-- {
-               a[n + i] = s[i];
-       }
-       b.a = a;
-}
-
-
 // ----------------------------------------------------------------------------
 // Filter implementation
 
@@ -88,6 +36,16 @@ func (b *byteArray) append(s []byte) {
 // this may not be true in some fonts, especially with certain
 // UTF-8 characters.
 //
+// If a Writer is configured to filter HTML, HTML tags and entities
+// are simply passed through and their widths are assumed to be zero
+// for formatting purposes.
+//
+// The form feed character ('\f') acts like a newline but it also
+// terminates all columns in the current line (effectively calling
+// Flush). Cells in the next line start new columns. Unless found
+// inside an HTML tag, form feed characters appear as newlines in
+// the output.
+//
 // The Writer must buffer input internally, because proper spacing
 // of one line may depend on the cells in future lines. Clients must
 // call Flush when done calling Write.
@@ -102,7 +60,7 @@ type Writer struct {
 
        // current state
        html_char byte;  // terminating char of html tag/entity, or 0 ('>', ';', or 0)
-       buf byteArray;  // collected text w/o tabs and newlines
+       buf io.ByteBuffer;  // collected text w/o tabs, newlines, or form feed chars
        size int;  // size of incomplete cell in bytes
        width int;  // width of incomplete cell in runes up to buf[pos] w/o ignored sections
        pos int;  // buffer position up to which width of incomplete cell has been computed
@@ -114,7 +72,7 @@ type Writer struct {
 
 // Internal representation (current state):
 //
-// - all text written is appended to buf; tabs and newlines are stripped away
+// - all text written is appended to buf; form feed chars, tabs and newlines are stripped away
 // - at any given time there is a (possibly empty) incomplete cell at the end
 //   (the cell starts after a tab or newline)
 // - size is the number of bytes belonging to the cell so far
@@ -182,12 +140,10 @@ func (b *Writer) Init(output io.Writer, cellwidth, padding int, padchar byte, fl
        }
        if padchar == '\t' {
                // tab enforces left-alignment
-               t := ^AlignRight;  // TODO 6g bug
-               flags &= uint(t);
+               flags &^= AlignRight;
        }
        b.flags = flags;
 
-       b.buf.init(1024);
        b.lines_size.Init(0);
        b.lines_width.Init(0);
        b.widths.Init(0);
@@ -204,7 +160,8 @@ func (b *Writer) line(i int) (*vector.IntVector, *vector.IntVector) {
 }
 
 
-// debugging support
+// debugging support (keep code around)
+/*
 func (b *Writer) dump() {
        pos := 0;
        for i := 0; i < b.lines_size.Len(); i++ {
@@ -219,6 +176,7 @@ func (b *Writer) dump() {
        }
        print("\n");
 }
+*/
 
 
 func (b *Writer) write0(buf []byte) os.Error {
@@ -232,7 +190,7 @@ func (b *Writer) write0(buf []byte) os.Error {
 
 var newline = []byte{'\n'}
 
-func (b *Writer) writePadding(textw, cellw int) (err os.Error) {
+func (b *Writer) writePadding(textw, cellw int) os.Error {
        if b.padbytes[0] == '\t' {
                // make cell width a multiple of cellwidth
                cellw = ((cellw + b.cellwidth - 1) / b.cellwidth) * b.cellwidth;
@@ -248,21 +206,18 @@ func (b *Writer) writePadding(textw, cellw int) (err os.Error) {
        }
 
        for n > len(b.padbytes) {
-               err = b.write0(&b.padbytes);
-               if err != nil {
-                       goto exit;
+               if err := b.write0(&b.padbytes); err != nil {
+                       return err;
                }
                n -= len(b.padbytes);
        }
-       err = b.write0(b.padbytes[0 : n]);
 
-exit:
-       return err;
+       return b.write0(b.padbytes[0 : n]);
 }
 
 
-func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int, err os.Error) {
-       pos = pos0;
+func (b *Writer) writeLines(pos0 int, line0, line1 int) (int, os.Error) {
+       pos := pos0;
        for i := line0; i < line1; i++ {
                line_size, line_width := b.line(i);
                for j := 0; j < line_size.Len(); j++ {
@@ -271,29 +226,25 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int, err os.Error)
                        switch {
                        default: // align left
 
-                               err = b.write0(b.buf.slice(pos, pos + s));
-                               if err != nil {
-                                       goto exit;
+                               if err := b.write0(b.buf.Data()[pos : pos + s]); err != nil {
+                                       return pos, err;
                                }
                                pos += s;
                                if j < b.widths.Len() {
-                                       err = b.writePadding(w, b.widths.At(j));
-                                       if err != nil {
-                                               goto exit;
+                                       if err := b.writePadding(w, b.widths.At(j)); err != nil {
+                                               return pos, err;
                                        }
                                }
 
                        case b.flags & AlignRight != 0:  // align right
 
                                if j < b.widths.Len() {
-                                       err = b.writePadding(w, b.widths.At(j));
-                                       if err != nil {
-                                               goto exit;
+                                       if err := b.writePadding(w, b.widths.At(j)); err != nil {
+                                               return pos, err;
                                        }
                                }
-                               err = b.write0(b.buf.slice(pos, pos + s));
-                               if err != nil {
-                                       goto exit;
+                               if err := b.write0(b.buf.Data()[pos : pos + s]); err != nil {
+                                       return pos, err;
                                }
                                pos += s;
                        }
@@ -302,19 +253,18 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int, err os.Error)
                if i+1 == b.lines_size.Len() {
                        // last buffered line - we don't have a newline, so just write
                        // any outstanding buffered data
-                       err = b.write0(b.buf.slice(pos, pos + b.size));
+                       if err := b.write0(b.buf.Data()[pos : pos + b.size]); err != nil {
+                               return pos, err;
+                       }
                        pos += b.size;
                } else {
                        // not the last line - write newline
-                       err = b.write0(newline);
-               }
-               if err != nil {
-                       goto exit;
+                       if err := b.write0(newline); err != nil {
+                               return pos, err;
+                       }
                }
        }
-
-exit:
-       return pos, err;
+       return pos, nil;
 }
 
 
@@ -332,7 +282,7 @@ func (b *Writer) format(pos0 int, line0, line1 int) (pos int, err os.Error) {
                        // print unprinted lines until beginning of block
                        pos, err = b.writeLines(pos, last, this);
                        if err != nil {
-                               goto exit;
+                               return pos, err;
                        }
                        last = this;
 
@@ -362,10 +312,7 @@ func (b *Writer) format(pos0 int, line0, line1 int) (pos int, err os.Error) {
        }
 
        // print unprinted lines until end
-       pos, err = b.writeLines(pos, last, line1);
-
-exit:
-       return pos, err;
+       return b.writeLines(pos, last, line1);
 }
 
 
@@ -373,9 +320,9 @@ exit:
 // that any data buffered in the Writer is written to output.
 //
 func (b *Writer) Flush() os.Error {
-       dummy, err := b.format(0, 0, b.lines_size.Len());
+       _, err := b.format(0, 0, b.lines_size.Len());
        // reset (even in the presence of errors)
-       b.buf.clear();
+       b.buf.Reset();
        b.size, b.width = 0, 0;
        b.pos = 0;
        b.lines_size.Init(0);
@@ -401,7 +348,7 @@ func unicodeLen(buf []byte) int {
 
 
 func (b *Writer) append(buf []byte) {
-       b.buf.append(buf);
+       b.buf.Write(buf);
        b.size += len(buf);
 }
 
@@ -420,11 +367,11 @@ func (b *Writer) Write(buf []byte) (written int, err os.Error) {
                if b.html_char == 0 {
                        // outside html tag/entity
                        switch ch {
-                       case '\t', '\n':
+                       case '\t', '\n', '\f':
                                b.append(buf[i0 : i]);
                                i0 = i + 1;  // exclude ch from (next) cell
-                               b.width += unicodeLen(b.buf.slice(b.pos, b.buf.len()));
-                               b.pos = b.buf.len();
+                               b.width += unicodeLen(b.buf.Data()[b.pos : b.buf.Len()]);
+                               b.pos = b.buf.Len();
 
                                // terminate cell
                                last_size, last_width := b.line(b.lines_size.Len() - 1);
@@ -432,15 +379,16 @@ func (b *Writer) Write(buf []byte) (written int, err os.Error) {
                                last_width.Push(b.width);
                                b.size, b.width = 0, 0;
 
-                               if ch == '\n' {
+                               if ch != '\t' {
+                                       // terminate line
                                        b.addLine();
-                                       if last_size.Len() == 1 {
-                                               // The previous line has only one cell which does not have
-                                               // an impact on the formatting of the following lines (the
-                                               // last cell per line is ignored by format()), thus we can
-                                               // flush the Writer contents.
-                                               err = b.Flush();
-                                               if err != nil {
+                                       if ch == '\f' || last_size.Len() == 1 {
+                                               // A '\f' always forces a flush. Otherwise, if the previous
+                                               // line has only one cell which does not have an impact on
+                                               // 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 {
                                                        return i0, err;
                                                }
                                        }
@@ -450,7 +398,7 @@ func (b *Writer) Write(buf []byte) (written int, err os.Error) {
                                if b.flags & FilterHTML != 0 {
                                        b.append(buf[i0 : i]);
                                        i0 = i;
-                                       b.width += unicodeLen(b.buf.slice(b.pos, b.buf.len()));
+                                       b.width += unicodeLen(b.buf.Data()[b.pos : b.buf.Len()]);
                                        b.pos = -1;  // preventative - should not be used (will cause index out of bounds)
                                        if ch == '<' {
                                                b.html_char = '>';
@@ -469,7 +417,7 @@ func (b *Writer) Write(buf []byte) (written int, err os.Error) {
                                if b.html_char == ';' {
                                        b.width++;  // count as one char
                                }
-                               b.pos = b.buf.len();
+                               b.pos = b.buf.Len();
                                b.html_char = 0;
                        }
                }
index b4cc610a0ad7edaefe5a4260bab2221f4b8f9562..7026446e62be3f92d36ccbe5a6662a9a3e821c1d 100644 (file)
@@ -103,132 +103,158 @@ func check(t *testing.T, tabwidth, padding int, padchar byte, flags uint, src, e
 }
 
 
-func Test(t *testing.T) {
-       check(
-               t, 8, 1, '.', 0,
+type entry struct {
+       tabwidth, padding int;
+       padchar byte;
+       flags uint;
+       src, expected string;
+}
+
+
+var tests = []entry {
+       entry{
+               8, 1, '.', 0,
                "",
                ""
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "\n\n\n",
                "\n\n\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "a\nb\nc",
                "a\nb\nc"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "\t",  // '\t' terminates an empty cell on last line - nothing to print
                ""
-       );
+       },
 
-       check(
-               t, 8, 1, '.', tabwriter.AlignRight,
+       entry{
+               8, 1, '.', tabwriter.AlignRight,
                "\t",  // '\t' terminates an empty cell on last line - nothing to print
                ""
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "*\t*",
                "**"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "*\t*\n",
                "*.......*\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "*\t*\t",
                "*.......*"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', tabwriter.AlignRight,
+       entry{
+               8, 1, '.', tabwriter.AlignRight,
                "*\t*\t",
                ".......**"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "\t\n",
                "........\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "a) foo",
                "a) foo"
-       );
+       },
 
-       check(
-               t, 8, 1, ' ', 0,
+       entry{
+               8, 1, ' ', 0,
                "b) foo\tbar",  // "bar" is not in any cell - not formatted, just flushed
                "b) foobar"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "c) foo\tbar\t",
                "c) foo..bar"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "d) foo\tbar\n",
                "d) foo..bar\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "e) foo\tbar\t\n",
                "e) foo..bar.....\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', tabwriter.FilterHTML,
-               "e) f&lt;o\t<b>bar</b>\t\n",
-               "e) f&lt;o..<b>bar</b>.....\n"
-       );
+       entry{
+               8, 1, '.', tabwriter.FilterHTML,
+               "f) f&lt;o\t<b>bar</b>\t\n",
+               "f) f&lt;o..<b>bar</b>.....\n"
+       },
 
-       check(
-               t, 8, 1, '*', 0,
+       entry{
+               8, 1, '*', 0,
                "Hello, world!\n",
                "Hello, world!\n"
-       );
+       },
 
-       check(
-               t, 0, 0, '.', 0,
+       entry{
+               0, 0, '.', 0,
                "1\t2\t3\t4\n"
                "11\t222\t3333\t44444\n",
 
                "1.2..3...4\n"
                "11222333344444\n"
-       );
+       },
 
-       check(
-               t, 5, 0, '.', 0,
+       entry{
+               0, 0, '.', tabwriter.FilterHTML,
+               "1\t2<!---\f--->\t3\t4\n"  // \f inside HTML is ignored
+               "11\t222\t3333\t44444\n",
+
+               "1.2<!---\f--->..3...4\n"
+               "11222333344444\n"
+       },
+
+       entry{
+               0, 0, '.', 0,
+               "1\t2\t3\t4\f"  // \f causes a newline and flush
+               "11\t222\t3333\t44444\n",
+
+               "1234\n"
+               "11222333344444\n"
+       },
+
+       entry{
+               5, 0, '.', 0,
                "1\t2\t3\t4\n",
                "1....2....3....4\n"
-       );
+       },
 
-       check(
-               t, 5, 0, '.', 0,
+       entry{
+               5, 0, '.', 0,
                "1\t2\t3\t4\t\n",
                "1....2....3....4....\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '.', 0,
+       entry{
+               8, 1, '.', 0,
                "本\tb\tc\n"
                "aa\t\u672c\u672c\u672c\tcccc\tddddd\n"
                "aaa\tbbbb\n",
@@ -236,10 +262,10 @@ func Test(t *testing.T) {
                "本.......b.......c\n"
                "aa......本本本.....cccc....ddddd\n"
                "aaa.....bbbb\n"
-       );
+       },
 
-       check(
-               t, 8, 1, ' ', tabwriter.AlignRight,
+       entry{
+               8, 1, ' ', tabwriter.AlignRight,
                "a\tè\tc\t\n"
                "aa\tèèè\tcccc\tddddd\t\n"
                "aaa\tèèèè\t\n",
@@ -247,10 +273,10 @@ func Test(t *testing.T) {
                "       a       è       c\n"
                "      aa     èèè    cccc   ddddd\n"
                "     aaa    èèèè\n"
-       );
+       },
 
-       check(
-               t, 2, 0, ' ', 0,
+       entry{
+               2, 0, ' ', 0,
                "a\tb\tc\n"
                "aa\tbbb\tcccc\n"
                "aaa\tbbbb\n",
@@ -258,10 +284,10 @@ func Test(t *testing.T) {
                "a  b  c\n"
                "aa bbbcccc\n"
                "aaabbbb\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '_', 0,
+       entry{
+               8, 1, '_', 0,
                "a\tb\tc\n"
                "aa\tbbb\tcccc\n"
                "aaa\tbbbb\n",
@@ -269,10 +295,10 @@ func Test(t *testing.T) {
                "a_______b_______c\n"
                "aa______bbb_____cccc\n"
                "aaa_____bbbb\n"
-       );
+       },
 
-       check(
-               t, 4, 1, '-', 0,
+       entry{
+               4, 1, '-', 0,
                "4444\t日本語\t22\t1\t333\n"
                "999999999\t22\n"
                "7\t22\n"
@@ -288,10 +314,10 @@ func Test(t *testing.T) {
                "\n"
                "666666-666666-666666----4444\n"
                "1------1------999999999-0000000000\n"
-       );
+       },
 
-       check(
-               t, 4, 3, '.', 0,
+       entry{
+               4, 3, '.', 0,
                "4444\t333\t22\t1\t333\n"
                "999999999\t22\n"
                "7\t22\n"
@@ -307,10 +333,10 @@ func Test(t *testing.T) {
                "\n"
                "666666...666666...666666......4444\n"
                "1........1........999999999...0000000000\n"
-       );
+       },
 
-       check(
-               t, 8, 1, '\t', tabwriter.FilterHTML,
+       entry{
+               8, 1, '\t', tabwriter.FilterHTML,
                "4444\t333\t22\t1\t333\n"
                "999999999\t22\n"
                "7\t22\n"
@@ -326,10 +352,10 @@ func Test(t *testing.T) {
                "\n"
                "666666\t666666\t666666\t\t4444\n"
                "1\t1\t<font color=red attr=日本語>999999999</font>\t0000000000\n"
-       );
+       },
 
-       check(
-               t, 0, 2, ' ', tabwriter.AlignRight,
+       entry{
+               0, 2, ' ', tabwriter.AlignRight,
                ".0\t.3\t2.4\t-5.1\t\n"
                "23.0\t12345678.9\t2.4\t-989.4\t\n"
                "5.1\t12.0\t2.4\t-7.0\t\n"
@@ -343,5 +369,12 @@ func Test(t *testing.T) {
                "    .0         0.0  332.0  8908.0\n"
                "    .0         -.3  456.4    22.1\n"
                "    .0         1.2   44.4   -13.3"
-       );
+       },
+}
+
+
+func Test(t *testing.T) {
+       for _, e := range tests {
+               check(t, e.tabwidth, e.padding, e.padchar, e.flags, e.src, e.expected);
+       }
 }