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  }