{-# LANGUAGE CPP, RankNTypes, MagicHash, BangPatterns #-}
#if __GLASGOW_HASKELL__ >= 701
{-# LANGUAGE Trustworthy #-}
#endif
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
#include "MachDeps.h"
#endif
module Data.Binary.Get (
Get
, runGet
, runGetOrFail
, ByteOffset
, Decoder(..)
, runGetIncremental
, pushChunk
, pushChunks
, pushEndOfInput
, skip
, isEmpty
, bytesRead
, lookAhead
, lookAheadM
, lookAheadE
, getByteString
, getLazyByteString
, getLazyByteStringNul
, getRemainingLazyByteString
, getWord8
, getWord16be
, getWord32be
, getWord64be
, getWord16le
, getWord32le
, getWord64le
, getWordhost
, getWord16host
, getWord32host
, getWord64host
, runGetState
, remaining
, getBytes
) where
import Foreign
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as L
import Control.Applicative
import Data.Binary.Get.Internal hiding ( Decoder(..), runGetIncremental )
import qualified Data.Binary.Get.Internal as I
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
import GHC.Base
import GHC.Word
#endif
data Decoder a = Fail !B.ByteString {-# UNPACK #-} !ByteOffset String
| Partial (Maybe B.ByteString -> Decoder a)
| Done !B.ByteString {-# UNPACK #-} !ByteOffset a
runGetIncremental :: Get a -> Decoder a
runGetIncremental = calculateOffset . I.runGetIncremental
calculateOffset :: I.Decoder a -> Decoder a
calculateOffset r0 = go r0 0
where
go r !acc = case r of
I.Done inp a -> Done inp (acc - fromIntegral (B.length inp)) a
I.Fail inp s -> Fail inp (acc - fromIntegral (B.length inp)) s
I.Partial k ->
Partial $ \ms ->
case ms of
Nothing -> go (k Nothing) acc
Just i -> go (k ms) (acc + fromIntegral (B.length i))
I.BytesRead unused k ->
go (k $! (acc - unused)) acc
{-# DEPRECATED runGetState "Use runGetIncremental instead. This function will be removed." #-}
runGetState :: Get a -> L.ByteString -> ByteOffset -> (a, L.ByteString, ByteOffset)
runGetState g lbs0 pos' = go (runGetIncremental g) lbs0
where
go (Done s pos a) lbs = (a, L.chunk s lbs, pos+pos')
go (Partial k) lbs = go (k (takeHeadChunk lbs)) (dropHeadChunk lbs)
go (Fail _ pos msg) _ =
error ("Data.Binary.Get.runGetState at position " ++ show pos ++ ": " ++ msg)
takeHeadChunk :: L.ByteString -> Maybe B.ByteString
takeHeadChunk lbs =
case lbs of
(L.Chunk bs _) -> Just bs
_ -> Nothing
dropHeadChunk :: L.ByteString -> L.ByteString
dropHeadChunk lbs =
case lbs of
(L.Chunk _ lbs') -> lbs'
_ -> L.Empty
runGetOrFail :: Get a -> L.ByteString
-> Either (L.ByteString, ByteOffset, String) (L.ByteString, ByteOffset, a)
runGetOrFail g lbs0 = feedAll (runGetIncremental g) lbs0
where
feedAll (Done bs pos x) lbs = Right (L.chunk bs lbs, pos, x)
feedAll (Partial k) lbs = feedAll (k (takeHeadChunk lbs)) (dropHeadChunk lbs)
feedAll (Fail x pos msg) xs = Left (L.chunk x xs, pos, msg)
type ByteOffset = Int64
runGet :: Get a -> L.ByteString -> a
runGet g lbs0 = feedAll (runGetIncremental g) lbs0
where
feedAll (Done _ _ x) _ = x
feedAll (Partial k) lbs = feedAll (k (takeHeadChunk lbs)) (dropHeadChunk lbs)
feedAll (Fail _ pos msg) _ =
error ("Data.Binary.Get.runGet at position " ++ show pos ++ ": " ++ msg)
pushChunk :: Decoder a -> B.ByteString -> Decoder a
pushChunk r inp =
case r of
Done inp0 p a -> Done (inp0 `B.append` inp) p a
Partial k -> k (Just inp)
Fail inp0 p s -> Fail (inp0 `B.append` inp) p s
pushChunks :: Decoder a -> L.ByteString -> Decoder a
pushChunks r0 = go r0 . L.toChunks
where
go r [] = r
go (Done inp pos a) xs = Done (B.concat (inp:xs)) pos a
go (Fail inp pos s) xs = Fail (B.concat (inp:xs)) pos s
go (Partial k) (x:xs) = go (k (Just x)) xs
pushEndOfInput :: Decoder a -> Decoder a
pushEndOfInput r =
case r of
Done _ _ _ -> r
Partial k -> k Nothing
Fail _ _ _ -> r
getLazyByteString :: Int64 -> Get L.ByteString
getLazyByteString n0 = L.fromChunks <$> go n0
where
consume n str
| fromIntegral (B.length str) >= n = Right (B.splitAt (fromIntegral n) str)
| otherwise = Left (fromIntegral (B.length str))
go n = do
str <- get
case consume n str of
Left used -> do
put B.empty
demandInput
fmap (str:) (go (n - used))
Right (want,rest) -> do
put rest
return [want]
getLazyByteStringNul :: Get L.ByteString
getLazyByteStringNul = L.fromChunks <$> go
where
findNull str =
case B.break (==0) str of
(want,rest) | B.null rest -> Nothing
| otherwise -> Just (want, B.drop 1 rest)
go = do
str <- get
case findNull str of
Nothing -> do
put B.empty
demandInput
fmap (str:) go
Just (want,rest) -> do
put rest
return [want]
getRemainingLazyByteString :: Get L.ByteString
getRemainingLazyByteString = L.fromChunks <$> go
where
go = do
str <- get
put B.empty
done <- isEmpty
if done
then return [str]
else fmap (str:) go
getPtr :: Storable a => Int -> Get a
getPtr n = readNWith n peek
{-# INLINE getPtr #-}
getWord8 :: Get Word8
getWord8 = readN 1 B.unsafeHead
{-# INLINE getWord8 #-}
{-# RULES
"getWord8/readN" getWord8 = readN 1 B.unsafeHead
"getWord16be/readN" getWord16be = readN 2 word16be
"getWord16le/readN" getWord16le = readN 2 word16le
"getWord32be/readN" getWord32be = readN 4 word32be
"getWord32le/readN" getWord32le = readN 4 word32le
"getWord64be/readN" getWord64be = readN 8 word64be
"getWord64le/readN" getWord64le = readN 8 word64le
#-}
getWord16be :: Get Word16
getWord16be = readN 2 word16be
word16be :: B.ByteString -> Word16
word16be = \s ->
(fromIntegral (s `B.unsafeIndex` 0) `shiftl_w16` 8) .|.
(fromIntegral (s `B.unsafeIndex` 1))
{-# INLINE getWord16be #-}
{-# INLINE word16be #-}
getWord16le :: Get Word16
getWord16le = readN 2 word16le
word16le :: B.ByteString -> Word16
word16le = \s ->
(fromIntegral (s `B.unsafeIndex` 1) `shiftl_w16` 8) .|.
(fromIntegral (s `B.unsafeIndex` 0) )
{-# INLINE getWord16le #-}
{-# INLINE word16le #-}
getWord32be :: Get Word32
getWord32be = readN 4 word32be
word32be :: B.ByteString -> Word32
word32be = \s ->
(fromIntegral (s `B.unsafeIndex` 0) `shiftl_w32` 24) .|.
(fromIntegral (s `B.unsafeIndex` 1) `shiftl_w32` 16) .|.
(fromIntegral (s `B.unsafeIndex` 2) `shiftl_w32` 8) .|.
(fromIntegral (s `B.unsafeIndex` 3) )
{-# INLINE getWord32be #-}
{-# INLINE word32be #-}
getWord32le :: Get Word32
getWord32le = readN 4 word32le
word32le :: B.ByteString -> Word32
word32le = \s ->
(fromIntegral (s `B.unsafeIndex` 3) `shiftl_w32` 24) .|.
(fromIntegral (s `B.unsafeIndex` 2) `shiftl_w32` 16) .|.
(fromIntegral (s `B.unsafeIndex` 1) `shiftl_w32` 8) .|.
(fromIntegral (s `B.unsafeIndex` 0) )
{-# INLINE getWord32le #-}
{-# INLINE word32le #-}
getWord64be :: Get Word64
getWord64be = readN 8 word64be
word64be :: B.ByteString -> Word64
word64be = \s ->
(fromIntegral (s `B.unsafeIndex` 0) `shiftl_w64` 56) .|.
(fromIntegral (s `B.unsafeIndex` 1) `shiftl_w64` 48) .|.
(fromIntegral (s `B.unsafeIndex` 2) `shiftl_w64` 40) .|.
(fromIntegral (s `B.unsafeIndex` 3) `shiftl_w64` 32) .|.
(fromIntegral (s `B.unsafeIndex` 4) `shiftl_w64` 24) .|.
(fromIntegral (s `B.unsafeIndex` 5) `shiftl_w64` 16) .|.
(fromIntegral (s `B.unsafeIndex` 6) `shiftl_w64` 8) .|.
(fromIntegral (s `B.unsafeIndex` 7) )
{-# INLINE getWord64be #-}
{-# INLINE word64be #-}
getWord64le :: Get Word64
getWord64le = readN 8 word64le
word64le :: B.ByteString -> Word64
word64le = \s ->
(fromIntegral (s `B.unsafeIndex` 7) `shiftl_w64` 56) .|.
(fromIntegral (s `B.unsafeIndex` 6) `shiftl_w64` 48) .|.
(fromIntegral (s `B.unsafeIndex` 5) `shiftl_w64` 40) .|.
(fromIntegral (s `B.unsafeIndex` 4) `shiftl_w64` 32) .|.
(fromIntegral (s `B.unsafeIndex` 3) `shiftl_w64` 24) .|.
(fromIntegral (s `B.unsafeIndex` 2) `shiftl_w64` 16) .|.
(fromIntegral (s `B.unsafeIndex` 1) `shiftl_w64` 8) .|.
(fromIntegral (s `B.unsafeIndex` 0) )
{-# INLINE getWord64le #-}
{-# INLINE word64le #-}
getWordhost :: Get Word
getWordhost = getPtr (sizeOf (undefined :: Word))
{-# INLINE getWordhost #-}
getWord16host :: Get Word16
getWord16host = getPtr (sizeOf (undefined :: Word16))
{-# INLINE getWord16host #-}
getWord32host :: Get Word32
getWord32host = getPtr (sizeOf (undefined :: Word32))
{-# INLINE getWord32host #-}
getWord64host :: Get Word64
getWord64host = getPtr (sizeOf (undefined :: Word64))
{-# INLINE getWord64host #-}
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)
#if __GLASGOW_HASKELL__ <= 606
foreign import ccall unsafe "stg_uncheckedShiftL64"
uncheckedShiftL64# :: Word64# -> Int# -> Word64#
#endif
#else
shiftl_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftL#` i)
#endif
#else
shiftl_w16 = shiftL
shiftl_w32 = shiftL
shiftl_w64 = shiftL
#endif