summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-01-18 20:13:45 -0500
committerjoe <joe@jerkface.net>2017-01-18 20:13:45 -0500
commita4359d2c67811e71368524d17aeb3809dae657da (patch)
treee3e240de09b6b446a6e8ee38429aa23ef1ef396d
parent27957be6c747a9ce4a98273e0099f15dd1a6f163 (diff)
Backported to older bytestring.
-rw-r--r--bencoding.cabal20
-rw-r--r--src/Data/BEncode/Internal.hs57
-rw-r--r--src/Data/BEncode/Types.hs6
-rw-r--r--tests/properties.hs7
4 files changed, 66 insertions, 24 deletions
diff --git a/bencoding.cabal b/bencoding.cabal
index c6fa2af..eddcd53 100644
--- a/bencoding.cabal
+++ b/bencoding.cabal
@@ -19,6 +19,10 @@ description:
19extra-source-files: README.md 19extra-source-files: README.md
20 , ChangeLog 20 , ChangeLog
21 21
22flag builder
23 description: Use older bytestring package and bytestring-builder.
24 default: False
25
22source-repository head 26source-repository head
23 type: git 27 type: git
24 location: git://github.com/cobit/bencoding.git 28 location: git://github.com/cobit/bencoding.git
@@ -41,15 +45,16 @@ library
41 build-depends: base == 4.* 45 build-depends: base == 4.*
42 , ghc-prim 46 , ghc-prim
43 , integer-gmp 47 , integer-gmp
44 , deepseq == 1.3.* 48 , deepseq >= 1.3
45
46 , mtl 49 , mtl
47
48 , attoparsec >= 0.10 50 , attoparsec >= 0.10
49 , bytestring >= 0.10
50 , text >= 0.11 51 , text >= 0.11
51 , pretty 52 , pretty
52 ghc-options: -Wall -O2 -fno-warn-unused-do-bind 53 ghc-options: -Wall -O2 -fno-warn-unused-do-bind
54 if flag(builder)
55 build-depends: bytestring >= 0.9, bytestring-builder
56 else
57 build-depends: bytestring >= 0.10
53 58
54 59
55test-suite properties 60test-suite properties
@@ -61,12 +66,15 @@ test-suite properties
61 , ghc-prim 66 , ghc-prim
62 67
63 , containers >= 0.4 68 , containers >= 0.4
64 , bytestring >= 0.10
65 , attoparsec >= 0.10 69 , attoparsec >= 0.10
66 70
67 , bencoding 71 , bencoding
68 , hspec 72 , hspec
69 , QuickCheck 73 , QuickCheck
74 if flag(builder)
75 build-depends: bytestring >= 0.9, bytestring-builder
76 else
77 build-depends: bytestring >= 0.10
70 78
71 ghc-options: -Wall -fno-warn-orphans 79 ghc-options: -Wall -fno-warn-orphans
72 80
@@ -81,7 +89,7 @@ benchmark bench-comparison
81 , deepseq 89 , deepseq
82 90
83 , attoparsec >= 0.10 91 , attoparsec >= 0.10
84 , bytestring >= 0.10 92 , bytestring >= 0.9
85 93
86 , criterion 94 , criterion
87 95
diff --git a/src/Data/BEncode/Internal.hs b/src/Data/BEncode/Internal.hs
index e5ba89c..1532fe6 100644
--- a/src/Data/BEncode/Internal.hs
+++ b/src/Data/BEncode/Internal.hs
@@ -9,6 +9,7 @@
9-- don't need to import this module, use 'Data.BEncode' instead. 9-- don't need to import this module, use 'Data.BEncode' instead.
10-- 10--
11{-# LANGUAGE MagicHash #-} 11{-# LANGUAGE MagicHash #-}
12{-# LANGUAGE CPP #-}
12module Data.BEncode.Internal 13module Data.BEncode.Internal
13 ( -- * Parsing 14 ( -- * Parsing
14 parser 15 parser
@@ -26,12 +27,20 @@ import qualified Data.Attoparsec.ByteString.Char8 as P
26import Data.ByteString as B 27import Data.ByteString as B
27import Data.ByteString.Internal as B (c2w, w2c) 28import Data.ByteString.Internal as B (c2w, w2c)
28import qualified Data.ByteString.Lazy as Lazy 29import qualified Data.ByteString.Lazy as Lazy
30#if MIN_VERSION_bytestring(0,10,0)
29import qualified Data.ByteString.Lazy.Builder as B 31import qualified Data.ByteString.Lazy.Builder as B
30import qualified Data.ByteString.Lazy.Builder.ASCII as B 32import qualified Data.ByteString.Lazy.Builder.ASCII as B
33#else
34import qualified Data.ByteString.Builder as B
35-- import qualified Data.ByteString.Builder.ASCII as B
36-- import qualified Data.ByteString.Builder.Internal as B
37#endif
31import Data.Foldable 38import Data.Foldable
32import Data.List as L 39import Data.List as L
33import Data.Monoid 40import Data.Monoid
34import Text.PrettyPrint hiding ((<>)) 41import Text.PrettyPrint hiding ((<>))
42import Data.Char
43import Data.Maybe
35 44
36import Data.BEncode.Types 45import Data.BEncode.Types
37import Data.BEncode.BDict as BD 46import Data.BEncode.BDict as BD
@@ -74,6 +83,9 @@ builder = go
74build :: BValue -> Lazy.ByteString 83build :: BValue -> Lazy.ByteString
75build = B.toLazyByteString . builder 84build = B.toLazyByteString . builder
76 85
86
87sample = "lld10:xOo@-ovQ\b}i8eeded12:]\ENQ89gJp\DC1Y\t!]17:\SOHRQ8\DLE\ESC\NULiUSRo\t.M<gei18eldede8:VX^ejm\SO_d12:.J*\DLEIc\SIV\ESCun\SOHdeede8:tg!\"lU\SOH\DEL1:Pe16:X\EMk\ESCGaek)\DC4'\t+\ESChhei-7e16:<OJ\v9\".i;\DC4[=]_D9de16:$-giCtwedm!\CAN\aA\DC3{dee"
88
77{-------------------------------------------------------------------- 89{--------------------------------------------------------------------
78-- Deserialization 90-- Deserialization
79--------------------------------------------------------------------} 91--------------------------------------------------------------------}
@@ -81,45 +93,56 @@ build = B.toLazyByteString . builder
81-- TODO try to replace peekChar with something else 93-- TODO try to replace peekChar with something else
82-- | BEncode format parser according to specification. 94-- | BEncode format parser according to specification.
83parser :: Parser BValue 95parser :: Parser BValue
84parser = valueP 96parser = valueP Nothing
85 where 97 where
86 valueP = do 98 valueP prior = do
87 mc <- P.peekChar 99 mc <- maybe (optional P.anyChar) (return . Just) prior
88 case mc of 100 case mc of
89 Nothing -> fail "end of input" 101 Nothing -> fail "end of input"
90 Just c -> 102 Just c ->
91 case c of 103 case c of
92 -- if we have digit it always should be string length 104 -- if we have digit it always should be string length
93 di | di <= '9' -> BString <$> stringP 105 di | '0' <= di && di <= '9' -> BString <$> stringP c
94 'i' -> P.anyChar *> ((BInteger <$> integerP) <* P.anyChar) 106 'i' -> ((BInteger <$> integerP) <* P.char 'e') -- P.anyChar)
95 'l' -> P.anyChar *> ((BList <$> listBodyP) <* P.anyChar) 107 'l' -> ((BList <$> listBodyP) ) -- <* P.anyChar)
96 'd' -> P.anyChar *> (BDict <$> dictBodyP) <* P.anyChar 108 'd' -> (BDict <$> dictBodyP) -- <* P.anyChar
97 t -> fail ("bencode unknown tag: " ++ [t]) 109 t -> fail ("bencode unknown tag: " ++ [t])
98 110
99 dictBodyP :: Parser BDict 111 dictBodyP :: Parser BDict
100 dictBodyP = Cons <$> stringP <*> valueP <*> dictBodyP 112 dictBodyP =
101 <|> pure Nil 113 (P.char 'e' *> pure Nil)
114 <|> do c <- P.satisfy isDigit -- P.anyChar
115 Cons <$> stringP c <*> valueP Nothing <*> dictBodyP
102 116
103 listBodyP = do 117 listBodyP = do
104 c <- P.peekChar 118 c <- optional P.anyChar
105 case c of 119 case c of
106 Just 'e' -> return [] 120 Just 'e' -> return []
107 _ -> (:) <$> valueP <*> listBodyP 121 _ -> (:) <$> valueP c <*> listBodyP
108 122
109 stringP :: Parser ByteString 123 leadingDigit c zeros n0
110 stringP = do 124 | n0==(-10) = d * 10^(fromIntegral (B.length zeros))
111 n <- P.decimal :: Parser Int 125 | n0/=0 = d * 10^(fromIntegral (B.length zeros) + truncate (1+logBase 10 (fromIntegral n0))) + n0
126 | otherwise = d * 10
127 where d = fromIntegral $ ord c - 48
128
129 stringP :: Char -> Parser ByteString
130 stringP c = do
131 zeros <- P.takeWhile (=='0')
132 n0 <- (P.decimal <|> pure (-10)) :: Parser Int
133 let n = leadingDigit c zeros n0
112 P.char ':' 134 P.char ':'
113 P.take n 135 P.take n
114 {-# INLINE stringP #-} 136 {-# INLINE stringP #-}
115 137
116 integerP :: Parser Integer 138 integerP :: Parser Integer
117 integerP = do 139 integerP = do
118 c <- P.peekChar 140 c <- optional P.anyChar
119 case c of 141 case c of
120 Just '-' -> do 142 Just '-' -> do
121 P.anyChar
122 negate <$> P.decimal 143 negate <$> P.decimal
144 Just c' -> do zeros <- P.takeWhile (=='0')
145 leadingDigit c' zeros <$> (P.decimal <|> pure (-10))
123 _ -> P.decimal 146 _ -> P.decimal
124 {-# INLINE integerP #-} 147 {-# INLINE integerP #-}
125 148
diff --git a/src/Data/BEncode/Types.hs b/src/Data/BEncode/Types.hs
index 15f570f..c1e1f01 100644
--- a/src/Data/BEncode/Types.hs
+++ b/src/Data/BEncode/Types.hs
@@ -7,6 +7,7 @@
7-- 7--
8-- Types for working with bencode data. 8-- Types for working with bencode data.
9-- 9--
10{-# LANGUAGE CPP #-}
10module Data.BEncode.Types 11module Data.BEncode.Types
11 ( -- * Types 12 ( -- * Types
12 BInteger 13 BInteger
@@ -51,6 +52,11 @@ data BValue
51 | BDict BDict -- ^ bencode key-value dictionary. 52 | BDict BDict -- ^ bencode key-value dictionary.
52 deriving (Show, Read, Eq, Ord) 53 deriving (Show, Read, Eq, Ord)
53 54
55#if !MIN_VERSION_bytestring(0,10,0)
56instance NFData ByteString where
57 rnf s = seq s ()
58#endif
59
54instance NFData BValue where 60instance NFData BValue where
55 rnf (BInteger i) = rnf i 61 rnf (BInteger i) = rnf i
56 rnf (BString s) = rnf s 62 rnf (BString s) = rnf s
diff --git a/tests/properties.hs b/tests/properties.hs
index 876b954..478dd8c 100644
--- a/tests/properties.hs
+++ b/tests/properties.hs
@@ -1,4 +1,5 @@
1{-# LANGUAGE DeriveGeneric #-} 1{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE CPP #-}
2{-# OPTIONS -fno-warn-unused-binds #-} 3{-# OPTIONS -fno-warn-unused-binds #-}
3module Main (main) where 4module Main (main) where
4 5
@@ -55,8 +56,12 @@ instance Arbitrary FileInfo where
55 56
56data T a = T 57data T a = T
57 58
59#if !MIN_VERSION_bytestring(0,10,0)
60toStrict bs = BS.concat $ BL.toChunks bs
61#endif
62
58prop_bencodable :: Eq a => BEncode a => T a -> a -> Bool 63prop_bencodable :: Eq a => BEncode a => T a -> a -> Bool
59prop_bencodable _ x = decode (BL.toStrict (encode x)) == Right x 64prop_bencodable _ x = decode (toStrict (encode x)) == Right x
60 65
61main :: IO () 66main :: IO ()
62main = hspec $ do 67main = hspec $ do