book-fetch/vendor/github.com/cheggaaa/pb/v3/speed.go

84 lines
2.0 KiB
Go

package pb
import (
"fmt"
"math"
"time"
"github.com/VividCortex/ewma"
)
var speedAddLimit = time.Second / 2
type speed struct {
ewma ewma.MovingAverage
lastStateId uint64
prevValue, startValue int64
prevTime, startTime time.Time
}
func (s *speed) value(state *State) float64 {
if s.ewma == nil {
s.ewma = ewma.NewMovingAverage()
}
if state.IsFirst() || state.Id() < s.lastStateId {
s.reset(state)
return 0
}
if state.Id() == s.lastStateId {
return s.ewma.Value()
}
if state.IsFinished() {
return s.absValue(state)
}
dur := state.Time().Sub(s.prevTime)
if dur < speedAddLimit {
return s.ewma.Value()
}
diff := math.Abs(float64(state.Value() - s.prevValue))
lastSpeed := diff / dur.Seconds()
s.prevTime = state.Time()
s.prevValue = state.Value()
s.lastStateId = state.Id()
s.ewma.Add(lastSpeed)
return s.ewma.Value()
}
func (s *speed) reset(state *State) {
s.lastStateId = state.Id()
s.startTime = state.Time()
s.prevTime = state.Time()
s.startValue = state.Value()
s.prevValue = state.Value()
s.ewma = ewma.NewMovingAverage()
}
func (s *speed) absValue(state *State) float64 {
if dur := state.Time().Sub(s.startTime); dur > 0 {
return float64(state.Value()) / dur.Seconds()
}
return 0
}
func getSpeedObj(state *State) (s *speed) {
if sObj, ok := state.Get(speedObj).(*speed); ok {
return sObj
}
s = new(speed)
state.Set(speedObj, s)
return
}
// ElementSpeed calculates current speed by EWMA
// Optionally can take one or two string arguments.
// First string will be used as value for format speed, default is "%s p/s".
// Second string will be used when speed not available, default is "? p/s"
// In template use as follows: {{speed .}} or {{speed . "%s per second"}} or {{speed . "%s ps" "..."}
var ElementSpeed ElementFunc = func(state *State, args ...string) string {
sp := getSpeedObj(state).value(state)
if sp == 0 {
return argsHelper(args).getNotEmptyOr(1, "? p/s")
}
return fmt.Sprintf(argsHelper(args).getNotEmptyOr(0, "%s p/s"), state.Format(int64(round(sp))))
}