diff options
-rw-r--r-- | bittorrent/src/Network/BitTorrent/Exchange/Bitfield.hs | 20 | ||||
-rw-r--r-- | bittorrent/src/System/Torrent/FileMap.hs | 16 |
2 files changed, 27 insertions, 9 deletions
diff --git a/bittorrent/src/Network/BitTorrent/Exchange/Bitfield.hs b/bittorrent/src/Network/BitTorrent/Exchange/Bitfield.hs index 7bae3475..1be9f970 100644 --- a/bittorrent/src/Network/BitTorrent/Exchange/Bitfield.hs +++ b/bittorrent/src/Network/BitTorrent/Exchange/Bitfield.hs | |||
@@ -9,7 +9,7 @@ | |||
9 | -- bitfields. Bitfields are used to keep track indices of complete | 9 | -- bitfields. Bitfields are used to keep track indices of complete |
10 | -- pieces either this peer have or remote peer have. | 10 | -- pieces either this peer have or remote peer have. |
11 | -- | 11 | -- |
12 | -- There are also commonly used piece seletion algorithms | 12 | -- There are also commonly used piece selection algorithms |
13 | -- which used to find out which one next piece to download. | 13 | -- which used to find out which one next piece to download. |
14 | -- Selectors considered to be used in the following order: | 14 | -- Selectors considered to be used in the following order: |
15 | -- | 15 | -- |
@@ -108,12 +108,17 @@ import Data.Torrent | |||
108 | 108 | ||
109 | -- TODO cache some operations | 109 | -- TODO cache some operations |
110 | 110 | ||
111 | -- | Bitfields are represented just as integer sets but with | 111 | -- | Bitfields are represented just as integer sets but with a restriction: |
112 | -- restriction: the each set should be within given interval (or | 112 | -- each integer in the set should be within the given interval. The greatest |
113 | -- subset of the specified interval). Size is used to specify | 113 | -- lower bound of the interval must be zero, so intervals may be specified by |
114 | -- interval, so bitfield of size 10 might contain only indices in | 114 | -- providing a maximum set size. For example, a bitfield of size 10 might |
115 | -- interval [0..9]. | 115 | -- contain only indices in interval [0..9]. |
116 | -- | 116 | -- |
117 | -- By convention, we use the following aliases for Int: | ||
118 | -- | ||
119 | -- [ PieceIx ] an Int member of the Bitfield. | ||
120 | -- | ||
121 | -- [ PieceCount ] maximum set size for a Bitfield. | ||
117 | data Bitfield = Bitfield { | 122 | data Bitfield = Bitfield { |
118 | bfSize :: !PieceCount | 123 | bfSize :: !PieceCount |
119 | , bfSet :: !IntSet | 124 | , bfSet :: !IntSet |
@@ -257,6 +262,7 @@ rarest xs | |||
257 | = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap | 262 | = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap |
258 | where | 263 | where |
259 | freqMap = frequencies xs | 264 | freqMap = frequencies xs |
265 | {-# NOINLINE freqMap #-} | ||
260 | 266 | ||
261 | minIx :: PieceIx -> Frequency | 267 | minIx :: PieceIx -> Frequency |
262 | -> (PieceIx, Frequency) | 268 | -> (PieceIx, Frequency) |
@@ -393,7 +399,7 @@ rarestFirst h a xs = rarest (map (intersection want) xs) | |||
393 | randomFirst :: Selector | 399 | randomFirst :: Selector |
394 | randomFirst = do | 400 | randomFirst = do |
395 | -- randomIO | 401 | -- randomIO |
396 | error "randomFirst" | 402 | error "TODO: randomFirst" |
397 | 403 | ||
398 | endGame :: Selector | 404 | endGame :: Selector |
399 | endGame = strictLast | 405 | endGame = strictLast |
diff --git a/bittorrent/src/System/Torrent/FileMap.hs b/bittorrent/src/System/Torrent/FileMap.hs index 6e8d7f5a..38c475e8 100644 --- a/bittorrent/src/System/Torrent/FileMap.hs +++ b/bittorrent/src/System/Torrent/FileMap.hs | |||
@@ -39,7 +39,7 @@ import Data.Torrent | |||
39 | 39 | ||
40 | data FileEntry = FileEntry | 40 | data FileEntry = FileEntry |
41 | { filePosition :: {-# UNPACK #-} !FileOffset | 41 | { filePosition :: {-# UNPACK #-} !FileOffset |
42 | , fileBytes :: {-# UNPACK #-} !BS.ByteString | 42 | , fileBytes :: {-# UNPACK #-} !BS.ByteString -- XXX: mutable buffer (see 'writeBytes'). |
43 | } deriving (Show, Eq) | 43 | } deriving (Show, Eq) |
44 | 44 | ||
45 | type FileMap = Vector FileEntry | 45 | type FileMap = Vector FileEntry |
@@ -62,6 +62,7 @@ unmapFiles = V.mapM_ unmapEntry | |||
62 | where | 62 | where |
63 | unmapEntry (FileEntry _ (PS fptr _ _)) = finalizeForeignPtr fptr | 63 | unmapEntry (FileEntry _ (PS fptr _ _)) = finalizeForeignPtr fptr |
64 | 64 | ||
65 | -- Unsafe: FileMap 'writeBytes' will modify supplied bytestrings in place. | ||
65 | fromLazyByteString :: BL.ByteString -> FileMap | 66 | fromLazyByteString :: BL.ByteString -> FileMap |
66 | fromLazyByteString lbs = V.unfoldr f (0, lbs) | 67 | fromLazyByteString lbs = V.unfoldr f (0, lbs) |
67 | where | 68 | where |
@@ -70,6 +71,8 @@ fromLazyByteString lbs = V.unfoldr f (0, lbs) | |||
70 | where chunkSize = fromIntegral $ BS.length x | 71 | where chunkSize = fromIntegral $ BS.length x |
71 | 72 | ||
72 | -- | /O(n)/. | 73 | -- | /O(n)/. |
74 | -- | ||
75 | -- Unsafe: mutable buffers are returned without copy. | ||
73 | toLazyByteString :: FileMap -> BL.ByteString | 76 | toLazyByteString :: FileMap -> BL.ByteString |
74 | toLazyByteString = V.foldr f Empty | 77 | toLazyByteString = V.foldr f Empty |
75 | where | 78 | where |
@@ -82,6 +85,7 @@ size m | |||
82 | | FileEntry {..} <- V.unsafeLast m | 85 | | FileEntry {..} <- V.unsafeLast m |
83 | = filePosition + fromIntegral (BS.length fileBytes) | 86 | = filePosition + fromIntegral (BS.length fileBytes) |
84 | 87 | ||
88 | -- | Find the file number for a particular byte offset within a torrent. | ||
85 | bsearch :: FileOffset -> FileMap -> Maybe Int | 89 | bsearch :: FileOffset -> FileMap -> Maybe Int |
86 | bsearch x m | 90 | bsearch x m |
87 | | V.null m = Nothing | 91 | | V.null m = Nothing |
@@ -114,12 +118,17 @@ take len m | |||
114 | s = System.Torrent.FileMap.size m | 118 | s = System.Torrent.FileMap.size m |
115 | 119 | ||
116 | -- | /O(log n + m)/. Do not use this function with 'unmapFiles'. | 120 | -- | /O(log n + m)/. Do not use this function with 'unmapFiles'. |
121 | -- | ||
122 | -- The returned bytestring points directly into an area memory mapped from a | ||
123 | -- file. | ||
117 | unsafeReadBytes :: FileOffset -> FileSize -> FileMap -> BL.ByteString | 124 | unsafeReadBytes :: FileOffset -> FileSize -> FileMap -> BL.ByteString |
118 | unsafeReadBytes off s m | 125 | unsafeReadBytes off s m |
119 | | (l , m') <- System.Torrent.FileMap.drop off m | 126 | | (l , m') <- System.Torrent.FileMap.drop off m |
120 | , (m'', _ ) <- System.Torrent.FileMap.take (off + s) m' | 127 | , (m'', _ ) <- System.Torrent.FileMap.take (off + s) m' |
121 | = BL.take (fromIntegral s) $ BL.drop (fromIntegral l) $ toLazyByteString m'' | 128 | = BL.take (fromIntegral s) $ BL.drop (fromIntegral l) $ toLazyByteString m'' |
122 | 129 | ||
130 | -- The returned bytestring is copied and safe to use after the file is | ||
131 | -- unmapped. | ||
123 | readBytes :: FileOffset -> FileSize -> FileMap -> IO BL.ByteString | 132 | readBytes :: FileOffset -> FileSize -> FileMap -> IO BL.ByteString |
124 | readBytes off s m = do | 133 | readBytes off s m = do |
125 | let bs_copy = BL.copy $ unsafeReadBytes off s m | 134 | let bs_copy = BL.copy $ unsafeReadBytes off s m |
@@ -129,6 +138,8 @@ readBytes off s m = do | |||
129 | forceLBS Empty = return () | 138 | forceLBS Empty = return () |
130 | forceLBS (Chunk _ x) = forceLBS x | 139 | forceLBS (Chunk _ x) = forceLBS x |
131 | 140 | ||
141 | -- UNSAFE: Uses the first byte string as a pointer to mutable data and writes | ||
142 | -- the contents of the second bytestring there. | ||
132 | bscpy :: BL.ByteString -> BL.ByteString -> IO () | 143 | bscpy :: BL.ByteString -> BL.ByteString -> IO () |
133 | bscpy (PS _ _ 0 `Chunk` dest_rest) src = bscpy dest_rest src | 144 | bscpy (PS _ _ 0 `Chunk` dest_rest) src = bscpy dest_rest src |
134 | bscpy dest (PS _ _ 0 `Chunk` src_rest) = bscpy dest src_rest | 145 | bscpy dest (PS _ _ 0 `Chunk` src_rest) = bscpy dest src_rest |
@@ -144,8 +155,9 @@ bscpy (PS dest_fptr dest_off dest_size `Chunk` dest_rest) | |||
144 | (PS src_fptr (src_off + csize) (src_size - csize) `Chunk` src_rest) | 155 | (PS src_fptr (src_off + csize) (src_size - csize) `Chunk` src_rest) |
145 | bscpy _ _ = return () | 156 | bscpy _ _ = return () |
146 | 157 | ||
158 | -- UNSAFE: Mutates bytestring contents within the provided FileMap. | ||
147 | writeBytes :: FileOffset -> BL.ByteString -> FileMap -> IO () | 159 | writeBytes :: FileOffset -> BL.ByteString -> FileMap -> IO () |
148 | writeBytes off lbs m = bscpy dest src | 160 | writeBytes off lbs m = bscpy dest src |
149 | where | 161 | where |
150 | src = BL.take (fromIntegral (BL.length dest)) lbs | 162 | src = BL.take (fromIntegral (BL.length dest)) lbs |
151 | dest = unsafeReadBytes off (fromIntegral (BL.length lbs)) m \ No newline at end of file | 163 | dest = unsafeReadBytes off (fromIntegral (BL.length lbs)) m |