| package progress // import "github.com/docker/docker/pkg/progress" |
| |
| import ( |
| "io" |
| "time" |
| |
| "golang.org/x/time/rate" |
| ) |
| |
| // Reader is a Reader with progress bar. |
| type Reader struct { |
| in io.ReadCloser // Stream to read from |
| out Output // Where to send progress bar to |
| size int64 |
| current int64 |
| lastUpdate int64 |
| id string |
| action string |
| rateLimiter *rate.Limiter |
| } |
| |
| // NewProgressReader creates a new ProgressReader. |
| func NewProgressReader(in io.ReadCloser, out Output, size int64, id, action string) *Reader { |
| return &Reader{ |
| in: in, |
| out: out, |
| size: size, |
| id: id, |
| action: action, |
| rateLimiter: rate.NewLimiter(rate.Every(100*time.Millisecond), 1), |
| } |
| } |
| |
| func (p *Reader) Read(buf []byte) (n int, err error) { |
| read, err := p.in.Read(buf) |
| p.current += int64(read) |
| updateEvery := int64(1024 * 512) // 512kB |
| if p.size > 0 { |
| // Update progress for every 1% read if 1% < 512kB |
| if increment := int64(0.01 * float64(p.size)); increment < updateEvery { |
| updateEvery = increment |
| } |
| } |
| if p.current-p.lastUpdate > updateEvery || err != nil { |
| p.updateProgress(err != nil && read == 0) |
| p.lastUpdate = p.current |
| } |
| |
| return read, err |
| } |
| |
| // Close closes the progress reader and its underlying reader. |
| func (p *Reader) Close() error { |
| if p.current < p.size { |
| // print a full progress bar when closing prematurely |
| p.current = p.size |
| p.updateProgress(false) |
| } |
| return p.in.Close() |
| } |
| |
| func (p *Reader) updateProgress(last bool) { |
| if last || p.current == p.size || p.rateLimiter.Allow() { |
| p.out.WriteProgress(Progress{ID: p.id, Action: p.action, Current: p.current, Total: p.size, LastUpdate: last}) |
| } |
| } |