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  }