diff options
author | Sam T <pxqr.sta@gmail.com> | 2013-06-28 17:09:12 +0400 |
---|---|---|
committer | Sam T <pxqr.sta@gmail.com> | 2013-06-28 17:09:12 +0400 |
commit | d4285b123599ea99f6728cc2a61676095a681dd1 (patch) | |
tree | b942ce2b846df8acb71e6c63964fcd2aa635e591 /src/System/Torrent/Storage.hs | |
parent | 526b6f43ebbfb6dd5e9518470b19930184e9adeb (diff) |
~ Move Storage to bittorrent.
This way we can hide some session <-> storage details.
Diffstat (limited to 'src/System/Torrent/Storage.hs')
-rw-r--r-- | src/System/Torrent/Storage.hs | 112 |
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 #-} | ||
20 | module Cobit.Storage | ||
21 | ( Storage | ||
22 | |||
23 | -- * Construction | ||
24 | , mapStorage, unmapStorage, withStorage | ||
25 | |||
26 | -- * Modification | ||
27 | , getBlk, putBlk | ||
28 | ) where | ||
29 | |||
30 | import Control.Applicative | ||
31 | import Control.Exception | ||
32 | import Control.Monad | ||
33 | import Data.ByteString as B | ||
34 | import qualified Data.ByteString.Lazy as Lazy | ||
35 | import Data.List as L | ||
36 | import System.FilePath | ||
37 | import System.Directory | ||
38 | |||
39 | import Data.Torrent | ||
40 | import Network.BitTorrent | ||
41 | import System.IO.MMap.Fixed | ||
42 | |||
43 | |||
44 | data 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 | ||
58 | mapStorage :: Int -> Maybe FilePath -> FilePath -> ContentInfo -> IO Storage | ||
59 | mapStorage 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 | |||
76 | unmapStorage :: Storage -> IO () | ||
77 | unmapStorage st = error "unmapStorage" | ||
78 | |||
79 | |||
80 | withStorage :: Int -> Maybe FilePath -> FilePath -> ContentInfo | ||
81 | -> (Storage -> IO a) | ||
82 | -> IO a | ||
83 | withStorage blkSize statusPath contentPath t = | ||
84 | bracket (mapStorage blkSize statusPath contentPath t) unmapStorage | ||
85 | |||
86 | isAvailable :: BlockIx -> Storage -> IO Bool | ||
87 | isAvailable ix Storage {..} = error "isAvailable" | ||
88 | |||
89 | |||
90 | putBlk :: Block -> Storage -> IO () | ||
91 | putBlk 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 | ||
97 | getBlk :: BlockIx -> Storage -> IO (Maybe Block) | ||
98 | getBlk 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 | |||
106 | ixInterval :: Int -> BlockIx -> FixedInterval | ||
107 | ixInterval pieceSize BlockIx {..} = | ||
108 | interval (ixPiece * pieceSize + ixOffset) ixLength | ||
109 | |||
110 | blkInterval :: Int -> Block -> FixedInterval | ||
111 | blkInterval pieceSize Block {..} = | ||
112 | interval (blkPiece * pieceSize + blkOffset) (B.length blkData) \ No newline at end of file | ||