utilize bytes.Buffer.ReadFrom
Vladimir Bauer
8 years ago
| 3 | 3 | "bytes" |
| 4 | 4 | "fmt" |
| 5 | 5 | "io" |
| 6 | "strings" | |
| 6 | 7 | "sync" |
| 7 | 8 | "time" |
| 8 | 9 | "unicode/utf8" |
| 67 | 68 | char rune |
| 68 | 69 | till int64 |
| 69 | 70 | } |
| 70 | writeBuf struct { | |
| 71 | buf []byte | |
| 72 | completeAfterFlush bool | |
| 71 | bufReader struct { | |
| 72 | io.Reader | |
| 73 | complete bool | |
| 73 | 74 | } |
| 74 | 75 | ) |
| 75 | 76 | |
| 285 | 286 | } |
| 286 | 287 | } |
| 287 | 288 | |
| 288 | func (b *Bar) render(tw int, prependWs, appendWs *widthSync) <-chan *writeBuf { | |
| 289 | ch := make(chan *writeBuf, 1) | |
| 289 | func (b *Bar) render(tw int, prependWs, appendWs *widthSync) <-chan *bufReader { | |
| 290 | ch := make(chan *bufReader, 1) | |
| 290 | 291 | |
| 291 | 292 | go func() { |
| 292 | 293 | select { |
| 298 | 299 | s.prependFuncs = nil |
| 299 | 300 | s.appendFuncs = nil |
| 300 | 301 | |
| 301 | ch <- &writeBuf{[]byte(s.panic), true} | |
| 302 | ch <- &bufReader{strings.NewReader(s.panic), true} | |
| 302 | 303 | } |
| 303 | 304 | close(ch) |
| 304 | 305 | }() |
| 305 | 306 | s.draw(tw, prependWs, appendWs) |
| 306 | ch <- &writeBuf{s.toBytes(), s.completed} | |
| 307 | ch <- &bufReader{io.MultiReader(s.bufP, s.bufB, s.bufA), s.completed} | |
| 307 | 308 | }: |
| 308 | 309 | case <-b.done: |
| 309 | 310 | s := b.cacheState |
| 310 | var buf []byte | |
| 311 | var r io.Reader | |
| 311 | 312 | if s.panic != "" { |
| 312 | buf = []byte(s.panic) | |
| 313 | r = strings.NewReader(s.panic) | |
| 313 | 314 | } else { |
| 314 | 315 | s.draw(tw, prependWs, appendWs) |
| 315 | buf = s.toBytes() | |
| 316 | r = io.MultiReader(s.bufP, s.bufB, s.bufA) | |
| 316 | 317 | } |
| 317 | ch <- &writeBuf{buf, false} | |
| 318 | ch <- &bufReader{r, false} | |
| 318 | 319 | close(ch) |
| 319 | 320 | } |
| 320 | 321 | }() |
| 321 | 322 | |
| 322 | 323 | return ch |
| 323 | } | |
| 324 | ||
| 325 | func (s *state) toBytes() []byte { | |
| 326 | buf := make([]byte, 0, s.bufP.Len()+s.bufB.Len()+s.bufA.Len()) | |
| 327 | buf = concatenateBlocks(buf, s.bufP.Bytes(), s.bufB.Bytes(), s.bufA.Bytes()) | |
| 328 | return buf | |
| 329 | 324 | } |
| 330 | 325 | |
| 331 | 326 | func (s *state) updateTimePerItemEstimate(amount int, now, next time.Time) { |
| 432 | 427 | } |
| 433 | 428 | } |
| 434 | 429 | |
| 435 | func concatenateBlocks(buf []byte, blocks ...[]byte) []byte { | |
| 436 | for _, block := range blocks { | |
| 437 | buf = append(buf, block...) | |
| 438 | } | |
| 439 | return buf | |
| 440 | } | |
| 441 | ||
| 442 | 430 | func (s *state) updateFormat(format string) { |
| 443 | 431 | for i, n := 0, 0; len(format) > 0; i++ { |
| 444 | 432 | s.format[i], n = utf8.DecodeRuneInString(format) |
| 17 | 17 | // Writer is a buffered the writer that updates the terminal. |
| 18 | 18 | // The contents of writer will be flushed when Flush is called. |
| 19 | 19 | type Writer struct { |
| 20 | out io.Writer | |
| 21 | ||
| 20 | out io.Writer | |
| 22 | 21 | buf bytes.Buffer |
| 23 | 22 | lineCount int |
| 24 | 23 | } |
| 25 | 24 | |
| 26 | 25 | // New returns a new Writer with defaults |
| 27 | 26 | func New(w io.Writer) *Writer { |
| 28 | return &Writer{ | |
| 29 | out: w, | |
| 30 | } | |
| 27 | return &Writer{out: w} | |
| 31 | 28 | } |
| 32 | 29 | |
| 33 | 30 | // Flush flushes the underlying buffer |
| 34 | 31 | func (w *Writer) Flush() error { |
| 35 | // Do nothing if buffer is empty | |
| 36 | if w.buf.Len() == 0 { | |
| 37 | return nil | |
| 32 | err := w.clearLines() | |
| 33 | w.lineCount = bytes.Count(w.buf.Bytes(), []byte("\n")) | |
| 34 | // WriteTo takes care of w.buf.Reset | |
| 35 | if _, e := w.buf.WriteTo(w.out); err == nil { | |
| 36 | err = e | |
| 38 | 37 | } |
| 39 | w.clearLines() | |
| 40 | w.lineCount = bytes.Count(w.buf.Bytes(), []byte("\n")) | |
| 41 | _, err := w.out.Write(w.buf.Bytes()) | |
| 42 | w.buf.Reset() | |
| 43 | 38 | return err |
| 44 | 39 | } |
| 45 | 40 | |
| 46 | // Write save the contents of b to its buffers. The only errors returned are ones encountered while writing to the underlying buffer. | |
| 47 | func (w *Writer) Write(b []byte) (n int, err error) { | |
| 48 | return w.buf.Write(b) | |
| 41 | // Write appends the contents of p to the underlying buffer | |
| 42 | func (w *Writer) Write(p []byte) (n int, err error) { | |
| 43 | return w.buf.Write(p) | |
| 49 | 44 | } |
| 50 | 45 | |
| 46 | // WriteString writes string to the underlying buffer | |
| 51 | 47 | func (w *Writer) WriteString(s string) (n int, err error) { |
| 52 | 48 | return w.buf.WriteString(s) |
| 53 | 49 | } |
| 50 | ||
| 51 | // ReadFrom reads from the provided io.Reader and writes to the underlying buffer. | |
| 52 | func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { | |
| 53 | return w.buf.ReadFrom(r) | |
| 54 | } |
| 2 | 2 | package cwriter |
| 3 | 3 | |
| 4 | 4 | import ( |
| 5 | "fmt" | |
| 5 | "io" | |
| 6 | 6 | "strings" |
| 7 | 7 | "syscall" |
| 8 | 8 | "unsafe" |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | func (w *Writer) clearLines() { | |
| 12 | fmt.Fprint(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) | |
| 11 | func (w *Writer) clearLines() error { | |
| 12 | _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) | |
| 13 | return err | |
| 13 | 14 | } |
| 14 | 15 | |
| 15 | 16 | // TermSize returns the dimensions of the given terminal. |
| 2 | 2 | package cwriter |
| 3 | 3 | |
| 4 | 4 | import ( |
| 5 | "fmt" | |
| 6 | 5 | "io" |
| 6 | "strings" | |
| 7 | 7 | "syscall" |
| 8 | 8 | "unsafe" |
| 9 | "strings" | |
| 9 | ||
| 10 | 10 | "github.com/mattn/go-isatty" |
| 11 | 11 | ) |
| 12 | 12 | |
| 49 | 49 | Fd() uintptr |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | func (w *Writer) clearLines() { | |
| 52 | func (w *Writer) clearLines() error { | |
| 53 | 53 | f, ok := w.out.(FdWriter) |
| 54 | 54 | if ok && !isatty.IsTerminal(f.Fd()) { |
| 55 | fmt.Fprint(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) | |
| 56 | return | |
| 55 | _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) | |
| 56 | return err | |
| 57 | 57 | } |
| 58 | 58 | fd := f.Fd() |
| 59 | 59 | var info consoleScreenBufferInfo |
| 72 | 72 | count = dword(info.size.x) |
| 73 | 73 | procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) |
| 74 | 74 | } |
| 75 | return nil | |
| 75 | 76 | } |
| 76 | 77 | |
| 77 | // GetTermSize returns the dimensions of the given terminal. | |
| 78 | // TermSize returns the dimensions of the given terminal. | |
| 78 | 79 | // the code is stolen from "golang.org/x/crypto/ssh/terminal" |
| 79 | func GetTermSize() (width, height int, err error) { | |
| 80 | func TermSize() (width, height int, err error) { | |
| 80 | 81 | var info consoleScreenBufferInfo |
| 81 | 82 | _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) |
| 82 | 83 | if e != 0 { |
| 294 | 294 | prependWs := newWidthSync(wSyncTimeout, len(p.bars), numP) |
| 295 | 295 | appendWs := newWidthSync(wSyncTimeout, len(p.bars), numA) |
| 296 | 296 | |
| 297 | sequence := make([]<-chan *writeBuf, len(p.bars)) | |
| 297 | sequence := make([]<-chan *bufReader, len(p.bars)) | |
| 298 | 298 | for i, b := range p.bars { |
| 299 | 299 | sequence[i] = b.render(tw, prependWs, appendWs) |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | 302 | var i int |
| 303 | for b := range fanIn(sequence...) { | |
| 304 | _, err = p.cw.Write(b.buf) | |
| 303 | for r := range fanIn(sequence...) { | |
| 304 | _, err = p.cw.ReadFrom(r) | |
| 305 | 305 | defer func(bar *Bar, complete bool) { |
| 306 | 306 | if complete { |
| 307 | 307 | bar.Complete() |
| 308 | 308 | } |
| 309 | }(p.bars[i], b.completeAfterFlush) | |
| 309 | }(p.bars[i], r.complete) | |
| 310 | 310 | i++ |
| 311 | 311 | } |
| 312 | 312 | |
| 320 | 320 | return |
| 321 | 321 | } |
| 322 | 322 | |
| 323 | func fanIn(inputs ...<-chan *writeBuf) <-chan *writeBuf { | |
| 324 | ch := make(chan *writeBuf) | |
| 323 | func fanIn(inputs ...<-chan *bufReader) <-chan *bufReader { | |
| 324 | ch := make(chan *bufReader) | |
| 325 | 325 | |
| 326 | 326 | go func() { |
| 327 | 327 | defer close(ch) |