{-# LANGUAGE CPP #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Deserializer
(
Deserializer(..)
, BinaryDeserializer(..)
, CerealDeserializer(..)
, word16H
, word32H
, word64H
, word
, wordL
, wordB
, wordH
, int8
, int16
, int16L
, int16B
, int16H
, int32
, int32L
, int32B
, int32H
, int64
, int64L
, int64B
, int64H
, int
, intL
, intB
, intH
, module Text.Parser.Combinators
, label
, module Text.Parser.LookAhead
, LittleEndianDeserializer(..)
, BigEndianDeserializer(..)
, deserializeIn
, deserializeH
, Deserialized(..)
, isDeserialized
, isMalformed
, maybeDeserialized
, defaultDeserializer
, Deserializable(..)
, getIn
, getL
, getB
, getH
, deserializeBytes
, deserializeBytesAs
, deserializeByteString
, deserializeByteStringAs
, deserializeLazyByteString
, deserializeLazyByteStringAs
, fromBytes
, fromBytesAs
, fromByteString
, fromByteStringAs
, fromLazyByteString
, fromLazyByteStringAs
, RestDeserializable(..)
) where
import Prelude hiding (take)
import GHC.Generics (Generic)
import Data.Typeable (Typeable)
import Data.Data (Data)
import Data.Proxy (Proxy(..))
import Data.Endian (Endian(..), swapEndian)
import Data.Word
import Data.Int
import Data.Bits ((.|.), shiftL)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BS
import qualified Data.ByteString.Short as SBS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Builder as BB
import qualified Data.Binary.Get as B
import qualified Data.Binary.Get.Internal as B
import qualified Data.Serialize.Get as S
import Data.List.Split (splitOn)
import Text.Parser.Combinators
import Text.Parser.LookAhead
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative (Applicative(..), (<$>), (<$), (<*>), (*>))
#endif
import Control.Applicative (Alternative, (<|>))
import Control.Monad (unless)
class (Monad μ, Parsing μ) ⇒ Deserializer μ where
{-# MINIMAL ensure, take, chunk, isolate #-}
endian ∷ Proxy μ → Endian
#ifdef WORDS_BIGENDIAN
endian _ = BigEndian
#else
endian _ = LittleEndian
#endif
word8 ∷ μ Word8
word8 = BS.unsafeHead <$> take 1 <?> "word8"
{-# INLINE word8 #-}
word16 ∷ μ Word16
word16 = getIn (endian (Proxy ∷ Proxy μ))
{-# INLINE word16 #-}
word32 ∷ μ Word32
word32 = getIn (endian (Proxy ∷ Proxy μ))
{-# INLINE word32 #-}
word64 ∷ μ Word64
word64 = getIn (endian (Proxy ∷ Proxy μ))
{-# INLINE word64 #-}
word16L ∷ μ Word16
word16L = (<?> "word16")
$ do bs ← take 2
let l = BS.unsafeIndex bs 0
h = BS.unsafeIndex bs 1
return $ shiftL (fromIntegral h) 8 .|. fromIntegral l
word16B ∷ μ Word16
word16B = swapEndian <$> word16L
word32L ∷ μ Word32
word32L = (<?> "word32")
$ do bs ← take 4
let o₀ = BS.unsafeIndex bs 0
o₁ = BS.unsafeIndex bs 1
o₂ = BS.unsafeIndex bs 2
o₃ = BS.unsafeIndex bs 3
return $ shiftL (fromIntegral o₃) 24
.|. shiftL (fromIntegral o₂) 16
.|. shiftL (fromIntegral o₁) 8
.|. fromIntegral o₀
word32B ∷ μ Word32
word32B = swapEndian <$> word32L
word64L ∷ μ Word64
word64L = (<?> "word64")
$ do bs ← take 8
let o₀ = BS.unsafeIndex bs 0
o₁ = BS.unsafeIndex bs 1
o₂ = BS.unsafeIndex bs 2
o₃ = BS.unsafeIndex bs 3
o₄ = BS.unsafeIndex bs 4
o₅ = BS.unsafeIndex bs 5
o₆ = BS.unsafeIndex bs 6
o₇ = BS.unsafeIndex bs 7
return $ shiftL (fromIntegral o₇) 56
.|. shiftL (fromIntegral o₆) 48
.|. shiftL (fromIntegral o₅) 40
.|. shiftL (fromIntegral o₄) 32
.|. shiftL (fromIntegral o₃) 24
.|. shiftL (fromIntegral o₂) 16
.|. shiftL (fromIntegral o₁) 8
.|. fromIntegral o₀
word64B ∷ μ Word64
word64B = swapEndian <$> word64L
satisfy ∷ (Word8 → Bool) → μ Word8
satisfy p = do w ← word8
if p w then return w
else unexpected (show w)
{-# INLINE satisfy #-}
byte ∷ Word8 → μ Word8
byte w = (<?> "byte " ++ show w)
$ do w' ← word8
if w' == w then return w'
else unexpected (show w')
{-# INLINE byte #-}
notByte ∷ Word8 → μ Word8
notByte w = (<?> "not byte " ++ show w)
$ do w' ← word8
if w' == w then unexpected (show w')
else return w'
{-# INLINE notByte #-}
bytes ∷ BS.ByteString → μ BS.ByteString
bytes bs = (<?> "bytes " ++ show (BS.unpack bs))
$ do bs' ← take (BS.length bs)
if bs' == bs then return bs'
else unexpected (show $ BS.unpack bs')
{-# INLINE bytes #-}
skip ∷ Int → μ ()
skip n | n <= 0 = pure ()
| otherwise = word8 *> skip (n - 1)
ensure ∷ Int → μ BS.ByteString
ensure_ ∷ Int → μ ()
ensure_ = (() <$) . ensure
{-# INLINE ensure_ #-}
check ∷ Int → μ Bool
check n = True <$ ensure_ n <|> return False
{-# INLINE check #-}
take ∷ Int → μ BS.ByteString
chunk ∷ μ BS.ByteString
isolate ∷ Int → μ α → μ α
newtype BinaryDeserializer α =
BinaryDeserializer { binaryDeserializer ∷ B.Get α }
deriving (Typeable, Generic, Functor, Applicative,
Alternative, Monad)
instance Parsing BinaryDeserializer where
try p = p
{-# INLINE try #-}
p <?> l = BinaryDeserializer (B.label l (binaryDeserializer p))
{-# INLINE (<?>) #-}
skipMany p = ((True <$ p) <|> pure False) >>= \case
True → skipMany p
False → return ()
unexpected = BinaryDeserializer . fail
{-# INLINE unexpected #-}
eof = BinaryDeserializer
$ B.isEmpty >>= \case
True → return ()
False → fail "Parsing.eof"
{-# INLINABLE eof #-}
notFollowedBy p = BinaryDeserializer
$ (B.lookAheadE (Left <$> (binaryDeserializer p)) <|>
pure (Right ())) >>= \case
Left e → fail (show e)
Right _ → return ()
{-# INLINABLE notFollowedBy #-}
instance LookAheadParsing BinaryDeserializer where
lookAhead = BinaryDeserializer . B.lookAhead . binaryDeserializer
{-# INLINE lookAhead #-}
instance Deserializer BinaryDeserializer where
word8 = BinaryDeserializer B.getWord8
{-# INLINE word8 #-}
word16L = BinaryDeserializer B.getWord16le
{-# INLINE word16L #-}
word16B = BinaryDeserializer B.getWord16be
{-# INLINE word16B #-}
word32L = BinaryDeserializer B.getWord32le
{-# INLINE word32L #-}
word32B = BinaryDeserializer B.getWord32be
{-# INLINE word32B #-}
word64L = BinaryDeserializer B.getWord64le
{-# INLINE word64L #-}
word64B = BinaryDeserializer B.getWord64be
{-# INLINE word64B #-}
skip = BinaryDeserializer . B.skip
{-# INLINE skip #-}
ensure n = BinaryDeserializer (B.ensureN n *> B.get)
{-# INLINE ensure #-}
ensure_ = BinaryDeserializer . B.ensureN
{-# INLINE ensure_ #-}
take = BinaryDeserializer . B.getByteString
{-# INLINE take #-}
chunk = BinaryDeserializer
$ do bs ← B.get
if BS.null bs
then do
e ← B.isEmpty
if e
then return bs
else B.get
else
return bs
{-# INLINABLE chunk #-}
isolate n d = BinaryDeserializer $ B.isolate n (binaryDeserializer d)
{-# INLINE isolate #-}
newtype CerealDeserializer α =
CerealDeserializer { cerealDeserializer ∷ S.Get α }
deriving (Typeable, Generic, Functor, Applicative,
Alternative, Monad)
instance Parsing CerealDeserializer where
try p = p
{-# INLINE try #-}
p <?> l = CerealDeserializer (S.label l (cerealDeserializer p))
{-# INLINE (<?>) #-}
skipMany p = ((True <$ p) <|> pure False) >>= \case
True → skipMany p
False → return ()
unexpected = CerealDeserializer . fail
{-# INLINE unexpected #-}
eof = CerealDeserializer
$ ((False <$ S.lookAheadM (Nothing <$ S.getWord8)) <|>
pure True) >>= \case
True → return ()
False → fail "Parsing.eof"
{-# INLINABLE eof #-}
notFollowedBy p = CerealDeserializer
$ (S.lookAheadE (Left <$> (cerealDeserializer p)) <|>
pure (Right ())) >>= \case
Left e → fail (show e)
Right _ → return ()
{-# INLINABLE notFollowedBy #-}
instance LookAheadParsing CerealDeserializer where
lookAhead = CerealDeserializer . S.lookAhead . cerealDeserializer
{-# INLINE lookAhead #-}
instance Deserializer CerealDeserializer where
word8 = CerealDeserializer S.getWord8
{-# INLINE word8 #-}
word16L = CerealDeserializer S.getWord16le
{-# INLINE word16L #-}
word16B = CerealDeserializer S.getWord16be
{-# INLINE word16B #-}
word32L = CerealDeserializer S.getWord32le
{-# INLINE word32L #-}
word32B = CerealDeserializer S.getWord32be
{-# INLINE word32B #-}
word64L = CerealDeserializer S.getWord64le
{-# INLINE word64L #-}
word64B = CerealDeserializer S.getWord64be
{-# INLINE word64B #-}
skip = CerealDeserializer . S.skip
{-# INLINE skip #-}
ensure = CerealDeserializer . S.ensure
{-# INLINE ensure #-}
take = CerealDeserializer . S.getBytes
{-# INLINE take #-}
chunk = CerealDeserializer
$ (<|> pure BS.empty)
$ do bs ← S.ensure 1
S.uncheckedSkip (BS.length bs)
return bs
{-# INLINE chunk #-}
isolate n d = CerealDeserializer (S.isolate n (cerealDeserializer d))
{-# INLINE isolate #-}
word16H ∷ Deserializer μ ⇒ μ Word16
#ifdef WORDS_BIGENDIAN
word16H = word16B
#else
word16H = word16L
#endif
{-# INLINE word16H #-}
word32H ∷ Deserializer μ ⇒ μ Word32
#ifdef WORDS_BIGENDIAN
word32H = word32B
#else
word32H = word32L
#endif
{-# INLINE word32H #-}
word64H ∷ Deserializer μ ⇒ μ Word64
#ifdef WORDS_BIGENDIAN
word64H = word64B
#else
word64H = word64L
#endif
{-# INLINE word64H #-}
word ∷ Deserializer μ ⇒ μ Word
#if WORD_SIZE_IN_BITS == 32
word = fromIntegral <$> word32
#else
word = fromIntegral <$> word64
#endif
{-# INLINE word #-}
wordL ∷ Deserializer μ ⇒ μ Word
#if WORD_SIZE_IN_BITS == 32
wordL = fromIntegral <$> word32L
#else
wordL = fromIntegral <$> word64L
#endif
{-# INLINE wordL #-}
wordB ∷ Deserializer μ ⇒ μ Word
#if WORD_SIZE_IN_BITS == 32
wordB = fromIntegral <$> word32B
#else
wordB = fromIntegral <$> word64B
#endif
{-# INLINE wordB #-}
wordH ∷ Deserializer μ ⇒ μ Word
#ifdef WORDS_BIGENDIAN
wordH = wordB
#else
wordH = wordL
#endif
{-# INLINE wordH #-}
int8 ∷ Deserializer μ ⇒ μ Int8
int8 = fromIntegral <$> word8
{-# INLINE int8 #-}
int16 ∷ Deserializer μ ⇒ μ Int16
int16 = fromIntegral <$> word16
{-# INLINE int16 #-}
int16L ∷ Deserializer μ ⇒ μ Int16
int16L = fromIntegral <$> word16L
{-# INLINE int16L #-}
int16B ∷ Deserializer μ ⇒ μ Int16
int16B = fromIntegral <$> word16B
{-# INLINE int16B #-}
int16H ∷ Deserializer μ ⇒ μ Int16
int16H = fromIntegral <$> word16H
{-# INLINE int16H #-}
int32 ∷ Deserializer μ ⇒ μ Int32
int32 = fromIntegral <$> word32
{-# INLINE int32 #-}
int32L ∷ Deserializer μ ⇒ μ Int32
int32L = fromIntegral <$> word32L
{-# INLINE int32L #-}
int32B ∷ Deserializer μ ⇒ μ Int32
int32B = fromIntegral <$> word32B
{-# INLINE int32B #-}
int32H ∷ Deserializer μ ⇒ μ Int32
int32H = fromIntegral <$> word32H
{-# INLINE int32H #-}
int64 ∷ Deserializer μ ⇒ μ Int64
int64 = fromIntegral <$> word64
{-# INLINE int64 #-}
int64L ∷ Deserializer μ ⇒ μ Int64
int64L = fromIntegral <$> word64L
{-# INLINE int64L #-}
int64B ∷ Deserializer μ ⇒ μ Int64
int64B = fromIntegral <$> word64B
{-# INLINE int64B #-}
int64H ∷ Deserializer μ ⇒ μ Int64
int64H = fromIntegral <$> word64H
{-# INLINE int64H #-}
int ∷ Deserializer μ ⇒ μ Int
#if WORD_SIZE_IN_BITS == 32
int = fromIntegral <$> int32
#else
int = fromIntegral <$> int64
#endif
{-# INLINE int #-}
intL ∷ Deserializer μ ⇒ μ Int
#if WORD_SIZE_IN_BITS == 32
intL = fromIntegral <$> int32L
#else
intL = fromIntegral <$> int64L
#endif
{-# INLINE intL #-}
intB ∷ Deserializer μ ⇒ μ Int
#if WORD_SIZE_IN_BITS == 32
intB = fromIntegral <$> int32B
#else
intB = fromIntegral <$> int64B
#endif
{-# INLINE intB #-}
intH ∷ Deserializer μ ⇒ μ Int
#if WORD_SIZE_IN_BITS == 32
intH = fromIntegral <$> int32H
#else
intH = fromIntegral <$> int64H
#endif
{-# INLINE intH #-}
label ∷ Parsing μ ⇒ String → μ α → μ α
label = flip (<?>)
{-# INLINE label #-}
newtype LittleEndianDeserializer μ α =
LittleEndianDeserializer { deserializeL ∷ μ α }
deriving (Typeable, Data, Generic, Functor, Applicative,
Alternative, Monad, Parsing, LookAheadParsing)
instance Deserializer μ ⇒ Deserializer (LittleEndianDeserializer μ) where
endian _ = LittleEndian
{-# INLINE endian #-}
word8 = LittleEndianDeserializer word8
{-# INLINE word8 #-}
word16 = LittleEndianDeserializer word16L
{-# INLINE word16 #-}
word32 = LittleEndianDeserializer word32L
{-# INLINE word32 #-}
word64 = LittleEndianDeserializer word64L
{-# INLINE word64 #-}
word16L = LittleEndianDeserializer word16L
{-# INLINE word16L #-}
word16B = LittleEndianDeserializer word16B
{-# INLINE word16B #-}
word32L = LittleEndianDeserializer word32L
{-# INLINE word32L #-}
word32B = LittleEndianDeserializer word32B
{-# INLINE word32B #-}
word64L = LittleEndianDeserializer word64L
{-# INLINE word64L #-}
word64B = LittleEndianDeserializer word64B
{-# INLINE word64B #-}
satisfy = LittleEndianDeserializer . satisfy
{-# INLINE satisfy #-}
byte = LittleEndianDeserializer . byte
{-# INLINE byte #-}
notByte = LittleEndianDeserializer . notByte
{-# INLINE notByte #-}
bytes = LittleEndianDeserializer . bytes
{-# INLINE bytes #-}
skip = LittleEndianDeserializer . skip
{-# INLINE skip #-}
ensure = LittleEndianDeserializer . ensure
{-# INLINE ensure #-}
ensure_ = LittleEndianDeserializer . ensure_
{-# INLINE ensure_ #-}
take = LittleEndianDeserializer . take
{-# INLINE take #-}
chunk = LittleEndianDeserializer chunk
{-# INLINE chunk #-}
isolate n = LittleEndianDeserializer . isolate n . deserializeL
{-# INLINE isolate #-}
newtype BigEndianDeserializer μ α =
BigEndianDeserializer { deserializeB ∷ μ α }
deriving (Typeable, Data, Generic, Functor, Applicative,
Alternative, Monad, Parsing, LookAheadParsing)
instance Deserializer μ ⇒ Deserializer (BigEndianDeserializer μ) where
endian _ = BigEndian
{-# INLINE endian #-}
word8 = BigEndianDeserializer word8
{-# INLINE word8 #-}
word16 = BigEndianDeserializer word16B
{-# INLINE word16 #-}
word32 = BigEndianDeserializer word32B
{-# INLINE word32 #-}
word64 = BigEndianDeserializer word64B
{-# INLINE word64 #-}
word16L = BigEndianDeserializer word16L
{-# INLINE word16L #-}
word16B = BigEndianDeserializer word16B
{-# INLINE word16B #-}
word32L = BigEndianDeserializer word32L
{-# INLINE word32L #-}
word32B = BigEndianDeserializer word32B
{-# INLINE word32B #-}
word64L = BigEndianDeserializer word64L
{-# INLINE word64L #-}
word64B = BigEndianDeserializer word64B
{-# INLINE word64B #-}
satisfy = BigEndianDeserializer . satisfy
{-# INLINE satisfy #-}
byte = BigEndianDeserializer . byte
{-# INLINE byte #-}
notByte = BigEndianDeserializer . notByte
{-# INLINE notByte #-}
bytes = BigEndianDeserializer . bytes
{-# INLINE bytes #-}
skip = BigEndianDeserializer . skip
{-# INLINE skip #-}
ensure = BigEndianDeserializer . ensure
{-# INLINE ensure #-}
ensure_ = BigEndianDeserializer . ensure_
{-# INLINE ensure_ #-}
take = BigEndianDeserializer . take
{-# INLINE take #-}
chunk = BigEndianDeserializer chunk
{-# INLINE chunk #-}
isolate n = BigEndianDeserializer . isolate n . deserializeB
{-# INLINE isolate #-}
deserializeIn ∷ Deserializer μ
⇒ Endian → (∀ μ' . (Deserializer μ') ⇒ μ' α) → μ α
deserializeIn LittleEndian = deserializeL
deserializeIn BigEndian = deserializeB
{-# INLINE deserializeIn #-}
deserializeH ∷ Deserializer μ ⇒ (∀ μ' . (Deserializer μ') ⇒ μ' α) → μ α
#ifdef WORDS_BIGENDIAN
deserializeH = deserializeB
#else
deserializeH = deserializeL
#endif
data Deserialized α = Deserialized α
| Malformed [String] String
isDeserialized ∷ Deserialized α → Bool
isDeserialized (Deserialized _) = True
isDeserialized (Malformed _ _) = False
isMalformed ∷ Deserialized α → Bool
isMalformed (Deserialized _) = False
isMalformed (Malformed _ _) = True
maybeDeserialized ∷ Deserialized α → Maybe α
maybeDeserialized (Deserialized a) = Just a
maybeDeserialized (Malformed _ _) = Nothing
defaultDeserializer ∷ (∀ μ . Deserializer μ ⇒ μ α) → LBS.ByteString
→ Deserialized α
defaultDeserializer m i = case B.runGetOrFail (binaryDeserializer m) i of
Left (_, _, e) → case splitOn "\n" e of
[] → Malformed [] e
[_] → Malformed [] e
es → Malformed (init es) (last es)
Right (_, _, a) →
Deserialized a
class Deserializable α where
get ∷ Deserializer μ ⇒ μ α
instance Deserializable Bool where
get = do w ← word8
case w of
0 → return False
1 → return True
_ → unexpected (show w)
instance Deserializable Word8 where
get = word8
{-# INLINE get #-}
instance Deserializable Word16 where
get = word16
{-# INLINE get #-}
instance Deserializable Word32 where
get = word32
{-# INLINE get #-}
instance Deserializable Word64 where
get = word64
{-# INLINE get #-}
instance Deserializable Word where
get = word
{-# INLINE get #-}
instance Deserializable Int8 where
get = int8
{-# INLINE get #-}
instance Deserializable Int16 where
get = int16
{-# INLINE get #-}
instance Deserializable Int32 where
get = int32
{-# INLINE get #-}
instance Deserializable Int64 where
get = int64
{-# INLINE get #-}
instance Deserializable Int where
get = int
{-# INLINE get #-}
instance (Deserializable α, Deserializable β) ⇒ Deserializable (α, β) where
get = (,) <$> get <*> get
{-# INLINE get #-}
instance Deserializable α ⇒ Deserializable (Maybe α) where
get = do w ← word8
case w of
0 → return Nothing
1 → Just <$> get
_ → unexpected (show w)
instance (Deserializable α, Deserializable β)
⇒ Deserializable (Either α β) where
get = do w ← word8
case w of
0 → Left <$> get
1 → Right <$> get
_ → unexpected (show w)
instance Deserializable BS.ByteString where
get = do l ← int <?> "length"
unless (l >= 0) $ unexpected "negative length"
take l <?> "contents"
{-# INLINABLE get #-}
instance Deserializable SBS.ShortByteString where
get = SBS.toShort <$> get
{-# INLINE get #-}
getIn ∷ (Deserializer μ, Deserializable α) ⇒ Endian → μ α
getIn e = deserializeIn e get
{-# INLINE getIn #-}
getL ∷ (Deserializer μ, Deserializable α) ⇒ μ α
getL = deserializeL get
{-# INLINE getL #-}
getB ∷ (Deserializer μ, Deserializable α) ⇒ μ α
getB = deserializeB get
{-# INLINE getB #-}
getH ∷ (Deserializer μ, Deserializable α) ⇒ μ α
#ifdef WORDS_BIGENDIAN
getH = getB
#else
getH = getL
#endif
{-# INLINE getH #-}
deserializeBytes ∷ Deserializable α ⇒ [Word8] → Deserialized α
deserializeBytes = defaultDeserializer get . LBS.pack
{-# INLINE deserializeBytes #-}
deserializeBytesAs ∷ Deserializable α ⇒ p α → [Word8] → Deserialized α
deserializeBytesAs _ = deserializeBytes
{-# INLINE deserializeBytesAs #-}
deserializeByteString ∷ Deserializable α
⇒ BS.ByteString → Deserialized α
deserializeByteString = defaultDeserializer get . LBS.fromStrict
{-# INLINE deserializeByteString #-}
deserializeByteStringAs ∷ Deserializable α
⇒ p α → BS.ByteString → Deserialized α
deserializeByteStringAs _ = deserializeByteString
{-# INLINE deserializeByteStringAs #-}
deserializeLazyByteString ∷ Deserializable α
⇒ LBS.ByteString → Deserialized α
deserializeLazyByteString = defaultDeserializer get
{-# INLINE deserializeLazyByteString #-}
deserializeLazyByteStringAs ∷ Deserializable α
⇒ p α → LBS.ByteString → Deserialized α
deserializeLazyByteStringAs _ = deserializeLazyByteString
{-# INLINE deserializeLazyByteStringAs #-}
fromBytes ∷ Deserializable α ⇒ [Word8] → Maybe α
fromBytes = maybeDeserialized . deserializeBytes
{-# INLINE fromBytes #-}
fromBytesAs ∷ Deserializable α ⇒ p α → [Word8] → Maybe α
fromBytesAs _ = fromBytes
{-# INLINE fromBytesAs #-}
fromByteString ∷ Deserializable α ⇒ BS.ByteString → Maybe α
fromByteString = maybeDeserialized . deserializeByteString
{-# INLINE fromByteString #-}
fromByteStringAs ∷ Deserializable α ⇒ p α → BS.ByteString → Maybe α
fromByteStringAs _ = fromByteString
{-# INLINE fromByteStringAs #-}
fromLazyByteString ∷ Deserializable α ⇒ LBS.ByteString → Maybe α
fromLazyByteString = maybeDeserialized . deserializeLazyByteString
{-# INLINE fromLazyByteString #-}
fromLazyByteStringAs ∷ Deserializable α ⇒ p α → LBS.ByteString → Maybe α
fromLazyByteStringAs _ = fromLazyByteString
{-# INLINE fromLazyByteStringAs #-}
class RestDeserializable α where
getRest ∷ Deserializer μ ⇒ μ α
instance RestDeserializable BS.ByteString where
getRest = go []
where go acc = do bs ← chunk
if BS.null bs then return $ BS.concat $ reverse acc
else go (bs : acc)
instance RestDeserializable SBS.ShortByteString where
getRest = SBS.toShort <$> getRest
{-# INLINE getRest #-}
instance RestDeserializable LBS.ByteString where
getRest = go []
where go acc = do bs ← chunk
if BS.null bs then return $ LBS.fromChunks $ reverse acc
else go (bs : acc)
instance RestDeserializable BB.Builder where
getRest = BB.lazyByteString <$> getRest
{-# INLINE getRest #-}
instance (RestDeserializable α, RestDeserializable β)
⇒ RestDeserializable (Either α β) where
getRest = word8 >>= \case
0 → Left <$> getRest
1 → Right <$> getRest
w → unexpected (show w)
{-# INLINABLE getRest #-}
instance (Deserializable α, RestDeserializable β)
⇒ RestDeserializable (α, β) where
getRest = (,) <$> get <*> getRest
{-# INLINE getRest #-}
instance Deserializable α ⇒ RestDeserializable [α] where
getRest = ([] <$ eof) <|> ((:) <$> get <*> getRest)