github.com/uber/kraken@v0.1.4/lib/torrent/storage/originstorage/torrent.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package originstorage
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  
    20  	"github.com/uber/kraken/core"
    21  	"github.com/uber/kraken/lib/store"
    22  	"github.com/uber/kraken/lib/torrent/storage"
    23  	"github.com/uber/kraken/lib/torrent/storage/piecereader"
    24  
    25  	"github.com/willf/bitset"
    26  	"go.uber.org/atomic"
    27  )
    28  
    29  // Torrent errors.
    30  var (
    31  	ErrReadOnly = errors.New("Read-only torrent is being written to")
    32  )
    33  
    34  // Torrent is a read-only storage.Torrent. It allows concurrent reads on all
    35  // pieces.
    36  type Torrent struct {
    37  	metaInfo    *core.MetaInfo
    38  	cas         *store.CAStore
    39  	numComplete *atomic.Int32
    40  }
    41  
    42  // NewTorrent creates a new Torrent.
    43  func NewTorrent(cas *store.CAStore, mi *core.MetaInfo) (*Torrent, error) {
    44  	return &Torrent{
    45  		cas:         cas,
    46  		metaInfo:    mi,
    47  		numComplete: atomic.NewInt32(int32(mi.NumPieces())),
    48  	}, nil
    49  }
    50  
    51  // Digest returns the digest of the target blob.
    52  func (t *Torrent) Digest() core.Digest {
    53  	return t.metaInfo.Digest()
    54  }
    55  
    56  // Stat returns the TorrentInfo for t.
    57  func (t *Torrent) Stat() *storage.TorrentInfo {
    58  	return storage.NewTorrentInfo(t.metaInfo, t.Bitfield())
    59  }
    60  
    61  // InfoHash returns the torrent metainfo hash.
    62  func (t *Torrent) InfoHash() core.InfoHash {
    63  	return t.metaInfo.InfoHash()
    64  }
    65  
    66  // NumPieces returns the number of pieces in the torrent.
    67  func (t *Torrent) NumPieces() int {
    68  	return t.metaInfo.NumPieces()
    69  }
    70  
    71  // Length returns the length of the target file.
    72  func (t *Torrent) Length() int64 {
    73  	return t.metaInfo.Length()
    74  }
    75  
    76  // PieceLength returns the length of piece pi.
    77  func (t *Torrent) PieceLength(pi int) int64 {
    78  	return t.metaInfo.GetPieceLength(pi)
    79  }
    80  
    81  // MaxPieceLength returns the longest piece length of the torrent.
    82  func (t *Torrent) MaxPieceLength() int64 {
    83  	return t.PieceLength(0)
    84  }
    85  
    86  // Complete is always true.
    87  func (t *Torrent) Complete() bool {
    88  	return true
    89  }
    90  
    91  // BytesDownloaded always returns the total number of bytes.
    92  func (t *Torrent) BytesDownloaded() int64 {
    93  	return t.metaInfo.Length()
    94  }
    95  
    96  // WritePiece returns error, since Torrent is read-only.
    97  func (t *Torrent) WritePiece(src storage.PieceReader, pi int) error {
    98  	return ErrReadOnly
    99  }
   100  
   101  // Bitfield always returns a completed bitfield.
   102  func (t *Torrent) Bitfield() *bitset.BitSet {
   103  	return bitset.New(uint(t.NumPieces())).Complement()
   104  }
   105  
   106  func (t *Torrent) String() string {
   107  	downloaded := int(float64(t.BytesDownloaded()) / float64(t.metaInfo.Length()) * 100)
   108  	return fmt.Sprintf("torrent(hash=%s, downloaded=%d%%)", t.InfoHash().Hex(), downloaded)
   109  }
   110  
   111  type opener struct {
   112  	torrent *Torrent
   113  }
   114  
   115  func (o *opener) Open() (store.FileReader, error) {
   116  	return o.torrent.cas.GetCacheFileReader(o.torrent.Digest().Hex())
   117  }
   118  
   119  // GetPieceReader returns a reader for piece pi.
   120  func (t *Torrent) GetPieceReader(pi int) (storage.PieceReader, error) {
   121  	if pi >= t.NumPieces() {
   122  		return nil, fmt.Errorf("invalid piece index %d: num pieces = %d", pi, t.NumPieces())
   123  	}
   124  	return piecereader.NewFileReader(t.getFileOffset(pi), t.PieceLength(pi), &opener{t}), nil
   125  }
   126  
   127  // HasPiece returns if piece pi is complete.
   128  // For Torrent it's always true.
   129  func (t *Torrent) HasPiece(pi int) bool {
   130  	return true
   131  }
   132  
   133  // MissingPieces always returns empty list.
   134  func (t *Torrent) MissingPieces() []int {
   135  	return []int{}
   136  }
   137  
   138  // getFileOffset calculates the offset in the torrent file given piece index.
   139  // Assumes pi is a valid piece index.
   140  func (t *Torrent) getFileOffset(pi int) int64 {
   141  	return t.metaInfo.PieceLength() * int64(pi)
   142  }