package httpdl import "sync/atomic" // seg is one contiguous byte range of the output file, downloaded by a single // ranged GET. A segment IS a byte range — a plain HTTP downloader does not need // the Piece/Segment/block layering that exists only to share code with // BitTorrent. start and end are fixed when the file is divided; written is // advanced (atomically) by the segment's one worker, so Stat() and the resume // snapshot can read its frontier while it downloads. type seg struct { index int start int64 // first byte offset, inclusive end int64 // last byte offset, inclusive written int64 // bytes already written into this segment (the resume point) } func (s *seg) length() int64 { return s.end - s.start + 1 } func (s *seg) endOff() int64 { return s.end } func (s *seg) done() bool { return atomic.LoadInt64(&s.written) >= s.length() } func (s *seg) addWritten(n int64) { atomic.AddInt64(&s.written, n) } func (s *seg) progress() int64 { return atomic.LoadInt64(&s.written) } func (s *seg) offset() int64 { return s.start + atomic.LoadInt64(&s.written) } func (s *seg) remaining() int64 { return s.length() - atomic.LoadInt64(&s.written) } // makeSegments divides a file of total bytes into contiguous segments, using at // most conns of them and never splitting below minSplit. The remainder lands in // the last segment. With conns==1 (the default) this yields one segment. Each // segment gets its own worker, so the division here fixes the parallelism: there // is no later rebalancing. func makeSegments(total, minSplit int64, conns int) []seg { if conns < 1 { conns = 1 } if minSplit < 1 { minSplit = 1 } n := int64(conns) if max := total / minSplit; max < n { n = max } if n < 1 { n = 1 } chunk := total / n segs := make([]seg, n) var start int64 for i := int64(0); i < n; i++ { end := start + chunk - 1 if i == n-1 { end = total - 1 } segs[i] = seg{index: int(i), start: start, end: end} start = end + 1 } return segs } // maxAutoConns is the ceiling --auto-split scales to. aria2 caps // max-connection-per-server (-x) at 16, so a got-chosen count honours the same // per-server limit rather than inventing a looser one. const maxAutoConns = 16 // autoConns picks a connection count from the file size, used only when // --auto-split is set: one connection per min-split-size of content, at least 1 // and at most maxAutoConns. It is the got-only stand-in for hand-tuning -x/-s, // and because the count never exceeds total/minSplit, makeSegments yields exactly // that many balanced segments. func autoConns(total, minSplit int64) int { if minSplit < 1 { minSplit = 1 } n := total / minSplit if n < 1 { n = 1 } if n > maxAutoConns { n = maxAutoConns } return int(n) }