github.com/anacrolix/torrent@v1.61.0/storage/piece-completion.go (about) 1 package storage 2 3 import ( 4 "cmp" 5 "iter" 6 "os" 7 8 "github.com/anacrolix/log" 9 10 "github.com/anacrolix/torrent/metainfo" 11 "github.com/anacrolix/torrent/types/infohash" 12 ) 13 14 type PieceCompletionGetSetter interface { 15 // I think the extra error parameter is vestigial. Looks like you should put your error in 16 // Completion.Err. 17 Get(metainfo.PieceKey) (Completion, error) 18 Set(_ metainfo.PieceKey, complete bool) error 19 } 20 21 // Implementations track the completion of pieces. It must be concurrent-safe. 22 type PieceCompletion interface { 23 PieceCompletionGetSetter 24 // Piece completion is maintained between storage instances. We can use this to avoid flushing 25 // pieces when they're marked complete if there's some other mechanism to ensure correctness. 26 Close() error 27 } 28 29 type PieceCompletionPersistenter interface { 30 Persistent() bool 31 } 32 33 func pieceCompletionIsPersistent(pc PieceCompletion) bool { 34 if p, ok := pc.(PieceCompletionPersistenter); ok { 35 return p.Persistent() 36 } 37 // Default is true. That's assumes flushing is required every time a piece is completed. 38 return true 39 } 40 41 // Optional interface with optimized Get for ranges. Use GetPieceCompletionRange wrapper to abstract 42 // over it not being implemented. 43 type PieceCompletionGetRanger interface { 44 GetRange(_ infohash.T, begin, end int) iter.Seq[Completion] 45 } 46 47 // Get piece completion as an iterator. Should be faster for long sequences of Gets. Uses optional 48 // interface PieceCompletionGetRanger if implemented. 49 func GetPieceCompletionRange(pc PieceCompletion, ih infohash.T, begin, end int) iter.Seq[Completion] { 50 if a, ok := pc.(PieceCompletionGetRanger); ok { 51 return a.GetRange(ih, begin, end) 52 } 53 return func(yield func(Completion) bool) { 54 for i := begin; i < end; i++ { 55 c, err := pc.Get(metainfo.PieceKey{ 56 InfoHash: ih, 57 Index: i, 58 }) 59 c.Err = cmp.Or(c.Err, err) 60 if !yield(c) { 61 return 62 } 63 } 64 } 65 } 66 67 func pieceCompletionForDir(dir string) (ret PieceCompletion) { 68 // This should be happening before sqlite attempts to open a database in the intended directory. 69 os.MkdirAll(dir, 0o700) 70 ret, err := NewDefaultPieceCompletionForDir(dir) 71 if err != nil { 72 // This kinda sux using the global logger. This code is ancient. 73 log.Levelf(log.Warning, "couldn't open piece completion db in %q: %s", dir, err) 74 ret = NewMapPieceCompletion() 75 } 76 return 77 }