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

     1  //go:build cgo
     2  // +build cgo
     3  
     4  package sqliteStorage
     5  
     6  import (
     7  	"context"
     8  	"encoding/hex"
     9  	"io"
    10  	"sync"
    11  	"time"
    12  
    13  	g "github.com/anacrolix/generics"
    14  	"github.com/anacrolix/squirrel"
    15  
    16  	"github.com/anacrolix/torrent/metainfo"
    17  	"github.com/anacrolix/torrent/storage"
    18  )
    19  
    20  // A convenience function that creates a connection pool, resource provider, and a pieces storage
    21  // ClientImpl and returns them all with a Close attached.
    22  func NewDirectStorage(opts NewDirectStorageOpts) (_ storage.ClientImplCloser, err error) {
    23  	cache, err := squirrel.NewCache(opts)
    24  	if err != nil {
    25  		return
    26  	}
    27  	return &client{
    28  		cache: cache,
    29  	}, nil
    30  }
    31  
    32  // Creates a storage.ClientImpl from a provided squirrel.Cache. The caller is responsible for
    33  // closing the squirrel.Cache.
    34  func NewWrappingClient(cache *squirrel.Cache) storage.ClientImpl {
    35  	return &client{
    36  		cache: cache,
    37  	}
    38  }
    39  
    40  type client struct {
    41  	cache           *squirrel.Cache
    42  	capacityMu      sync.Mutex
    43  	capacityFetched time.Time
    44  	capacityCap     int64
    45  	capacityCapped  bool
    46  }
    47  
    48  func (c *client) Close() error {
    49  	return c.cache.Close()
    50  }
    51  
    52  func (c *client) capacity() (cap int64, capped bool) {
    53  	c.capacityMu.Lock()
    54  	defer c.capacityMu.Unlock()
    55  	if !c.capacityFetched.IsZero() && time.Since(c.capacityFetched) < time.Second {
    56  		cap, capped = c.capacityCap, c.capacityCapped
    57  		return
    58  	}
    59  	c.capacityCap, c.capacityCapped = c.cache.GetCapacity()
    60  	// Should this go before or after the capacityCap and capacityCapped assignments?
    61  	c.capacityFetched = time.Now()
    62  	cap, capped = c.capacityCap, c.capacityCapped
    63  	return
    64  }
    65  
    66  func (c *client) OpenTorrent(
    67  	context.Context,
    68  	*metainfo.Info,
    69  	metainfo.Hash,
    70  ) (storage.TorrentImpl, error) {
    71  	t := torrent{c.cache}
    72  	capFunc := c.capacity
    73  	return storage.TorrentImpl{PieceWithHash: t.Piece, Close: t.Close, Capacity: &capFunc}, nil
    74  }
    75  
    76  type torrent struct {
    77  	c *squirrel.Cache
    78  }
    79  
    80  func (t torrent) Piece(p metainfo.Piece, pieceHash g.Option[[]byte]) storage.PieceImpl {
    81  	ret := piece{
    82  		sb: t.c.OpenWithLength(hex.EncodeToString(pieceHash.Unwrap()), p.Length()),
    83  	}
    84  	ret.ReaderAt = &ret.sb
    85  	ret.WriterAt = &ret.sb
    86  	return ret
    87  }
    88  
    89  func (t torrent) Close() error {
    90  	return nil
    91  }
    92  
    93  type piece struct {
    94  	sb squirrel.Blob
    95  	io.ReaderAt
    96  	io.WriterAt
    97  }
    98  
    99  func (p piece) MarkComplete() error {
   100  	return p.sb.SetTag("verified", true)
   101  }
   102  
   103  func (p piece) MarkNotComplete() error {
   104  	return p.sb.SetTag("verified", false)
   105  }
   106  
   107  func (p piece) Completion() (ret storage.Completion) {
   108  	err := p.sb.GetTag("verified", func(stmt squirrel.SqliteStmt) {
   109  		ret.Complete = stmt.ColumnInt(0) != 0
   110  	})
   111  	ret.Ok = err == nil
   112  	ret.Err = err
   113  	return
   114  }