summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-09-29 06:01:06 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-09-29 06:01:06 +0400
commit573487ff0d758e1c500c26bc1e8b90dd155eb97b (patch)
tree9e177fe4a599e1d12ad921e0ca7c8b39e1c7e26d
parente0ad240589038f9cc61a43844067ad021c0a903f (diff)
Introduce BDictMap
-rw-r--r--bencoding.cabal2
-rw-r--r--src/Data/BEncode.hs47
-rw-r--r--src/Data/BEncode/BDict.hs90
3 files changed, 118 insertions, 21 deletions
diff --git a/bencoding.cabal b/bencoding.cabal
index b4cd89f..bbbf001 100644
--- a/bencoding.cabal
+++ b/bencoding.cabal
@@ -39,6 +39,8 @@ library
39 default-extensions: PatternGuards 39 default-extensions: PatternGuards
40 hs-source-dirs: src 40 hs-source-dirs: src
41 exposed-modules: Data.BEncode 41 exposed-modules: Data.BEncode
42 , Data.BEncode.BDict
43
42 build-depends: base == 4.* 44 build-depends: base == 4.*
43 , ghc-prim 45 , ghc-prim
44 , deepseq == 1.3.* 46 , deepseq == 1.3.*
diff --git a/src/Data/BEncode.hs b/src/Data/BEncode.hs
index b6897ec..8858730 100644
--- a/src/Data/BEncode.hs
+++ b/src/Data/BEncode.hs
@@ -106,6 +106,7 @@ import Control.Applicative
106import Control.DeepSeq 106import Control.DeepSeq
107import Control.Monad 107import Control.Monad
108import Data.Int 108import Data.Int
109import Data.List as L
109import Data.Maybe (mapMaybe) 110import Data.Maybe (mapMaybe)
110import Data.Monoid 111import Data.Monoid
111import Data.Foldable (foldMap) 112import Data.Foldable (foldMap)
@@ -135,12 +136,13 @@ import qualified Text.ParserCombinators.ReadP as ReadP
135import GHC.Generics 136import GHC.Generics
136#endif 137#endif
137 138
139import Data.BEncode.BDict as BD
140
138 141
139type BInteger = Integer 142type BInteger = Integer
140type BString = ByteString 143type BString = ByteString
141type BList = [BValue] 144type BList = [BValue]
142type BDict = Map BKey BValue 145type BDict = BDictMap BValue
143type BKey = ByteString
144 146
145-- | 'BEncode' is straightforward ADT for b-encoded values. Please 147-- | 'BEncode' is straightforward ADT for b-encoded values. Please
146-- note that since dictionaries are sorted, in most cases we can 148-- note that since dictionaries are sorted, in most cases we can
@@ -295,7 +297,7 @@ gfromM1S :: forall c. Selector c
295 => GBEncodable f BValue 297 => GBEncodable f BValue
296 => BDict -> Result (M1 i c f p) 298 => BDict -> Result (M1 i c f p)
297gfromM1S dict 299gfromM1S dict
298 | Just va <- M.lookup (BC.pack (selRename name)) dict = M1 <$> gfrom va 300 | Just va <- BD.lookup (BC.pack (selRename name)) dict = M1 <$> gfrom va
299 | otherwise = decodingError $ "generic: Selector not found " ++ show name 301 | otherwise = decodingError $ "generic: Selector not found " ++ show name
300 where 302 where
301 name = selName (error "gfromM1S: impossible" :: M1 i c f p) 303 name = selName (error "gfromM1S: impossible" :: M1 i c f p)
@@ -303,7 +305,7 @@ gfromM1S dict
303instance (Selector s, GBEncodable f BValue) 305instance (Selector s, GBEncodable f BValue)
304 => GBEncodable (M1 S s f) BDict where 306 => GBEncodable (M1 S s f) BDict where
305 {-# INLINE gto #-} 307 {-# INLINE gto #-}
306 gto s @ (M1 x) = BC.pack (selRename (selName s)) `M.singleton` gto x 308 gto s @ (M1 x) = BC.pack (selRename (selName s)) `BD.singleton` gto x
307 309
308 {-# INLINE gfrom #-} 310 {-# INLINE gfrom #-}
309 gfrom = gfromM1S 311 gfrom = gfromM1S
@@ -377,14 +379,15 @@ instance BEncodable BList where
377 fromBEncode _ = decodingError "BList" 379 fromBEncode _ = decodingError "BList"
378 {-# INLINE fromBEncode #-} 380 {-# INLINE fromBEncode #-}
379 381
380instance BEncodable BDict where 382-}
381 toBEncode = BDict 383
384instance BEncode BDict where
385 toBEncode = BDict
382 {-# INLINE toBEncode #-} 386 {-# INLINE toBEncode #-}
383 387
384 fromBEncode (BDict d) = pure d 388 fromBEncode (BDict d) = pure d
385 fromBEncode _ = decodingError "BDict" 389 fromBEncode _ = decodingError "BDict"
386 {-# INLINE fromBEncode #-} 390 {-# INLINE fromBEncode #-}
387-}
388 391
389{-------------------------------------------------------------------- 392{--------------------------------------------------------------------
390-- Integral instances 393-- Integral instances
@@ -401,7 +404,7 @@ toBEncodeIntegral = BInteger . fromIntegral
401fromBEncodeIntegral :: forall a. Typeable a => Integral a => BValue -> Result a 404fromBEncodeIntegral :: forall a. Typeable a => Integral a => BValue -> Result a
402fromBEncodeIntegral (BInteger i) = pure (fromIntegral i) 405fromBEncodeIntegral (BInteger i) = pure (fromIntegral i)
403fromBEncodeIntegral _ 406fromBEncodeIntegral _
404 = decodingError $ show $ typeOf (undefined :: a) 407 = decodingError $ show $ typeOf (error "fromBEncodeIntegral: imposible" :: a)
405{-# INLINE fromBEncodeIntegral #-} 408{-# INLINE fromBEncodeIntegral #-}
406 409
407 410
@@ -500,16 +503,17 @@ instance BEncode Text where
500 503
501instance BEncode a => BEncode [a] where 504instance BEncode a => BEncode [a] where
502 {-# SPECIALIZE instance BEncode BList #-} 505 {-# SPECIALIZE instance BEncode BList #-}
503 toBEncode = BList . map toBEncode 506 toBEncode = BList . L.map toBEncode
504 {-# INLINE toBEncode #-} 507 {-# INLINE toBEncode #-}
505 508
506 fromBEncode (BList xs) = mapM fromBEncode xs 509 fromBEncode (BList xs) = mapM fromBEncode xs
507 fromBEncode _ = decodingError "list" 510 fromBEncode _ = decodingError "list"
508 {-# INLINE fromBEncode #-} 511 {-# INLINE fromBEncode #-}
509 512
513{-
510instance BEncode a => BEncode (Map BKey a) where 514instance BEncode a => BEncode (Map BKey a) where
511 {-# SPECIALIZE instance BEncode BDict #-} 515 {-# SPECIALIZE instance BEncode (Map BKey BValue) #-}
512 toBEncode = BDict . M.map toBEncode 516 toBEncode = BDict . -- BD.map toBEncode
513 {-# INLINE toBEncode #-} 517 {-# INLINE toBEncode #-}
514 518
515 fromBEncode (BDict d) = traverse fromBEncode d 519 fromBEncode (BDict d) = traverse fromBEncode d
@@ -524,7 +528,7 @@ instance (Eq a, BEncode a) => BEncode (Set a) where
524 fromBEncode (BList xs) = S.fromAscList <$> traverse fromBEncode xs 528 fromBEncode (BList xs) = S.fromAscList <$> traverse fromBEncode xs
525 fromBEncode _ = decodingError "Data.Set" 529 fromBEncode _ = decodingError "Data.Set"
526 {-# INLINE fromBEncode #-} 530 {-# INLINE fromBEncode #-}
527 531-}
528instance BEncode Version where 532instance BEncode Version where
529 toBEncode = toBEncode . BC.pack . showVersion 533 toBEncode = toBEncode . BC.pack . showVersion
530 {-# INLINE toBEncode #-} 534 {-# INLINE toBEncode #-}
@@ -639,13 +643,13 @@ key -->? mval = Assoc $ ((,) key . toBEncode) <$> mval
639 643
640-- | Build BEncode dictionary using key -> value description. 644-- | Build BEncode dictionary using key -> value description.
641fromAssocs :: [Assoc] -> BValue 645fromAssocs :: [Assoc] -> BValue
642fromAssocs = BDict . M.fromList . mapMaybe unAssoc 646fromAssocs = undefined -- BDict . M.fromList . mapMaybe unAssoc
643{-# INLINE fromAssocs #-} 647{-# INLINE fromAssocs #-}
644 648
645-- | A faster version of 'fromAssocs'. Should be used only when keys 649-- | A faster version of 'fromAssocs'. Should be used only when keys
646-- in builder list are sorted by ascending. 650-- in builder list are sorted by ascending.
647fromAscAssocs :: [Assoc] -> BValue 651fromAscAssocs :: [Assoc] -> BValue
648fromAscAssocs = BDict . M.fromAscList . mapMaybe unAssoc 652fromAscAssocs = BDict . BD.fromAscList . mapMaybe unAssoc
649{-# INLINE fromAscAssocs #-} 653{-# INLINE fromAscAssocs #-}
650 654
651{-------------------------------------------------------------------- 655{--------------------------------------------------------------------
@@ -670,7 +674,7 @@ fromAscAssocs = BDict . M.fromAscList . mapMaybe unAssoc
670-- then whole destructuring fail. 674-- then whole destructuring fail.
671reqKey :: BEncode a => BDict -> BKey -> Result a 675reqKey :: BEncode a => BDict -> BKey -> Result a
672reqKey d key 676reqKey d key
673 | Just b <- M.lookup key d = fromBEncode b 677 | Just b <- BD.lookup key d = fromBEncode b
674 | otherwise = Left msg 678 | otherwise = Left msg
675 where 679 where
676 msg = "required field `" ++ BC.unpack key ++ "' not found" 680 msg = "required field `" ++ BC.unpack key ++ "' not found"
@@ -679,7 +683,7 @@ reqKey d key
679-- 'Nothing'. 683-- 'Nothing'.
680optKey :: BEncode a => BDict -> BKey -> Result (Maybe a) 684optKey :: BEncode a => BDict -> BKey -> Result (Maybe a)
681optKey d key 685optKey d key
682 | Just b <- M.lookup key d 686 | Just b <- BD.lookup key d
683 , Right r <- fromBEncode b = return (Just r) 687 , Right r <- fromBEncode b = return (Just r)
684 | otherwise = return Nothing 688 | otherwise = return Nothing
685 689
@@ -759,7 +763,7 @@ builder = go
759 foldMap go l <> 763 foldMap go l <>
760 B.word8 (c2w 'e') 764 B.word8 (c2w 'e')
761 go (BDict d) = B.word8 (c2w 'd') <> 765 go (BDict d) = B.word8 (c2w 'd') <>
762 foldMap mkKV (M.toAscList d) <> 766 foldMap mkKV (BD.toAscList d) <>
763 B.word8 (c2w 'e') 767 B.word8 (c2w 'e')
764 where 768 where
765 mkKV (k, v) = buildString k <> go v 769 mkKV (k, v) = buildString k <> go v
@@ -786,7 +790,7 @@ parser = valueP
786 'l' -> P.anyChar *> ((BList <$> listBody) <* P.anyChar) 790 'l' -> P.anyChar *> ((BList <$> listBody) <* P.anyChar)
787 'd' -> do 791 'd' -> do
788 P.anyChar 792 P.anyChar
789 (BDict . M.fromDistinctAscList <$> 793 (BDict . BD.fromAscList <$>
790 many ((,) <$> stringP <*> valueP)) 794 many ((,) <$> stringP <*> valueP))
791 <* P.anyChar 795 <* P.anyChar
792 t -> fail ("bencode unknown tag: " ++ [t]) 796 t -> fail ("bencode unknown tag: " ++ [t])
@@ -819,15 +823,16 @@ parser = valueP
819--------------------------------------------------------------------} 823--------------------------------------------------------------------}
820 824
821ppBS :: ByteString -> Doc 825ppBS :: ByteString -> Doc
822ppBS = text . map w2c . B.unpack 826ppBS = text . L.map w2c . B.unpack
823 827
824-- | Convert to easily readable JSON-like document. Typically used for 828-- | Convert to easily readable JSON-like document. Typically used for
825-- debugging purposes. 829-- debugging purposes.
826ppBEncode :: BValue -> Doc 830ppBEncode :: BValue -> Doc
827ppBEncode (BInteger i) = int $ fromIntegral i 831ppBEncode (BInteger i) = int $ fromIntegral i
828ppBEncode (BString s) = ppBS s 832ppBEncode (BString s) = ppBS s
829ppBEncode (BList l) = brackets $ hsep $ punctuate comma $ map ppBEncode l 833ppBEncode (BList l)
834 = brackets $ hsep $ punctuate comma $ L.map ppBEncode l
830ppBEncode (BDict d) 835ppBEncode (BDict d)
831 = braces $ vcat $ punctuate comma $ map ppKV $ M.toAscList d 836 = braces $ vcat $ punctuate comma $ L.map ppKV $ BD.toAscList d
832 where 837 where
833 ppKV (k, v) = ppBS k <+> colon <+> ppBEncode v 838 ppKV (k, v) = ppBS k <+> colon <+> ppBEncode v
diff --git a/src/Data/BEncode/BDict.hs b/src/Data/BEncode/BDict.hs
new file mode 100644
index 0000000..c674b21
--- /dev/null
+++ b/src/Data/BEncode/BDict.hs
@@ -0,0 +1,90 @@
1module Data.BEncode.BDict
2 ( BKey
3 , BDictMap
4
5 -- * Construction
6 , Data.BEncode.BDict.empty
7 , Data.BEncode.BDict.singleton
8
9 -- * Query
10 , Data.BEncode.BDict.lookup
11
12 -- * Combine
13 , Data.BEncode.BDict.union
14
15 -- * Transformations
16 , Data.BEncode.BDict.map
17
18 -- * Conversion
19 , Data.BEncode.BDict.fromAscList
20 , Data.BEncode.BDict.toAscList
21 ) where
22
23import Control.DeepSeq
24import Data.ByteString as BS
25import Data.Monoid
26
27
28type BKey = ByteString
29
30-- STRICTNESS NOTE: the BKey is always evaluated since we either use a
31-- literal or compare before insert to the dict
32--
33-- LAYOUT NOTE: we don't use [StrictPair BKey a] since it introduce
34-- one more constructor per cell
35--
36
37-- | BDictMap is list of key value pairs sorted by keys.
38data BDictMap a
39 = Cons !BKey a (BDictMap a)
40 | Nil
41 deriving (Show, Read, Eq, Ord)
42
43instance NFData a => NFData (BDictMap a) where
44 rnf Nil = ()
45 rnf (Cons _ v xs)= rnf v `seq` rnf xs
46
47instance Functor BDictMap where
48 fmap = Data.BEncode.BDict.map
49 {-# INLINE fmap #-}
50
51--instance Foldable BDictMap where
52
53instance Monoid (BDictMap a) where
54 mempty = Data.BEncode.BDict.empty
55 mappend = Data.BEncode.BDict.union
56
57empty :: BDictMap a
58empty = Nil
59{-# INLINE empty #-}
60
61singleton :: BKey -> a -> BDictMap a
62singleton k v = Cons k v Nil
63{-# INLINE singleton #-}
64
65lookup :: BKey -> BDictMap a -> Maybe a
66lookup x = go
67 where
68 go Nil = Nothing
69 go (Cons k v xs)
70 | k == x = Just v
71 | otherwise = go xs
72{-# INLINE lookup #-}
73
74union :: BDictMap a -> BDictMap a -> BDictMap a
75union = undefined
76
77map :: (a -> b) -> BDictMap a -> BDictMap b
78map f = go
79 where
80 go Nil = Nil
81 go (Cons k v xs) = Cons k (f v) (go xs)
82{-# INLINE map #-}
83
84fromAscList :: [(BKey, a)] -> BDictMap a
85fromAscList [] = Nil
86fromAscList ((k, v) : xs) = Cons k v (fromAscList xs)
87
88toAscList :: BDictMap a -> [(BKey, a)]
89toAscList Nil = []
90toAscList (Cons k v xs) = (k, v) : toAscList xs \ No newline at end of file