{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fglasgow-exts #-}
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
#include "MachDeps.h"
#endif
#include "Common.h"
module Data.Binary.Strict.IncrementalGet (
Get
, Result(..)
, runGet
, skip
, bytesRead
, remaining
, isEmpty
, plus
, zero
, spanOf
, suspend
, getWord8
, getByteString
, getWord16be
, getWord32be
, getWord64be
, getWord16le
, getWord32le
, getWord64le
, getWordhost
, getWord16host
, getWord32host
, getWord64host
) where
import Control.Applicative(Alternative(..), Applicative(..))
import Control.Monad (MonadPlus(..), ap)
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Internal as BL
import Foreign
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
import GHC.Base
import GHC.Word
#endif
import qualified Data.Binary.Strict.Class as Class
data S = S {-# UNPACK #-} !BL.ByteString
{-# UNPACK #-} !Int
{-# UNPACK #-} ![B.ByteString]
{-# UNPACK #-} !Int
data Result a = Failed String
| Finished B.ByteString a
| Partial (B.ByteString -> Result a)
data IResult a = IFailed S String
| IFinished S a
| IPartial (B.ByteString -> IResult a)
instance Show (IResult a) where
show (IFailed _ err) = "IFailed " ++ err
show (IFinished _ _) = "IFinished"
show (IPartial _) = "IPartial"
instance (Show a) => Show (Result a) where
show (Failed err) = "Failed " ++ err
show (Finished rest rs) = "Finished " ++ show rest ++ " " ++ show rs
show (Partial _) = "Partial"
newtype Get r a = Get { unGet :: S -> (a -> S -> IResult r) -> IResult r }
instance Functor (Get r) where
fmap f m = Get (\s -> \cont -> unGet m s (cont . f))
instance Monad (Get r) where
return a = Get (\s -> \k -> k a s)
m >>= k = Get (\s -> \cont -> unGet m s (\a -> \s' -> unGet (k a) s' cont))
#ifdef MIN_VERSION_GLASGOW_HASKELL
#if MIN_VERSION_GLASGOW_HASKELL(8,8,0,0)
instance MonadFail (Get r) where
fail err = Get (\s -> const $ IFailed s err)
#else
fail err = Get (\s -> const $ IFailed s err)
#endif
#endif
get :: Get r S
get = Get (\s -> \k -> k s s)
strictToLazy :: B.ByteString -> BL.ByteString
strictToLazy x
| B.null x = BL.Empty
| otherwise = BL.Chunk x BL.Empty
lazyToStrict :: BL.ByteString -> B.ByteString
lazyToStrict = B.concat . BL.toChunks
initState :: B.ByteString -> S
initState input = S (strictToLazy input) 0 [] 0
{-# INLINE initState #-}
toplevelTranslate :: IResult a -> Result a
toplevelTranslate (IFailed _ err) = Failed err
toplevelTranslate (IFinished (S rest _ _ _) value) = Finished (lazyToStrict rest) value
toplevelTranslate (IPartial k) = Partial $ toplevelTranslate . k
terminalContinuation :: a -> S -> IResult a
terminalContinuation v s = IFinished s v
runGet :: Get r r -> B.ByteString -> Result r
runGet m input =
toplevelTranslate $ unGet m (initState input) terminalContinuation
cutContinuation :: (a -> S -> IResult r) -> a -> S -> IResult r
cutContinuation k v s =
case k v s of
IFailed (S lb i adds failDepth) err -> IFailed (S lb i adds (failDepth - 1)) err
x -> x
plus :: Get r a -> Get r a -> Get r a
plus p1 p2 =
Get $ \(S lb i adds failDepth) k ->
let
filter f@(IFailed (S _ _ adds' failDepth') _)
| failDepth' == failDepth + 1 = unGet p2 (S (lb `BL.append` (BL.fromChunks $ reverse adds')) i (adds' ++ adds) failDepth) k
| otherwise = f
filter (IPartial cont) = IPartial (filter . cont)
filter v@(IFinished _ _) = v
in
filter $ unGet p1 (S lb i [] (failDepth + 1)) (cutContinuation k)
zero :: Get r a
zero = fail ""
instance MonadPlus (Get r) where
mzero = zero
mplus = plus
instance Applicative (Get r) where
pure = return
(<*>) = ap
instance Alternative (Get r) where
empty = zero
(<|>) = plus
instance Class.BinaryParser (Get r) where
skip = skip
bytesRead = bytesRead
remaining = remaining
isEmpty = isEmpty
spanOf = spanOf
getWord8 = getWord8
getByteString = getByteString
getWord16be = getWord16be
getWord32be = getWord32be
getWord64be = getWord64be
getWord16le = getWord16le
getWord32le = getWord32le
getWord64le = getWord64le
getWordhost = getWordhost
getWord16host = getWord16host
getWord32host = getWord32host
getWord64host = getWord64host
skip :: Int -> Get r ()
skip n = readN (fromIntegral n) (const ())
bytesRead :: Get r Int
bytesRead = do
S _ b _ _ <- get
return b
remaining :: Get r Int
remaining = do
S s _ _ _<- get
return (fromIntegral (BL.length s))
isEmpty :: Get r Bool
isEmpty = do
S s _ _ _ <- get
return $ BL.null s
getByteString :: Int -> Get r B.ByteString
getByteString n = readN n id
{-# INLINE getByteString #-}
suspend :: Get r ()
suspend = Get $ \(S lb i adds failDepth) k ->
IPartial (\s -> k () (S (BL.append lb $ strictToLazy s) i (s : adds) failDepth))
getBytes :: Int -> Get r B.ByteString
getBytes n = Get $ \(S s offset adds failDepth) -> \cont ->
if fromIntegral n <= BL.length s
then let (consume, rest) = BL.splitAt (fromIntegral n) s
in cont (lazyToStrict consume) $ S rest (offset + fromIntegral n) adds failDepth
else IPartial (\s' -> unGet (getBytes n) (S (BL.append s $ strictToLazy s') offset (s' : adds) failDepth) cont)
{-# INLINE getBytes #-}
readN :: Int -> (B.ByteString -> a) -> Get r a
readN n f = fmap f $ getBytes n
{-# INLINE readN #-}
getPtr :: Storable a => Int -> Get r a
getPtr n = do
(fp, o, _) <- readN n B.toForeignPtr
return . B.inlinePerformIO $ withForeignPtr fp $ \p -> peek (castPtr $ p `plusPtr` o)
{-# INLINE getPtr #-}
GETWORDS(Get r, getBytes)
GETHOSTWORDS(Get r)
spanOf :: (Word8 -> Bool) -> Get r B.ByteString
spanOf p =
Get $ \(S lb i adds failDepth) k ->
let
(left, rest) = BL.span p lb
in
if BL.null rest
then IPartial (\s -> unGet (spanOf p) (S (strictToLazy s) (i + (fromIntegral $ BL.length lb)) (s : adds) failDepth) (\a -> k $ B.append (lazyToStrict left) a))
else k (lazyToStrict left) (S rest (i + (fromIntegral $ BL.length left)) adds failDepth)
shiftl_w16 :: Word16 -> Int -> Word16
shiftl_w32 :: Word32 -> Int -> Word32
shiftl_w64 :: Word64 -> Int -> Word64
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
shiftl_w16 (W16# w) (I# i) = W16# (w `uncheckedShiftL#` i)
shiftl_w32 (W32# w) (I# i) = W32# (w `uncheckedShiftL#` i)
#if WORD_SIZE_IN_BITS < 64
shiftl_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftL64#` i)
#else
shiftl_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftL#` i)
#endif
#else
shiftl_w16 = shiftL
shiftl_w32 = shiftL
shiftl_w64 = shiftL
#endif