summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Exchange/Session/Metadata.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent/Exchange/Session/Metadata.hs')
-rw-r--r--src/Network/BitTorrent/Exchange/Session/Metadata.hs102
1 files changed, 0 insertions, 102 deletions
diff --git a/src/Network/BitTorrent/Exchange/Session/Metadata.hs b/src/Network/BitTorrent/Exchange/Session/Metadata.hs
deleted file mode 100644
index f08ebe00..00000000
--- a/src/Network/BitTorrent/Exchange/Session/Metadata.hs
+++ /dev/null
@@ -1,102 +0,0 @@
1{-# LANGUAGE TemplateHaskell #-}
2module Network.BitTorrent.Exchange.Session.Metadata
3 ( -- * Transfer state
4 Status
5 , nullStatus
6
7 -- * State updates
8 , Updates
9 , runUpdates
10
11 -- * Piece transfer control
12 , scheduleBlock
13 , resetPending
14 , cancelPending
15 , pushBlock
16 ) where
17
18import Control.Concurrent
19import Control.Lens
20import Control.Monad.Reader
21import Control.Monad.State
22import Data.ByteString as BS
23import Data.ByteString.Lazy as BL
24import Data.Default
25import Data.List as L
26import Data.Tuple
27
28import Data.BEncode as BE
29import Data.Torrent as Torrent
30import Network.BitTorrent.Address
31import Network.BitTorrent.Exchange.Block as Block
32import Network.BitTorrent.Exchange.Message as Message hiding (Status)
33
34
35-- | Current transfer status.
36data Status = Status
37 { _pending :: [(PeerAddr IP, PieceIx)]
38 , _bucket :: Bucket
39 }
40
41makeLenses ''Status
42
43instance Default Status where
44 def = error "default status"
45
46-- | Create a new scheduler for infodict of the given size.
47nullStatus :: Int -> Status
48nullStatus ps = Status [] (Block.empty ps)
49
50type Updates = ReaderT (PeerAddr IP) (State Status)
51
52runUpdates :: MVar Status -> PeerAddr IP -> Updates a -> IO a
53runUpdates v a m = modifyMVar v (return . swap . runState (runReaderT m a))
54
55scheduleBlock :: Updates (Maybe PieceIx)
56scheduleBlock = do
57 addr <- ask
58 bkt <- use bucket
59 case spans metadataPieceSize bkt of
60 [] -> return Nothing
61 ((off, _ ) : _) -> do
62 let pix = off `div` metadataPieceSize
63 pending %= ((addr, pix) :)
64 return (Just pix)
65
66cancelPending :: PieceIx -> Updates ()
67cancelPending pix = pending %= L.filter ((pix ==) . snd)
68
69resetPending :: Updates ()
70resetPending = do
71 addr <- ask
72 pending %= L.filter ((addr ==) . fst)
73
74parseInfoDict :: BS.ByteString -> InfoHash -> Result InfoDict
75parseInfoDict chunk topic =
76 case BE.decode chunk of
77 Right (infodict @ InfoDict {..})
78 | topic == idInfoHash -> return infodict
79 | otherwise -> Left "broken infodict"
80 Left err -> Left $ "unable to parse infodict " ++ err
81
82-- todo use incremental parsing to avoid BS.concat call
83pushBlock :: Torrent.Piece BS.ByteString -> InfoHash -> Updates (Maybe InfoDict)
84pushBlock Torrent.Piece {..} topic = do
85 addr <- ask
86 p <- use pending
87 when ((addr, pieceIndex) `L.notElem` p) $ error "not requested"
88 cancelPending pieceIndex
89
90 bucket %= Block.insert (metadataPieceSize * pieceIndex) pieceData
91 b <- use bucket
92 case toPiece b of
93 Nothing -> return Nothing
94 Just chunks ->
95 case parseInfoDict (BL.toStrict chunks) topic of
96 Right x -> do
97 pending .= []
98 return (Just x)
99 Left e -> do
100 pending .= []
101 bucket .= Block.empty (Block.size b)
102 return Nothing