3 package possumTorrentStorage
8 "github.com/anacrolix/log"
9 possum "github.com/anacrolix/possum/go"
10 possumResource "github.com/anacrolix/possum/go/resource"
11 "github.com/anacrolix/torrent/storage"
17 // Extends possum resource.Provider with an efficient implementation of torrent
18 // storage.ConsecutiveChunkReader.
19 type Provider struct {
20 possumResource.Provider
24 var _ storage.ConsecutiveChunkReader = Provider{}
26 // Sorts by a precomputed key but swaps on another slice at the same time.
27 type keySorter[T any, K cmp.Ordered] struct {
32 func (o keySorter[T, K]) Len() int {
36 func (o keySorter[T, K]) Less(i, j int) bool {
37 return o.keys[i] < o.keys[j]
40 func (o keySorter[T, K]) Swap(i, j int) {
41 o.keys[i], o.keys[j] = o.keys[j], o.keys[i]
42 o.orig[i], o.orig[j] = o.orig[j], o.orig[i]
45 // TODO: Should the parent ReadConsecutiveChunks method take the expected number of bytes to avoid
46 // trying to read discontinuous or incomplete sequences of chunks?
47 func (p Provider) ReadConsecutiveChunks(prefix string) (rc io.ReadCloser, err error) {
48 p.Logger.Levelf(log.Debug, "ReadConsecutiveChunks(%q)", prefix)
50 pr, err := p.Handle.NewReader()
59 items, err := pr.ListItems(prefix)
63 keys := make([]int64, 0, len(items))
64 for _, item := range items {
67 i, err = strconv.ParseInt(offsetStr, 10, 64)
69 err = fmt.Errorf("failed to parse offset %q: %w", offsetStr, err)
72 keys = append(keys, i)
74 sort.Sort(keySorter[possum.Item, int64]{items, keys})
76 consValues := make([]consecutiveValue, 0, len(items))
77 for i, item := range items {
79 if itemOffset > offset {
80 // We can't provide a continuous read.
83 if itemOffset+item.Stat.Size() <= offset {
84 // This item isn't needed
88 v, err = pr.Add(prefix + item.Key)
92 consValues = append(consValues, consecutiveValue{
95 size: item.Stat.Size(),
97 offset += item.Stat.Size() - (offset - itemOffset)
106 err := p.writeConsecutiveValues(consValues, pw)
107 err = pw.CloseWithError(err)
115 type consecutiveValue struct {
121 func (pp Provider) writeConsecutiveValues(
122 values []consecutiveValue, pw *io.PipeWriter,
125 for _, v := range values {
127 valueOff := off - v.offset
128 n, err = io.Copy(pw, io.NewSectionReader(v.pv, valueOff, v.size-valueOff))