+module FuncTorrent.FileSystem
+ (startThread,
+ MsgChannel,
+ initFS,
+ Msg(..),
+ Piece(..),
+ pieceMapFromFile
+ )
+ where
+
+import Control.Concurrent (ThreadId, forkIO)
+import Control.Concurrent.Chan (Chan, newChan, readChan)
+import Control.Concurrent.MVar (MVar, putMVar)
+import Control.Monad (forever)
+import Data.Map (traverseWithKey)
+
+import qualified Data.ByteString as BS
+import Data.Map ((!))
+import System.IO (Handle, openFile, IOMode (ReadWriteMode))
+
+import FuncTorrent.PieceManager (PieceDlState(..), PieceData(..), PieceMap, pieceNumToOffset)
+import FuncTorrent.Utils (readFileAtOffset, writeFileAtOffset, verifyHash)
+
+type PieceNum = Integer
+data Piece = Piece PieceNum BS.ByteString
+
+data Msg = ReadPiece PieceNum Integer (MVar Piece)
+ | WritePiece Piece
+ | VerifyPiece PieceNum (MVar Bool)
+
+type MsgChannel = Chan Msg
+
+-- init :: FileName -> IO (Handle, MsgChannel)
+initFS :: FilePath -> IO (Handle, MsgChannel)
+initFS filepath = do
+ c <- newChan
+ h <- openFile filepath ReadWriteMode
+ return (h, c)
+
+startThread :: Handle -> MsgChannel -> PieceMap -> IO ThreadId
+startThread handle c pieceMap = do
+ forkIO $ forever $ recvMsg >>= sendResponse
+ where
+ recvMsg = readChan c
+ sendResponse msg =
+ case msg of
+ ReadPiece n len' var -> do
+ bs <- readPiece n len'
+ putMVar var (Piece n bs)
+ WritePiece (Piece n bs) -> do
+ writePiece n bs
+ VerifyPiece n var -> do
+ isHashValid <- verifyPiece n
+ putMVar var isHashValid
+ readPiece n len' = do
+ let offset = pieceNumToOffset pieceMap n
+ readFileAtOffset handle offset len'
+ writePiece n piece = do
+ let offset = pieceNumToOffset pieceMap n
+ writeFileAtOffset handle offset piece
+ verifyPiece n = do
+ let offset = pieceNumToOffset pieceMap n
+ hash' = hash (pieceMap ! n)
+ len' = len (pieceMap ! n)
+ bs' <- readFileAtOffset handle offset len'
+ return $ verifyHash bs' hash'
+
+pieceMapFromFile :: Handle -> PieceMap -> IO PieceMap
+pieceMapFromFile handle pieceMap = do
+ traverseWithKey f pieceMap
+ where
+ f k v = do
+ let offset = pieceNumToOffset pieceMap k
+ isHashValid <- flip verifyHash (hash v) <$> readFileAtOffset handle offset (len v)
+ if isHashValid
+ then return $ v { dlstate = Have }
+ else return v