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 }