-- | The following module is responsible for general types used -- throughout the system. module Torrent ( -- * Types InfoHash , PeerId , AnnounceURL , TorrentState(..) , TorrentInfo(..) , PieceNum , PieceSize , PieceMap , PiecesDoneMap , PieceInfo(..) , BlockSize , Block(..) , Capabilities(..) -- * Interface , bytesLeft , defaultBlockSize , defaultOptimisticSlots , defaultPort , mkPeerId , mkTorrentInfo ) where import Control.Applicative import Control.DeepSeq import Data.Array import Data.List import qualified Data.ByteString as B import qualified Data.Map as M import Data.Word import Numeric import System.Random import Test.QuickCheck import Protocol.BCode import Digest import Version -- | The type of Infohashes as used in torrents. These are identifiers -- of torrents type InfoHash = Digest -- | The peerId is the ID of a client. It is used to identify clients -- from each other type PeerId = String -- | The internal type of Announce URLs type AnnounceURL = B.ByteString -- | Internal type for a torrent. It identifies a torrent in various places of the system. data TorrentInfo = TorrentInfo { infoHash :: InfoHash, pieceCount :: Int, -- Number of pieces in torrent announceURL :: AnnounceURL } deriving Show data TorrentState = Seeding | Leeching deriving Show instance NFData TorrentState ---------------------------------------------------------------------- -- Capabilities data Capabilities = Fast deriving (Show, Eq) -- PIECES ---------------------------------------------------------------------- type PieceNum = Int type PieceSize = Int data PieceInfo = PieceInfo { offset :: !Integer, -- ^ Offset of the piece, might be greater than Int len :: !Integer, -- ^ Length of piece; usually a small value digest :: !B.ByteString -- ^ Digest of piece; taken from the .torret file } deriving (Eq, Show) type PieceMap = Array PieceNum PieceInfo -- | The PiecesDoneMap is a map which is true if we have the piece and false otherwise type PiecesDoneMap = M.Map PieceNum Bool -- | Return the amount of bytes left on a torrent given what pieces are done and the -- map of the shape of the torrent in question. bytesLeft :: PiecesDoneMap -> PieceMap -> Integer bytesLeft done pm = foldl' (\accu (k,v) -> case M.lookup k done of Just False -> (len v) + accu _ -> accu) 0 $ Data.Array.assocs pm -- BLOCKS ---------------------------------------------------------------------- type BlockSize = Int data Block = Block { blockOffset :: !Int -- ^ offset of this block within the piece , blockSize :: !BlockSize -- ^ size of this block within the piece } deriving (Eq, Ord, Show) instance NFData Block where rnf (Block bo sz) = rnf bo `seq` rnf sz `seq` () instance Arbitrary Block where arbitrary = Block <$> pos <*> pos where pos = choose (0, 4294967296 - 1) defaultBlockSize :: BlockSize defaultBlockSize = 16384 -- Bytes -- | Default number of optimistic slots defaultOptimisticSlots :: Int defaultOptimisticSlots = 2 -- | Default port to communicate on defaultPort :: Word16 defaultPort = 1579 -- | Convert a BCode block into its corresponding TorrentInfo block, perhaps -- failing in the process. mkTorrentInfo :: BCode -> IO TorrentInfo mkTorrentInfo bc = do (ann, np) <- case queryInfo bc of Nothing -> fail "Could not create torrent info" Just x -> return x ih <- hashInfoDict bc return TorrentInfo { infoHash = ih, announceURL = ann, pieceCount = np } where queryInfo b = do ann <- announce b np <- numberPieces b return (ann, np) -- | Create a new PeerId for this client mkPeerId :: StdGen -> PeerId mkPeerId gen = header ++ take (20 - length header) ranString where randomList :: Int -> StdGen -> [Int] randomList n = take n . unfoldr (Just . random) rs = randomList 10 gen ranString = concatMap (\i -> showHex (abs i) "") rs header = "-CT" ++ protoVersion ++ "-"