package decor import ( "fmt" "io" "math" "time" "github.com/VividCortex/ewma" ) // SpeedFormatter is wrapper for SizeB1024 and SizeB1000 to format value as speed/s. type SpeedFormatter struct { fmt.Formatter } func (self *SpeedFormatter) Format(st fmt.State, verb rune) { self.Formatter.Format(st, verb) io.WriteString(st, "/s") } // EwmaSpeed exponential-weighted-moving-average based speed decorator. // Note that it's necessary to supply bar.Incr* methods with incremental // work duration as second argument, in order for this decorator to // work correctly. This decorator is a wrapper of MovingAverageSpeed. func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator { var average MovingAverage if age == 0 { average = ewma.NewMovingAverage() } else { average = ewma.NewMovingAverage(age) } return MovingAverageSpeed(unit, format, average, wcc...) } // MovingAverageSpeed decorator relies on MovingAverage implementation // to calculate its average. // // `unit` one of [0|UnitKiB|UnitKB] zero for no unit // // `format` printf compatible verb for value, like "%f" or "%d" // // `average` MovingAverage implementation // // `wcc` optional WC config // // format examples: // // unit=UnitKiB, format="%.1f" output: "1.0MiB/s" // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s" // unit=UnitKB, format="%.1f" output: "1.0MB/s" // unit=UnitKB, format="% .1f" output: "1.0 MB/s" // func MovingAverageSpeed(unit int, format string, average MovingAverage, wcc ...WC) Decorator { var wc WC for _, widthConf := range wcc { wc = widthConf } if format == "" { format = "%.0f" } d := &movingAverageSpeed{ WC: wc.Init(), average: average, producer: chooseSpeedProducer(unit, format), } return d } type movingAverageSpeed struct { WC producer func(float64) string average ewma.MovingAverage msg string } func (d *movingAverageSpeed) Decor(st *Statistics) string { if !st.Completed { var speed float64 if v := d.average.Value(); v > 0 { speed = 1 / v } d.msg = d.producer(speed * 1e9) } return d.FormatMsg(d.msg) } func (d *movingAverageSpeed) NextAmount(n int64, wdd ...time.Duration) { var workDuration time.Duration for _, wd := range wdd { workDuration = wd } durPerByte := float64(workDuration) / float64(n) if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { return } d.average.Add(durPerByte) } // AverageSpeed decorator with dynamic unit measure adjustment. It's // a wrapper of NewAverageSpeed. func AverageSpeed(unit int, format string, wcc ...WC) Decorator { return NewAverageSpeed(unit, format, time.Now(), wcc...) } // NewAverageSpeed decorator with dynamic unit measure adjustment and // user provided start time. // // `unit` one of [0|UnitKiB|UnitKB] zero for no unit // // `format` printf compatible verb for value, like "%f" or "%d" // // `startTime` start time // // `wcc` optional WC config // // format examples: // // unit=UnitKiB, format="%.1f" output: "1.0MiB/s" // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s" // unit=UnitKB, format="%.1f" output: "1.0MB/s" // unit=UnitKB, format="% .1f" output: "1.0 MB/s" // func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator { var wc WC for _, widthConf := range wcc { wc = widthConf } if format == "" { format = "%.0f" } d := &averageSpeed{ WC: wc.Init(), startTime: startTime, producer: chooseSpeedProducer(unit, format), } return d } type averageSpeed struct { WC startTime time.Time producer func(float64) string msg string } func (d *averageSpeed) Decor(st *Statistics) string { if !st.Completed { speed := float64(st.Current) / float64(time.Since(d.startTime)) d.msg = d.producer(speed * 1e9) } return d.FormatMsg(d.msg) } func (d *averageSpeed) AverageAdjust(startTime time.Time) { d.startTime = startTime } func chooseSpeedProducer(unit int, format string) func(float64) string { switch unit { case UnitKiB: return func(speed float64) string { return fmt.Sprintf(format, &SpeedFormatter{SizeB1024(math.Round(speed))}) } case UnitKB: return func(speed float64) string { return fmt.Sprintf(format, &SpeedFormatter{SizeB1000(math.Round(speed))}) } default: return func(speed float64) string { return fmt.Sprintf(format, speed) } } }