summaryrefslogtreecommitdiff
path: root/src/Data/BEncode/Internal.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Data/BEncode/Internal.hs')
-rw-r--r--src/Data/BEncode/Internal.hs57
1 files changed, 40 insertions, 17 deletions
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