package fetch import "sync/atomic" const ( MaxConnections = 16 minSegmentBytes = int64(32) << 20 ) type Segment struct { Start int64 `json:"start"` End int64 `json:"end"` Done int64 `json:"done"` } type State struct { FileName string `json:"fileName"` Validator string `json:"validator"` TotalBytes int64 `json:"totalBytes"` Segments []Segment `json:"segments"` } func (state State) DoneBytes() int64 { var done int64 for _, segment := range state.Segments { done += segment.Done } return done } func (state State) valid() bool { if state.FileName == "" || len(state.Segments) == 0 { return false } for _, segment := range state.Segments { if segment.Start < 0 || segment.Done < 0 { return false } if segment.End == 0 && state.TotalBytes > 0 { return false } if segment.End > 0 && segment.Start+segment.Done > segment.End { return false } } return true } func planSegments(totalBytes int64, connections int) []Segment { if totalBytes <= 0 { return []Segment{{}} } count := min(int64(max(connections, 1)), max(totalBytes/minSegmentBytes, 1)) size := totalBytes / count segments := make([]Segment, count) for index := range segments { start := int64(index) * size segments[index] = Segment{Start: start, End: start + size} } segments[count-1].End = totalBytes return segments } type liveSegment struct { start int64 end int64 done atomic.Int64 } func newLiveSegment(segment Segment) *liveSegment { live := &liveSegment{start: segment.Start, end: segment.End} live.done.Store(segment.Done) return live } func (segment *liveSegment) position() int64 { return segment.start + segment.done.Load() } func (segment *liveSegment) bounded() bool { return segment.end > 0 } func (segment *liveSegment) remaining() int64 { if !segment.bounded() { return -1 } return segment.end - segment.position() } func (segment *liveSegment) complete() bool { return segment.bounded() && segment.position() >= segment.end } func (segment *liveSegment) snapshot() Segment { return Segment{Start: segment.start, End: segment.end, Done: segment.done.Load()} }