public static int BytesPerPiece(this ITorrentInfo self, int pieceIndex)
 {
     if (self.IsV1Only())
     {
         if (pieceIndex < self.PieceCount() - 1)
         {
             return(self.PieceLength);
         }
         return((int)(self.Size - self.PieceIndexToByteOffset(pieceIndex)));
     }
     else if (self.IsV2Only())
     {
         for (int i = 0; i < self.Files.Count; i++)
         {
             var file = self.Files[i];
             if (pieceIndex < file.StartPieceIndex || pieceIndex > file.EndPieceIndex)
             {
                 continue;
             }
             var remainder = file.Length - (pieceIndex - file.StartPieceIndex) * self.PieceLength;
             return((int)(remainder > self.PieceLength ? self.PieceLength : remainder));
         }
         throw new ArgumentOutOfRangeException(nameof(pieceIndex));
     }
     else
     {
         throw new NotSupportedException("hybrid torrents aren't supported");
     }
 }
 /// <summary>
 /// The number of pieces in the torrent
 /// </summary>
 /// <param name="self"></param>
 /// <returns></returns>
 public static int PieceCount(this ITorrentInfo self)
 {
     if (self.IsV1Only())
     {
         return((int)((self.Size + self.PieceLength - 1) / self.PieceLength));
     }
     else if (self.IsV2Only())
     {
         return(self.Files[self.Files.Count - 1].EndPieceIndex + 1);
     }
     else
     {
         throw new NotSupportedException();
     }
 }
 public static long PieceIndexToByteOffset(this ITorrentInfo self, int pieceIndex)
 {
     if (self.IsV1Only())
     {
         return((long)(long)self.PieceLength * pieceIndex);
     }
     else if (self.IsV2Only())
     {
         for (int i = 0; i < self.Files.Count; i++)
         {
             var file = self.Files[i];
             if (pieceIndex < file.StartPieceIndex || pieceIndex > file.EndPieceIndex)
             {
                 continue;
             }
             return(file.OffsetInTorrent + ((pieceIndex - file.StartPieceIndex) * (long)self.PieceLength));
         }
         throw new ArgumentOutOfRangeException(nameof(pieceIndex));
     }
     else
     {
         throw new NotSupportedException("Hybrid torrents aren't supported");
     }
 }
 public static int ByteOffsetToPieceIndex(this ITorrentInfo self, long offset)
 {
     if (self.IsV1Only())
     {
         return((int)(offset / self.PieceLength));
     }
     else if (self.IsV2Only())
     {
         for (int i = 0; i < self.Files.Count; i++)
         {
             var file = self.Files[i];
             if (offset < file.OffsetInTorrent || offset >= file.OffsetInTorrent + file.Length)
             {
                 continue;
             }
             return(file.StartPieceIndex + (int)((offset - file.OffsetInTorrent) / self.PieceLength));
         }
         throw new ArgumentOutOfRangeException(nameof(offset));
     }
     else
     {
         throw new NotSupportedException("Hybrid torrents aren't supported");
     }
 }
 public static int BlocksPerPiece(this ITorrentInfo self, int pieceIndex)
 => (BytesPerPiece(self, pieceIndex) + Constants.BlockSize - 1) / Constants.BlockSize;
 static bool IsHybrid(this ITorrentInfo self)
 => self.InfoHashes.IsHybrid;
 static bool IsV2Only(this ITorrentInfo self)
 => self.InfoHashes.V1 == null && self.InfoHashes.V2 != null;