summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/System/Torrent/Storage.hs112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/System/Torrent/Storage.hs b/src/System/Torrent/Storage.hs
new file mode 100644
index 00000000..c00f770a
--- /dev/null
+++ b/src/System/Torrent/Storage.hs
@@ -0,0 +1,112 @@
1-- |
2-- Copyright : (c) Sam T. 2013
3-- License : MIT
4-- Maintainer : pxqr.sta@gmail.com
5-- Stability : experimental
6-- Portability : non-portable
7--
8-- This module implements mapping from single continious block space
9-- to file storage. Storage can be used in two modes:
10--
11-- * As in memory storage - in this case we don't touch filesystem.
12--
13-- * As ordinary mmaped file storage - when we need to store
14-- data in the filesystem.
15--
16--
17{-# LANGUAGE DoAndIfThenElse #-}
18{-# LANGUAGE NamedFieldPuns #-}
19{-# LANGUAGE RecordWildCards #-}
20module Cobit.Storage
21 ( Storage
22
23 -- * Construction
24 , mapStorage, unmapStorage, withStorage
25
26 -- * Modification
27 , getBlk, putBlk
28 ) where
29
30import Control.Applicative
31import Control.Exception
32import Control.Monad
33import Data.ByteString as B
34import qualified Data.ByteString.Lazy as Lazy
35import Data.List as L
36import System.FilePath
37import System.Directory
38
39import Data.Torrent
40import Network.BitTorrent
41import System.IO.MMap.Fixed
42
43
44data Storage = Storage {
45 session :: SwarmSession
46
47 -- | Used to map linear block addresses to disjoint mallocated/mmaped adresses.
48 , stPayload :: Fixed
49
50 -- | Used to find out block offsets.
51 , stPieceSize :: Int
52
53 -- | Used to verify pieces and set finally mark piece as verified.
54 , stHashes :: ByteString
55 }
56
57-- TODO doc args
58mapStorage :: Int -> Maybe FilePath -> FilePath -> ContentInfo -> IO Storage
59mapStorage blkSize statusPath contentPath ci = do
60 let content_paths = contentLayout contentPath ci
61 mapM_ mkDir (L.map fst content_paths)
62 Storage <$> coalesceFiles content_paths
63 <*> getAllocator statusPath (pieceCount ci) (blockCount blkSize ci)
64 <*> pure (ciPieceLength ci)
65 <*> pure (ciPieces ci)
66 where
67 getAllocator (Just path) = error "getAllocator"
68 getAllocator Nothing = error "getAllocator"
69
70 mkDir path = do
71 let dirPath = fst (splitFileName path)
72 exist <- doesDirectoryExist dirPath
73 unless exist $ do
74 createDirectoryIfMissing True dirPath
75
76unmapStorage :: Storage -> IO ()
77unmapStorage st = error "unmapStorage"
78
79
80withStorage :: Int -> Maybe FilePath -> FilePath -> ContentInfo
81 -> (Storage -> IO a)
82 -> IO a
83withStorage blkSize statusPath contentPath t =
84 bracket (mapStorage blkSize statusPath contentPath t) unmapStorage
85
86isAvailable :: BlockIx -> Storage -> IO Bool
87isAvailable ix Storage {..} = error "isAvailable"
88
89
90putBlk :: Block -> Storage -> IO ()
91putBlk blk @ Block {..} st @ Storage {..} = do
92 available <- isAvailable (blockIx blk) st
93 unless available $
94 writeBytes (blkInterval stPieceSize blk) (Lazy.fromChunks [blkData]) stPayload
95
96-- TODO
97getBlk :: BlockIx -> Storage -> IO (Maybe Block)
98getBlk ix @ BlockIx {..} st @ Storage {..} = do
99 available <- isAvailable ix st
100 if available
101 then Just <$> do
102 bs <- readBytes (ixInterval stPieceSize ix) stPayload
103 return $ Block ixPiece ixOffset (Lazy.toStrict bs)
104 else return Nothing
105
106ixInterval :: Int -> BlockIx -> FixedInterval
107ixInterval pieceSize BlockIx {..} =
108 interval (ixPiece * pieceSize + ixOffset) ixLength
109
110blkInterval :: Int -> Block -> FixedInterval
111blkInterval pieceSize Block {..} =
112 interval (blkPiece * pieceSize + blkOffset) (B.length blkData) \ No newline at end of file