98 lines
2.1 KiB
Go
98 lines
2.1 KiB
Go
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()}
|
|
}
|