summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2014-01-05 20:06:13 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2014-01-05 20:06:13 +0400
commit3eaa3003d9ece714595dffbf07202ee90336d05f (patch)
tree4dfa6528139a37dc194da60cb01161ebff9fd5d9
parentb925e91c5e53a4300e5b0511e8811c5f1af9e803 (diff)
Add storage streaming
-rw-r--r--bittorrent.cabal1
-rw-r--r--src/System/Torrent/Storage.hs24
-rw-r--r--tests/System/Torrent/StorageSpec.hs26
3 files changed, 51 insertions, 0 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal
index 2f30d848..67f1ef18 100644
--- a/bittorrent.cabal
+++ b/bittorrent.cabal
@@ -212,6 +212,7 @@ test-suite spec
212 212
213 , mtl 213 , mtl
214 , resourcet 214 , resourcet
215 , conduit
215 216
216 , hspec >= 1.8.1.1 217 , hspec >= 1.8.1.1
217 , QuickCheck 218 , QuickCheck
diff --git a/src/System/Torrent/Storage.hs b/src/System/Torrent/Storage.hs
index 1c84bf69..3bb229a4 100644
--- a/src/System/Torrent/Storage.hs
+++ b/src/System/Torrent/Storage.hs
@@ -36,11 +36,17 @@ module System.Torrent.Storage
36 , readPiece 36 , readPiece
37 , hintRead 37 , hintRead
38 , unsafeReadPiece 38 , unsafeReadPiece
39
40 -- * Streaming
41 , sourceStorage
42 , sinkStorage
39 ) where 43 ) where
40 44
41import Control.Applicative 45import Control.Applicative
42import Control.Exception 46import Control.Exception
47import Control.Monad.Trans
43import Data.ByteString.Lazy as BL 48import Data.ByteString.Lazy as BL
49import Data.Conduit
44import Data.Typeable 50import Data.Typeable
45 51
46import Data.Torrent.Bitfield as BF 52import Data.Torrent.Bitfield as BF
@@ -120,6 +126,24 @@ unsafeReadPiece pix s @ Storage {..}
120 offset = fromIntegral pix * fromIntegral pieceLen 126 offset = fromIntegral pix * fromIntegral pieceLen
121 sz = fromIntegral pieceLen 127 sz = fromIntegral pieceLen
122 128
129-- | Stream storage pieces from first to the last.
130sourceStorage :: Storage -> Source IO (Piece BL.ByteString)
131sourceStorage s = go 0
132 where
133 go pix
134 | pix < totalPieces s = do
135 piece <- liftIO $ readPiece pix s
136 liftIO $ hintRead (succ pix) s
137 yield piece
138 go (succ pix)
139 | otherwise = return ()
140
141-- | Write stream of pieces to the storage. Fail if storage is 'ReadOnly'.
142sinkStorage :: Storage -> Sink (Piece BL.ByteString) IO ()
143sinkStorage s = do
144 awaitForever $ \ piece ->
145 liftIO $ writePiece piece s
146
123-- | TODO examples of use 147-- | TODO examples of use
124genPieceInfo :: Storage -> IO PieceInfo 148genPieceInfo :: Storage -> IO PieceInfo
125genPieceInfo = undefined 149genPieceInfo = undefined
diff --git a/tests/System/Torrent/StorageSpec.hs b/tests/System/Torrent/StorageSpec.hs
index 8267b7a5..d2185961 100644
--- a/tests/System/Torrent/StorageSpec.hs
+++ b/tests/System/Torrent/StorageSpec.hs
@@ -1,6 +1,8 @@
1module System.Torrent.StorageSpec (spec) where 1module System.Torrent.StorageSpec (spec) where
2import Control.Exception 2import Control.Exception
3import Data.ByteString.Lazy as BL 3import Data.ByteString.Lazy as BL
4import Data.Conduit as C
5import Data.Conduit.List as C
4import System.FilePath 6import System.FilePath
5import System.Directory 7import System.Directory
6import System.IO.Unsafe 8import System.IO.Unsafe
@@ -25,6 +27,12 @@ createLayout :: IO ()
25createLayout = 27createLayout =
26 bracket (open ReadWriteEx 0 layout) close (const (return ())) 28 bracket (open ReadWriteEx 0 layout) close (const (return ()))
27 29
30psize :: PieceSize
31psize = 16
32
33pcount :: PieceCount
34pcount = 11
35
28spec :: Spec 36spec :: Spec
29spec = before createLayout $ do 37spec = before createLayout $ do
30 describe "writePiece" $ do 38 describe "writePiece" $ do
@@ -56,3 +64,21 @@ spec = before createLayout $ do
56 withStorage ReadOnly 100 layout $ \ s -> do 64 withStorage ReadOnly 100 layout $ \ s -> do
57 _ <- readPiece 1 s 65 _ <- readPiece 1 s
58 readPiece 2 s `shouldThrow` (== InvalidIndex 2) 66 readPiece 2 s `shouldThrow` (== InvalidIndex 2)
67
68 describe "sourceStorage" $ do
69 it "should source all chunks" $ do
70 withStorage ReadOnly psize layout $ \ s -> do
71 n <- sourceStorage s $$ C.fold (\ n _ -> succ n) 0
72 n `shouldBe` pcount
73
74 -- this test should fail if 'sourceStorage' test fail
75 describe "sinkStorage" $ do
76 it "should write all chunks" $ do
77 let byteVal = 0
78 let bzeroPiece p = p { pieceData = BL.replicate (BL.length (pieceData p)) byteVal }
79 let isZeroPiece p = (== byteVal) `BL.all` pieceData p
80
81 withStorage ReadWrite psize layout $ \ s -> do
82 sourceStorage s $= C.map bzeroPiece $$ sinkStorage s
83 b <- sourceStorage s $$ C.fold (\ b p -> b && isZeroPiece p) True
84 b `shouldBe` True