github.com/anacrolix/torrent@v1.61.0/storage/wrappers.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  
     9  	g "github.com/anacrolix/generics"
    10  
    11  	"github.com/anacrolix/torrent/metainfo"
    12  )
    13  
    14  type Client struct {
    15  	ci ClientImpl
    16  }
    17  
    18  func NewClient(cl ClientImpl) *Client {
    19  	return &Client{cl}
    20  }
    21  
    22  func (cl Client) OpenTorrent(
    23  	ctx context.Context,
    24  	info *metainfo.Info,
    25  	infoHash metainfo.Hash,
    26  ) (*Torrent, error) {
    27  	t, err := cl.ci.OpenTorrent(ctx, info, infoHash)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	return &Torrent{t}, nil
    32  }
    33  
    34  type Torrent struct {
    35  	TorrentImpl
    36  }
    37  
    38  // Deprecated. Use PieceWithHash, as this doesn't work with pure v2 torrents.
    39  func (t *Torrent) Piece(p metainfo.Piece) Piece {
    40  	return t.PieceWithHash(p, g.Some(p.V1Hash().Unwrap().Bytes()))
    41  }
    42  
    43  func (t *Torrent) PieceWithHash(p metainfo.Piece, pieceHash g.Option[[]byte]) Piece {
    44  	var pieceImpl PieceImpl
    45  	if t.TorrentImpl.PieceWithHash != nil {
    46  		pieceImpl = t.TorrentImpl.PieceWithHash(p, pieceHash)
    47  	} else {
    48  		pieceImpl = t.TorrentImpl.Piece(p)
    49  	}
    50  	return Piece{pieceImpl, p}
    51  }
    52  
    53  type Piece struct {
    54  	PieceImpl
    55  	mip metainfo.Piece
    56  }
    57  
    58  var _ io.WriterTo = Piece{}
    59  
    60  // Why do we have this wrapper? Well PieceImpl doesn't implement io.Reader, so we can't let io.Copy
    61  // and friends check for io.WriterTo and fallback for us since they expect an io.Reader.
    62  func (p Piece) WriteTo(w io.Writer) (_ int64, err error) {
    63  	if i, ok := p.PieceImpl.(io.WriterTo); ok {
    64  		return i.WriteTo(w)
    65  	}
    66  	n := p.mip.Length()
    67  	// NewReader will do the next smartest thing which may allow more efficient resource use between
    68  	// ReadAt calls. Worst case it gives us a nopCloser+p.
    69  	rc, err := p.NewReader()
    70  	if err != nil {
    71  		err = fmt.Errorf("opening reader: %w", err)
    72  		return
    73  	}
    74  	defer rc.Close()
    75  	r := io.NewSectionReader(rc, 0, n)
    76  	return io.CopyN(w, r, n)
    77  }
    78  
    79  func (p Piece) WriteAt(b []byte, off int64) (n int, err error) {
    80  	// Callers should not be writing to completed pieces, but it's too
    81  	// expensive to be checking this on every single write using uncached
    82  	// completions.
    83  
    84  	// c := p.Completion()
    85  	// if c.Ok && c.Complete {
    86  	// 	err = errors.New("piece already completed")
    87  	// 	return
    88  	// }
    89  	if off+int64(len(b)) > p.mip.Length() {
    90  		panic("write overflows piece")
    91  	}
    92  	return p.PieceImpl.WriteAt(b, off)
    93  }
    94  
    95  // If you're calling this you're probably doing something very inefficient. Consider WriteTo which
    96  // handles data spread across multiple objects in storage.
    97  func (p Piece) ReadAt(b []byte, off int64) (n int, err error) {
    98  	if off < 0 {
    99  		err = os.ErrInvalid
   100  		return
   101  	}
   102  	if off >= p.mip.Length() {
   103  		err = io.EOF
   104  		return
   105  	}
   106  	b = b[:min(int64(len(b)), p.mip.Length()-off)]
   107  	if len(b) == 0 {
   108  		return
   109  	}
   110  	n, err = p.PieceImpl.ReadAt(b, off)
   111  	if n > len(b) {
   112  		panic(n)
   113  	}
   114  	if n == 0 && err == nil {
   115  		panic("io.Copy will get stuck")
   116  	}
   117  	off += int64(n)
   118  
   119  	// Doing this here may be inaccurate. There's legitimate reasons we may fail to read while the
   120  	// data is still there, such as too many open files. There should probably be a specific error
   121  	// to return if the data has been lost.
   122  	if off < p.mip.Length() {
   123  		if err == io.EOF {
   124  			// TODO: Hey, this guy over here isn't checking errors.
   125  			p.MarkNotComplete()
   126  		}
   127  	}
   128  
   129  	return
   130  }
   131  
   132  func (p Piece) NewReader() (PieceReader, error) {
   133  	pr, ok := p.PieceImpl.(PieceReaderer)
   134  	if ok {
   135  		return pr.NewReader()
   136  	}
   137  	// TODO: Make generic reflect wrapper for nop Closer.
   138  	return struct {
   139  		io.ReaderAt
   140  		io.Closer
   141  	}{
   142  		p,
   143  		nopCloser{},
   144  	}, nil
   145  }
   146  
   147  type nopCloser struct{}
   148  
   149  func (nopCloser) Close() error {
   150  	return nil
   151  }