summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/PeerWire/Bitfield.hs
blob: 5cbd4e86b8a51b001ec222444d8f1551c06b84ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
-- |
--   Copyright   :  (c) Sam T. 2013
--   License     :  MIT
--   Maintainer  :  pxqr.sta@gmail.com
--   Stability   :  experimental
--   Portability :  portable
--
--
--   This module provides Bitfield datatype used to represent sets of
--   piece indexes any peer have. All associated operations should be
--   defined here as well.
--
module Network.BitTorrent.PeerWire.Bitfield
       ( Bitfield(..)

         -- * Construction
       , empty, full
       , fromByteString, toByteString

         -- * Query
       , findMin, findMax, difference

         -- * Serialization
       , getBitfield, putBitfield, bitfieldByteCount
       ) where

import Control.Applicative hiding (empty)
import Data.Array.Unboxed
import Data.Bits
import           Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.List as L
import Data.Maybe
import Data.Serialize
import Data.Word

import Network.BitTorrent.PeerWire.Block
import Data.Torrent

newtype Bitfield = MkBitfield {
    bfBits :: ByteString
--  , bfSize :: Int
  } deriving (Show, Eq, Ord)


empty :: Int -> Bitfield
empty n = MkBitfield $ B.replicate (sizeInBase n 8) 0

full :: Int -> Bitfield
full n = MkBitfield $ B.replicate (sizeInBase n 8)  (complement 0)

fromByteString :: ByteString -> Bitfield
fromByteString = MkBitfield
{-# INLINE fromByteString #-}

toByteString :: Bitfield -> ByteString
toByteString = bfBits
{-# INLINE toByteString #-}

combine :: [ByteString] -> Maybe ByteString
combine []         = Nothing
combine as@(a : _) = return $ foldr andBS empty as
  where
    andBS x acc = B.pack (B.zipWith (.&.) x acc)
    empty = B.replicate (B.length a) 0

frequencies :: [Bitfield] -> UArray PieceIx Int
frequencies = undefined

diffWord8 :: Word8 -> Word8 -> Word8
diffWord8 a b = a .&. (a `xor` b)
{-# INLINE diffWord8 #-}

difference :: Bitfield -> Bitfield -> Bitfield
difference a b = MkBitfield $ B.pack $ B.zipWith diffWord8 (bfBits a) (bfBits b)
{-# INLINE difference #-}

difference' :: ByteString -> ByteString -> ByteString
difference' a b = undefined
  where
    go i = undefined


-- TODO: bit tricks
findMinWord8 :: Word8 -> Maybe Int
findMinWord8 b = L.findIndex (testBit b) [0..bitSize (undefined :: Word8) - 1]
{-# INLINE findMinWord8 #-}

-- | Get min index of piece that the peer have.
findMin :: Bitfield -> Maybe PieceIx
findMin (MkBitfield b) = do
  byteIx <- B.findIndex (0 /=) b
  bitIx  <- findMinWord8 (B.index b byteIx)
  return $ byteIx * bitSize (undefined :: Word8) + bitIx
{-# INLINE findMin #-}

findMaxWord8 :: Word8 -> Maybe Int
findMaxWord8 = error "bitfield: findMaxWord8"

findMax :: Bitfield -> Maybe PieceIx
findMax = error "bitfield: findMax"
{-# INLINE findMax #-}


getBitfield :: Int -> Get Bitfield
getBitfield n = MkBitfield <$> getBytes n
{-# INLINE getBitfield #-}

putBitfield :: Bitfield -> Put
putBitfield = putByteString . bfBits
{-# INLINE putBitfield #-}

bitfieldByteCount :: Bitfield -> Int
bitfieldByteCount = B.length . bfBits
{-# INLINE bitfieldByteCount #-}