github.com/anacrolix/torrent@v1.61.0/storage/possum/possum-provider.go (about) 1 //go:build cgo 2 3 package possumTorrentStorage 4 5 import ( 6 "fmt" 7 "io" 8 "sort" 9 "strconv" 10 11 "github.com/anacrolix/log" 12 possum "github.com/anacrolix/possum/go" 13 possumResource "github.com/anacrolix/possum/go/resource" 14 15 "github.com/anacrolix/torrent/storage" 16 ) 17 18 // Extends possum resource.Provider with an efficient implementation of torrent 19 // storage.ConsecutiveChunkReader. TODO: This doesn't expose Capacity. 20 type Provider struct { 21 possumResource.Provider 22 Logger log.Logger 23 } 24 25 var _ interface { 26 storage.ConsecutiveChunkReader 27 storage.ChunksReaderer 28 } = Provider{} 29 30 type chunkReader struct { 31 r possum.Reader 32 values []consecutiveValue 33 } 34 35 func (c chunkReader) ReadAt(p []byte, off int64) (n int, err error) { 36 vi := sort.Search(len(c.values), func(i int) bool { 37 return off < c.values[i].offset+c.values[i].size 38 }) 39 if vi == len(c.values) { 40 err = io.ErrUnexpectedEOF 41 return 42 } 43 v := c.values[vi] 44 return v.pv.ReadAt(p, off-v.offset) 45 } 46 47 func (c chunkReader) Close() error { 48 return c.r.Close() 49 } 50 51 type ChunkReader interface { 52 io.ReaderAt 53 io.Closer 54 } 55 56 // TODO: Should the parent ReadConsecutiveChunks method take the expected number of bytes to avoid 57 // trying to read discontinuous or incomplete sequences of chunks? 58 func (p Provider) ChunksReader(dir string) (ret storage.PieceReader, err error) { 59 prefix := dir + "/" 60 p.Logger.Levelf(log.Critical, "ChunkReader(%q)", prefix) 61 //debug.PrintStack() 62 pr, err := p.Handle.NewReader() 63 if err != nil { 64 return 65 } 66 defer func() { 67 if err != nil { 68 pr.End() 69 } 70 }() 71 items, err := pr.ListItems(prefix) 72 if err != nil { 73 return 74 } 75 keys := make([]int64, 0, len(items)) 76 for _, item := range items { 77 var i int64 78 offsetStr := item.Key 79 i, err = strconv.ParseInt(offsetStr, 10, 64) 80 if err != nil { 81 err = fmt.Errorf("failed to parse offset %q: %w", offsetStr, err) 82 return 83 } 84 keys = append(keys, i) 85 } 86 sort.Sort(keySorter[possum.Item, int64]{items, keys}) 87 offset := int64(0) 88 consValues := make([]consecutiveValue, 0, len(items)) 89 for i, item := range items { 90 itemOffset := keys[i] 91 if itemOffset+item.Stat.Size() <= offset { 92 // This item isn't needed 93 continue 94 } 95 var v possum.Value 96 v, err = pr.Add(prefix + item.Key) 97 if err != nil { 98 return 99 } 100 consValues = append(consValues, consecutiveValue{ 101 pv: v, 102 offset: itemOffset, 103 size: item.Stat.Size(), 104 }) 105 offset = itemOffset + item.Stat.Size() 106 } 107 err = pr.Begin() 108 if err != nil { 109 return 110 } 111 ret = chunkReader{ 112 r: pr, 113 values: consValues, 114 } 115 return 116 } 117 118 // TODO: Should the parent ReadConsecutiveChunks method take the expected number of bytes to avoid 119 // trying to read discontinuous or incomplete sequences of chunks? 120 func (p Provider) ReadConsecutiveChunks(prefix string) (rc io.ReadCloser, err error) { 121 p.Logger.Levelf(log.Debug, "ReadConsecutiveChunks(%q)", prefix) 122 //debug.PrintStack() 123 pr, err := p.Handle.NewReader() 124 if err != nil { 125 return 126 } 127 defer func() { 128 if err != nil { 129 pr.End() 130 } 131 }() 132 items, err := pr.ListItems(prefix) 133 if err != nil { 134 return 135 } 136 keys := make([]int64, 0, len(items)) 137 for _, item := range items { 138 var i int64 139 offsetStr := item.Key 140 i, err = strconv.ParseInt(offsetStr, 10, 64) 141 if err != nil { 142 err = fmt.Errorf("failed to parse offset %q: %w", offsetStr, err) 143 return 144 } 145 keys = append(keys, i) 146 } 147 sort.Sort(keySorter[possum.Item, int64]{items, keys}) 148 offset := int64(0) 149 consValues := make([]consecutiveValue, 0, len(items)) 150 for i, item := range items { 151 itemOffset := keys[i] 152 if itemOffset > offset { 153 // We can't provide a continuous read. 154 break 155 } 156 if itemOffset+item.Stat.Size() <= offset { 157 // This item isn't needed 158 continue 159 } 160 var v possum.Value 161 v, err = pr.Add(prefix + item.Key) 162 if err != nil { 163 return 164 } 165 consValues = append(consValues, consecutiveValue{ 166 pv: v, 167 offset: itemOffset, 168 size: item.Stat.Size(), 169 }) 170 offset += item.Stat.Size() - (offset - itemOffset) 171 } 172 err = pr.Begin() 173 if err != nil { 174 return 175 } 176 rc, pw := io.Pipe() 177 go func() { 178 defer pr.End() 179 err := p.writeConsecutiveValues(consValues, pw) 180 err = pw.CloseWithError(err) 181 if err != nil { 182 panic(err) 183 } 184 }() 185 return 186 } 187 188 type consecutiveValue struct { 189 pv possum.Value 190 offset int64 191 size int64 192 } 193 194 func (pp Provider) writeConsecutiveValues( 195 values []consecutiveValue, pw *io.PipeWriter, 196 ) (err error) { 197 off := int64(0) 198 for _, v := range values { 199 var n int64 200 valueOff := off - v.offset 201 n, err = io.Copy(pw, io.NewSectionReader(v.pv, valueOff, v.size-valueOff)) 202 if err != nil { 203 return 204 } 205 off += n 206 } 207 return nil 208 } 209 210 func (pp Provider) MovePrefix(from, to string) (err error) { 211 return pp.Handle.MovePrefix([]byte(from), []byte(to)) 212 }