2 Copyright (C) 2015-2016 Ramakrishnan Muthukrishnan <ram@rkrishnan.org>
4 This file is part of FuncTorrent.
6 FuncTorrent is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 FuncTorrent is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with FuncTorrent; if not, see <http://www.gnu.org/licenses/>
21 module FuncTorrent.PieceManager
26 updatePieceAvailability,
32 import Prelude hiding (filter)
34 import qualified Data.ByteString.Char8 as BC (length)
35 import Control.Monad (liftM)
36 import Data.ByteString (ByteString)
37 import Data.Map (Map, (!), fromList, toList, mapWithKey, filter)
40 import FuncTorrent.PeerMsgs (Peer)
41 import FuncTorrent.Utils (splitN, splitNum)
43 data PieceDlState = Pending
48 -- todo - map with index to a new data structure (peers who have that piece and state)
49 data PieceData = PieceData { peers :: [Peer] -- ^ list of peers who have this piece
50 , dlstate :: PieceDlState -- ^ state of the piece from download perspective.
51 , hash :: ByteString -- ^ piece hash
52 , len :: Integer } -- ^ piece length
54 -- which piece is with which peers
55 type PieceMap = Map Integer PieceData
57 pieceNumToOffset :: PieceMap -> Integer -> Integer
58 pieceNumToOffset _ 0 = 0
59 pieceNumToOffset pieceMap k = k * len (pieceMap ! (k - 1))
61 -- simple algorithm to pick piece.
62 -- pick the first piece from 0 that is not downloaded yet.
63 pickPiece :: PieceMap -> Maybe Integer
65 (fst `liftM`) . headMay . toList . filter (\v -> dlstate v == Pending)
67 bytesDownloaded :: PieceMap -> Integer
69 sum . map (len . snd) . toList . filter (\v -> dlstate v == Have)
71 updatePieceAvailability :: PieceMap -> Peer -> [Integer] -> PieceMap
72 updatePieceAvailability pieceStatus p pieceList =
73 mapWithKey (\k pd -> if k `elem` pieceList
74 then (pd { peers = p : peers pd })
77 -- Make the initial Piece map, with the assumption that no peer has the
78 -- piece and that every piece is pending download.
79 initPieceMap :: ByteString -> Integer -> Integer -> PieceMap
80 initPieceMap pieceHash fileLen pieceLen = fromList kvs
82 numPieces = (toInteger . (`quot` 20) . BC.length) pieceHash
83 kvs = [(i, PieceData { peers = []
87 | (i, h, pLen) <- zip3 [0..numPieces] hashes pLengths]
88 hashes = splitN 20 pieceHash
89 pLengths = splitNum fileLen pieceLen