{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE Trustworthy #-}

#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
#include "MachDeps.h"
#endif

module Data.Tickle.Get(
  -- * Get data type
  Get
  -- ** Primitive parsers
, lazyByteString
, lazyByteStringNul
, remainingLazyByteString
, ptr
, word8
, word16be
, word16le
, word32be
, word32le
, word64be
, word64le
, wordhost
, word16host
, word32host 
, word64host
, failGet
, constant
, bytesRead
, demandInput
, skip
, isNotEmpty
, isEmpty
, getByteString
, modify
, readN
, ensureN
  -- ** Higher-level parsers
  -- *** IEEE754 parsers
, toFloat
, toFloat16
, float16be
, float16le
, float32be
, float32le
, float64be
, float64le
  -- *** Integer parsers
, int8
, int16be
, int16le
, int32be
, int32le
, int64be
, int64le
, IntegerError
, integerError
, _IntegerTagUnexpectedEof
, _Integer0TagUnexpectedEof
, _Integer1TagUnexpectedEof
, _IntegerListError
, integer
  -- *** List parsers
, ListError
, listError
, listErrorIso
, list
, many
  -- ** Higher-level combinators
, runAndKeepTrack
, pushBack
, pushFront
, xrunGetIncremental
, noMeansNo
, prompt
, isolate
, lookAhead
, lookAheadM
, lookAheadE
, readNWith
, calculateOffset
, pushChunk
, pushChunks
, pushEndOfInput
  -- ** Error label
, (!+)
, addLabel
, (!-)
, setLabel
, (!!-)
, modifyLabel
  -- * Decoder
, Decoder
, decoder
, _Fail
, _Partial
, _Done
  -- ** Run Get parser
, runGet
, (.>>)
, (<<.)
, runGetIncremental
  -- * XDecoder data type
, XDecoder
  -- ** Reduction
, xdecoder
  -- ** Prisms
, _XFail
, _XPartial
, _XDone
, _XBytesRead
  -- * CompletedXDecoder data type
, CompletedXDecoder
  -- ** Reduction
, completedXDecoder
  -- ** Prism
, _CompletedFail
, _CompletedDone
  -- ** Isomorphism
, completedIso
  -- ** Lens
, completedByteString
, completedValue
  -- ** Prism
, uncomplete
  -- ** Traversal
, uncompletedByteString
, uncompletedValue
) where

import Control.Applicative(Applicative((<*>), pure), (<$>))
import Control.Category(Category((.), id))
import Control.Lens.Iso(Iso', iso)
import Control.Lens.Lens(lens)
import Control.Lens.Prism(Prism', prism')
import Control.Lens.Review((#))
import Control.Lens.Type(Iso, Lens', Traversal')
import Control.Monad(Monad((>>=), (>>), return), ap)
import Data.Bifoldable(Bifoldable(bifoldMap))
import Data.Bifunctor(Bifunctor(bimap))
import Data.Bitraversable(Bitraversable(bitraverse))
import Data.Bits((.|.), (.&.), shiftL, shiftR)
import Data.Bool(Bool(False, True), (&&), not, otherwise)
import qualified Data.ByteString as B(ByteString, concat, append, length, splitAt, empty, null, break, drop)
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Lazy as L(ByteString, toChunks, fromChunks)
import qualified Data.ByteString.Lazy.Internal as LI(ByteString(Chunk, Empty))
import qualified Data.ByteString.Unsafe as BU(unsafeDrop, unsafeTake, unsafeHead, unsafeIndex, unsafeUseAsCString)
import Data.Either(Either(Left, Right), either)
import Data.Eq(Eq((==)))
import Data.Foldable(Foldable(foldMap))
import Data.Function(const)
import Data.Functor(Functor(fmap))
import Data.Functor.Apply(Apply((<.>)))
import Data.Functor.Alt(Alt((<!>)))
import qualified Data.Functor.Alt as Al(Alt(some, many))
import Data.Functor.Bind(Bind((>>-)))
import Data.Int(Int, Int8, Int16, Int32, Int64)
import Data.List(reverse, foldr)
import Data.Maybe(Maybe(Nothing, Just), maybe, isJust)
import Data.Monoid(Monoid(mempty))
import Data.Ord(Ord((>), (>=), (<), (>=)))
import Data.Semigroup(Semigroup((<>)))
import Data.Tickle.IsolateError(IsolateError, _NegativeSize, _IsolateXFail, _UnexpectedConsumed)
import Data.Tickle.RunGetResult(RunGetResult, _RunGet, _RunGetFail)
import Data.Traversable(Traversable(traverse))
import Data.Tuple(uncurry)
import Foreign(Ptr, castPtr, Storable(peek), sizeOf, alloca, poke)
import System.IO.Unsafe(unsafePerformIO)
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
import GHC.Word(Word, Word8, Word16(W16#), Word32(W32#), Word64(W64#))
import GHC.Base(uncheckedShiftL#, Int(I#))
#endif
import Prelude(Num((-), (+)), Float, Double, Integer, ($!), Show, fromIntegral, undefined, seq)
import System.IO(IO)

-- $setup
-- >>> import Control.Lens.Fold((^?))
-- >>> import Control.Lens.Prism(_Right, _Left)
-- >>> import qualified Data.ByteString.Lazy.Char8 as BLC(pack)
-- >>> import qualified Data.ByteString.Char8 as BC(ByteString, pack)
-- >>> import Data.String(String)
-- >>> import Data.List((++))
-- >>> import Data.Maybe(fromMaybe, isNothing)
-- >>> import Data.Validation(_Success, _Failure)
-- >>> import Prelude(Num((*)), subtract, even, mod)

newtype Get e a = 
  Get (forall r.
    B.ByteString ->
    (B.ByteString -> a -> XDecoder e r) ->
    XDecoder e r)

bimapG ::
  (e -> f)
  -> (a -> b)
  -> Get e a
  -> Get f b
bimapG f g (Get z) =
  Get (\b q -> 
    let r = z b XDone
        go (XDone i a) = q i (g a)
        go (XPartial k) = XPartial (go . k)
        go (XFail i s) = XFail i (f s) 
        go (XBytesRead u k) = XBytesRead u (go . k)
    in go r)
{-# INLINE bimapG #-}

-- | Map on the error and result of a @Get@ decoder.
--
-- >>> runGet (bimap (const True) (\x -> x + x) word8) (BLC.pack "")
-- RunGetFail 0 True
--
-- >>> runGet (bimap (const True) (\x -> x + x) word8) (BLC.pack "abc")
-- RunGet 194
instance Bifunctor Get where
  bimap =
    bimapG

fmapG ::
  (a -> b)
  -> Get e a
  -> Get e b
fmapG =
  bimapG id
{-# INLINE fmapG #-}

-- | Map on the result of a @Get@ decoder.
--
-- >>> runGet (fmap (\x -> x + x) word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (fmap (\x -> x + x) word8) (BLC.pack "abc")
-- RunGet 194
instance Functor (Get e) where
  fmap =
    fmapG

-- | Apply a function on the @Get@ decoder result.
--
-- >>> runGet (fmap (+) word8 <.> word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (fmap (+) word8 <.> word8) (BLC.pack "a")
-- RunGetFail 1 ()
--
-- >>> runGet (fmap (+) word8 <.> word8) (BLC.pack "ab")
-- RunGet 195
--
-- >>> runGet (fmap (+) word8 <.> word8) (BLC.pack "abc")
-- RunGet 195
instance Apply (Get e) where
  (<.>) =
    ap

apG ::
  Get e (a -> b)
  -> Get e a
  -> Get e b
apG d e =
  do b <- d
     a <- e
     return (b a)
{-# INLINE [0] apG #-}

-- | Apply a function on the @Get@ decoder result.
--
-- >>> runGet (fmap (+) word8 <*> word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (fmap (+) word8 <*> word8) (BLC.pack "a")
-- RunGetFail 1 ()
--
-- >>> runGet (fmap (+) word8 <*> word8) (BLC.pack "ab")
-- RunGet 195
--
-- >>> runGet (fmap (+) word8 <*> word8) (BLC.pack "abc")
-- RunGet 195
--
-- >>> runGet (pure 7 :: Get () Int) (BLC.pack "abc")
-- RunGet 7
--
-- prop> runGet (pure x :: Get () Int) (BLC.pack "abc") == _RunGet # x
instance Applicative (Get e) where
  pure =
    return
  {-# INLINE pure #-}
  (<*>) =
    apG
  {-# INLINE (<*>) #-}

-- | Sequence an action through the @Get@ decoder.
--
-- >>> runGet (word8 >>- \c1 -> fmap (\c2 -> c1 + c2) word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (word8 >>- \c1 -> fmap (\c2 -> c1 + c2) word8) (BLC.pack "a")
-- RunGetFail 1 ()
--
-- >>> runGet (word8 >>- \c1 -> fmap (\c2 -> c1 + c2) word8) (BLC.pack "ab")
-- RunGet 195
--
-- >>> runGet (word8 >>- \c1 -> fmap (\c2 -> c1 + c2) word8) (BLC.pack "abc")
-- RunGet 195
instance Bind (Get e) where
  (>>-) =
    (>>=)

returnG ::
  a
  -> Get e a
returnG a =
    Get (\b q -> q b a)
{-# INLINE [0] returnG #-}

bindG ::
  Get e a
  -> (a -> Get e b)
  -> Get e b
Get k `bindG` f =
  Get (\b q -> k b (\c a -> 
    let Get l = f a 
    in l c q))
{-# INLINE bindG #-}

-- | Sequence an action through the @Get@ decoder.
--
-- >>> runGet (return 7 :: Get () Int) (BLC.pack "abc")
-- RunGet 7
--
-- prop> runGet (return x :: Get () Int) (BLC.pack "abc") == _RunGet # x
--
-- >>> runGet (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2)) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2)) (BLC.pack "a")
-- RunGetFail 1 ()
--
-- >>> runGet (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2)) (BLC.pack "ab")
-- RunGet 195
--
-- >>> runGet (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2)) (BLC.pack "abc")
-- RunGet 195
instance Monad (Get e) where
  return =
    returnG
  (>>=) =
    bindG

-- | Pick between two @Get@ decoders, finding the first to not fail.
--
-- >>> runGet ((+1) <$> word8 <!> subtract 1 <$> word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet ((+1) <$> word8 <!> subtract 1 <$> word8) (BLC.pack "abc")
-- RunGet 98
--
-- >>> runGet (word8 <!> failGet ()) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (word8 <!> failGet ()) (BLC.pack "abc")
-- RunGet 97
--
-- >>> runGet (Al.some word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (Al.some word8) (BLC.pack "a")
-- RunGet [97]
--
-- >>> runGet (Al.some word8) (BLC.pack "abc")
-- RunGet [97,98,99]
--
-- >>> runGet (Al.many word8) (BLC.pack "")
-- RunGet []
--
-- >>> runGet (Al.many word8) (BLC.pack "a")
-- RunGet [97]
--
-- >>> runGet (Al.many word8) (BLC.pack "abc")
-- RunGet [97,98,99]
instance Alt (Get e) where
  f <!> g = 
    do (d, bs) <- runAndKeepTrack f
       case d of
         CompletedDone b a -> Get (\_ q -> q b a)
         CompletedFail _ _ -> pushBack bs >> g
  some p =
    (:) <$> p <*> Al.many p
  many p =
    (p >>= \x ->
     fmap (x:) (Al.many p)) <!> pure []
  
-- | Pick between two @Get@ decoders, finding the first to not fail.
--
-- >>> runGet (((+1) <$> word8) <> (subtract 1 <$> word8)) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (((+1) <$> word8) <> (subtract 1 <$> word8)) (BLC.pack "abc")
-- RunGet 98
--
-- >>> runGet (word8 <> failGet ()) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (word8 <> failGet ()) (BLC.pack "abc")
-- RunGet 97
instance Semigroup (Get e a) where
  (<>) =
    (<!>)

-- | A @Get@ decoder that always fails with the given value.
--
-- prop> runGet (failGet x :: Get Int ()) (BLC.pack s) == _RunGetFail # (0, x)
--
-- >>> runGet (failGet "abc" :: Get String ()) (BLC.pack "def")
-- RunGetFail 0 "abc"
failGet ::
  e
  -> Get e a
failGet e =
  Get (\i _ -> XFail i e)

constant ::
  (forall r. XDecoder e r)
  -> Get e a
constant d =
  Get (\_ _ -> d)

-- | Run a @Get@ decoder, but keep a track of the input that ran it to completion.
--
-- >>> runGet (runAndKeepTrack word8 :: Get () (CompletedXDecoder () Word8, [BC.ByteString])) (BLC.pack "")
-- RunGet (CompletedFail "" (),[])
--
-- >>> runGet (runAndKeepTrack word8 :: Get () (CompletedXDecoder () Word8, [BC.ByteString])) (BLC.pack "abc")
-- RunGet (CompletedDone "bc" 97,["abc"])
runAndKeepTrack ::
  Get e a
  -> Get x (CompletedXDecoder e a, [B.ByteString])
runAndKeepTrack (Get k) =
  Get (\b q -> 
    let go !acc w = case w of
                      XDone c a -> q b (CompletedDone c a, reverse acc)
                      XPartial l -> XPartial (\c -> go (maybe acc (:acc) c) (l c))
                      XFail c e -> q b (CompletedFail c e, reverse acc)
                      XBytesRead i l -> XBytesRead i (go acc . l)
    in go [] (k b XDone))
{-# INLINE runAndKeepTrack #-}

-- |
--
-- >>> runGet (pushBack [] :: Get () ()) (BLC.pack "")
-- RunGet ()
--
-- >>> runGet (pushBack [] :: Get () ()) (BLC.pack "abc")
-- RunGet ()
--
-- >>> runGet (pushBack [BC.pack "def"] :: Get () ()) (BLC.pack "")
-- RunGet ()
--
-- >>> runGet (pushBack [BC.pack "def"] :: Get () ()) (BLC.pack "abc")
-- RunGet ()
pushBack ::
  [B.ByteString]
  -> Get e ()
pushBack z =
  let y [] = id
      y _ = B.concat . (:z)
  in Get (\b q -> q (y z b) ())
{-# INLINE pushBack #-}

-- |
--
-- >>> runGet (pushFront (BC.pack "def") :: Get () ()) (BLC.pack "")
-- RunGet ()
--
-- >>> runGet (pushFront (BC.pack "def") :: Get () ()) (BLC.pack "abc")
-- RunGet ()
pushFront ::
  B.ByteString
  -> Get e ()
pushFront b =
  Get (\c q -> q (B.append b c) ())
{-# INLINE pushFront #-}

xrunGetIncremental ::
  Get e a
  -> XDecoder e a
xrunGetIncremental (Get k) =
  noMeansNo (k B.empty XDone)

noMeansNo ::
  XDecoder e a
  -> XDecoder e a
noMeansNo = 
  let neverAgain (XPartial k) =
        neverAgain (k Nothing)
      neverAgain (XBytesRead i k) =
        XBytesRead i (neverAgain . k)
      neverAgain r@(XDone _ _) =
        r
      neverAgain r@(XFail _ _) =
        r
      go (XPartial k) =
        XPartial (\b -> 
          (if isJust b then go else neverAgain) (k b))
      go (XBytesRead i k) =
        XBytesRead i (go . k)
      go r@(XDone _ _) =
        r
      go r@(XFail _ _) =
        r
  in go

prompt ::
  B.ByteString
  -> XDecoder e a
  -> (B.ByteString -> XDecoder e a)
  -> XDecoder e a
prompt b d f =
  let loop = XPartial (maybe d (\s -> if B.null s then loop else f (b `B.append` s)))
  in loop

-- |
--
-- >>> runGet (bytesRead :: Get () Int64) (BLC.pack "")
-- RunGet 0
--
-- >>> runGet (bytesRead :: Get () Int64) (BLC.pack "abc")
-- RunGet 0
--
-- >>> runGet (word8 >> word16be >> word32le >> bytesRead) (BLC.pack "abcdefghijk")
-- RunGet 7
bytesRead ::
  Get e Int64
bytesRead =
  Get (\b q -> XBytesRead (fromIntegral (B.length b)) (q b))

-- |
--
-- >>> runGet (isolate 1 word8) (BLC.pack "ab")
-- RunGet 97
--
-- >>> runGet (isolate 1 word8) (BLC.pack "abcde")
-- RunGet 97
--
-- >>> runGet (isolate 2 word16le) (BLC.pack "abcde")
-- RunGet 25185
--
-- >>> runGet (isolate 1 word16le) (BLC.pack "abcde")
-- RunGetFail 0 (IsolateXFail ())
--
-- >>> runGet (isolate (-3) word16le) (BLC.pack "abcde")
-- RunGetFail 0 NegativeSize
--
-- >>> runGet (isolate 3 word16le) (BLC.pack "abcde")
-- RunGetFail 2 (UnexpectedConsumed 2 3)
isolate ::
  Int
  -> Get e a
  -> Get (IsolateError e) a
isolate m (Get k) =
  let go !n (XDone l x) =
        if n == 0 && B.null l
          then
            return x
          else
            do pushFront l
               failGet (_UnexpectedConsumed # (m - n - B.length l, m))
      go 0 (XPartial r) =
        go 0 (r Nothing)
      go n (XPartial r) =
        do i <- Get (\b q -> let takeLimited t =
                                   let (j, o) = B.splitAt n t
                                   in q o (Just j)
                             in if B.null b
                                  then
                                    prompt b (q B.empty Nothing) takeLimited
                                  else
                                    takeLimited b)
           case i of
             Nothing ->
               go n (r Nothing)
             Just u ->
               go (n - B.length u) (r (Just u))       
      go _ (XFail b e) =
        pushFront b >> failGet (_IsolateXFail # e)
      go n (XBytesRead i r) =
        go n (r $! fromIntegral m - fromIntegral n - i)
  in if m < 0
       then
         failGet (_NegativeSize # ())
       else
         go m (k B.empty XDone)

-- |
--
-- >>> runGet demandInput (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet demandInput (BLC.pack "a")
-- RunGet ()
--
-- >>> runGet demandInput (BLC.pack "abc")
-- RunGet ()
demandInput ::
  Get () ()
demandInput =
  Get (\b q ->
    prompt b (XFail b ()) (`q` ()))

-- |
--
-- >>> runGet (word8 >>= \c -> skip 2 >> word8 >>= \d -> return (c,d)) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (word8 >>= \c -> skip 2 >> word8 >>= \d -> return (c,d)) (BLC.pack "abcdefghi")
-- RunGet (97,100)
--
-- >>> runGet (word8 >>= \c -> skip 2 >> word8 >>= \d -> return (c,d)) (BLC.pack "abc")
-- RunGetFail 3 ()
skip ::
  Int
  -> Get () ()
skip n =
  readN n (return ())
{-# INLINE skip #-}

-- |
--
-- >>> runGet isNotEmpty (BLC.pack "")
-- RunGet False
--
-- >>> runGet isNotEmpty (BLC.pack "abc")
-- RunGet True
--
-- >>> runGet (isNotEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "abc")
-- RunGet (97,True)
--
-- >>> runGet (isNotEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (isNotEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "a")
-- RunGet (97,True)
isNotEmpty ::
  Get e Bool
isNotEmpty =
  fmap not isEmpty

-- |
--
-- >>> runGet isEmpty (BLC.pack "")
-- RunGet True
--
-- >>> runGet isEmpty (BLC.pack "abc")
-- RunGet False
--
-- >>> runGet (isEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "abc")
-- RunGet (97,False)
--
-- >>> runGet (isEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (isEmpty >>= \p -> word8 >>= \w -> return (w, p)) (BLC.pack "a")
-- RunGet (97,False)
isEmpty ::
  Get e Bool
isEmpty =
  Get (\b q ->
    if B.null b
      then
        prompt b (q b True) (`q` False)
      else
        q b False)

-- |
--
-- >>> runGet (lookAhead word8) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (lookAhead word8) (BLC.pack "abc")
-- RunGet 97
--
-- >>> runGet (lookAhead word8) (BLC.pack "a")
-- RunGet 97
lookAhead ::
  Get e a
  -> Get e a
lookAhead g =
  do (d, bs) <- runAndKeepTrack g
     case d of
       CompletedDone _ a ->
         pushBack bs >> return a
       CompletedFail inp s ->
         constant (XFail inp s)

-- |
--
-- >>> runGet (lookAheadM (word8 >>= \w -> return (if even w then Just (w + 5) else Nothing))) (BLC.pack "abc")
-- RunGet Nothing
--
-- >>> runGet (lookAheadM (word8 >>= \w -> return (if even w then Just (w + 5) else Nothing))) (BLC.pack "bc")
-- RunGet (Just 103)
--
-- >>> runGet (lookAheadM (word8 >>= \w -> return (if even w then Just (w + 5) else Nothing))) (BLC.pack "")
-- RunGetFail 0 ()
lookAheadM ::
  Get e (Maybe a)
  -> Get e (Maybe a)
lookAheadM g =
  let g' = fmap (maybe (Left ()) Right) g
  in fmap (either (return Nothing) Just) (lookAheadE g')

-- |
--
-- >>> runGet (lookAheadE (word8 >>= \w -> return (if even w then Left (w + 5) else Right (w - 4)))) (BLC.pack "abc")
-- RunGet (Right 93)
--
-- >>> runGet (lookAheadE (word8 >>= \w -> return (if even w then Left (w + 5) else Right (w - 4)))) (BLC.pack "bc")
-- RunGet (Left 103)
--
-- >>> runGet (lookAheadE (word8 >>= \w -> return (if even w then Left (w + 5) else Right (w - 4)))) (BLC.pack "")
-- RunGetFail 0 ()
lookAheadE ::
  Get e (Either a b)
  -> Get e (Either a b)
lookAheadE g =
  do (d, b) <- runAndKeepTrack g
     case d of
       CompletedDone _ (Left x) ->
         pushBack b >> return (Left x)
       CompletedDone i (Right x) ->
         Get (\_ q -> q i (Right x))
       CompletedFail i s ->
         constant (XFail i s)

-- |
--
-- >>> runGet ([(), ()] !+ (setLabel [] word8)) (BLC.pack "")
-- RunGetFail 0 [(),()]
--
-- >>> runGet ([(), ()] !+ (setLabel [] word8)) (BLC.pack "abc")
-- RunGet 97
(!+) ::
  Semigroup e =>
  e
  -> Get e a
  -> Get e a
(!+) =
  addLabel

infixl 3 !+

-- |
--
-- >>> runGet ([(), ()] `addLabel` (setLabel [] word8)) (BLC.pack "")
-- RunGetFail 0 [(),()]
--
-- >>> runGet ([(), ()] `addLabel` (setLabel [] word8)) (BLC.pack "abc")
-- RunGet 97
addLabel ::
  Semigroup e =>
  e
  -> Get e a
  -> Get e a
addLabel m =
  modifyLabel (<> m)

-- |
--
-- >>> runGet ("error" !- word8) (BLC.pack "")
-- RunGetFail 0 "error"
--
-- >>> runGet ("error" !- word8) (BLC.pack "abc")
-- RunGet 97
(!-) ::
  e
  -> Get d a
  -> Get e a
(!-) =
  setLabel

infixl 3 !-

-- |
--
-- >>> runGet ("error" `setLabel` word8) (BLC.pack "")
-- RunGetFail 0 "error"
--
-- >>> runGet ("error" `setLabel` word8) (BLC.pack "abc")
-- RunGet 97
setLabel ::
  e
  -> Get d a
  -> Get e a
setLabel =
  modifyLabel . return

-- |
--
-- >>> runGet (reverse !!- setLabel "error" word8) (BLC.pack "")
-- RunGetFail 0 "rorre"
--
-- >>> runGet (reverse !!- setLabel "error" word8) (BLC.pack "abc")
-- RunGet 97
(!!-) ::
  (d -> e)
  -> Get d a
  -> Get e a
(!!-) =
  modifyLabel

infixl 3 !!-

-- |
--
-- >>> runGet (reverse `modifyLabel` setLabel "error" word8) (BLC.pack "")
-- RunGetFail 0 "rorre"
--
-- >>> runGet (reverse `modifyLabel` setLabel "error" word8) (BLC.pack "abc")
-- RunGet 97
modifyLabel ::
  (d -> e)
  -> Get d a
  -> Get e a
modifyLabel m =
  bimap m id

-- |
--
-- >>> runGet (getByteString (-3)) (BLC.pack "")
-- RunGet ""
--
-- >>> runGet (getByteString 3) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (getByteString 3) (BLC.pack "abc")
-- RunGet "abc"
--
-- >>> runGet (getByteString 3) (BLC.pack "abcdef")
-- RunGet "abc"
getByteString ::
  Int
  -> Get () B.ByteString
getByteString n =
  if n > 0
    then 
      readN n (BU.unsafeTake n)
    else
      return B.empty
{-# INLINE getByteString #-}

modify ::
  (B.ByteString -> B.ByteString)
  -> Get e ()
modify f =
  Get (\b q -> q (f b) ())

get ::
  Get e B.ByteString
get =
  Get (\b q -> q b b)

put ::
  B.ByteString -> Get e ()
put s = 
  Get (\_ q -> q s ())

-- |
--
-- >>> runGet (readN 3 id) (BLC.pack "abc")
-- RunGet "abc"
--
-- >>> runGet (readN 3 id) (BLC.pack "ab")
-- RunGetFail 0 ()
--
-- >>> runGet (readN 3 id) (BLC.pack "abcdef")
-- RunGet "abcdef"
--
-- >>> runGet (readN (-3) id) (BLC.pack "abcdef")
-- RunGet ""
readN ::
  Int
  -> (B.ByteString -> a)
  -> Get () a
readN !n f =
  ensureN n >> unsafeReadN n f
{-# INLINE [0] readN #-}

{-# RULES

"readN/readN merge" forall n m f g.
  readN n f `apG` readN m g =
    readN (n+m) (\bs -> f bs (g (BU.unsafeDrop n bs)))

"returnG/readN swap" [~1] forall f.
  returnG f =
    readN 0 (const f)

"readN 0/returnG swapback" [1] forall f.
  readN 0 f =
    returnG (f B.empty) 

  #-}

-- |
--
-- >>> runGet (ensureN 3) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (ensureN 3) (BLC.pack "abc")
-- RunGet ()
--
-- >>> runGet (ensureN 3) (BLC.pack "abcdef")
-- RunGet ()
ensureN ::
  Int
  -> Get () ()
ensureN !m =
  Get (\i k -> 
    let go n =
          Get (\b q -> if B.length b >= n
                         then
                           q b ()
                         else
                           let Get g = demandInput >> go n
                           in g b q)
    in if B.length i >= m
         then
           k i ()
         else
           let Get g = go m
           in g i k)
{-# INLINE ensureN #-}

toFloat ::
  (Storable w, Storable f) =>
  w
  -> f
toFloat w =
  unsafePerformIO (alloca (\buf ->
    do poke (castPtr buf) w
       peek buf))
{-# INLINE toFloat #-}

toFloat16 ::
  Word16
  -> Float
toFloat16 word16 = 
  let sign16 =
        word16 .&. 0x8000
      exp16 =
        word16 .&. 0x7C00
      frac16 =
        word16 .&. 0x3FF
      sign32 =
        if sign16 > 0
          then
            0x80000000 -- -0.0
            
          else
            0
      word32
        :: Word32
      word32 | word16 .&. 0x7FFF == 0 =
        0
             | exp16 == 0x7C00 =
        special
             | otherwise =
        shiftL exp32 23 .|. shiftL frac32 13
      special =
        if frac16 == 0
          -- Infinity
          then 0x7F800000
          
          -- NaN; signals are maintained in lower 10 bits
          else 0x7FC00000 .|. fromIntegral frac16
      (exp32, frac32) =
        if exp16 > 0
          then normalised
          else denormalised
      normalised =
        let exp = (fromIntegral exp16 `shiftR` 10) - 15 + 127
            frac = fromIntegral frac16
        in (exp, frac)
      denormalised =
        let exp = (fromIntegral exp16 `shiftR` 10) - 15 + 127 - e
            (e, frac ) = 
              let step acc x = if x .&. 0x400 == 0
                    then step (acc + 1) (shiftL x 1)
                    else (acc, fromIntegral x .&. 0x3FF)
              in step 0 (shiftL frac16 1) 
        in (exp, frac)
  in toFloat (sign32 .|. word32) 
{-# INLINE toFloat16 #-}

float16be ::
  Get () Float
float16be =
  fmap toFloat16 word16be
{-# INLINE float16be #-}

float16le ::
  Get () Float
float16le =
  fmap toFloat16 word16le
{-# INLINE float16le #-}

float32be ::
  Get () Float
float32be =
  fmap toFloat word32be
{-# INLINE float32be #-}

float32le ::
  Get () Float
float32le =
  fmap toFloat word32le
{-# INLINE float32le #-}

float64be ::
  Get () Double
float64be =
  fmap toFloat word64be
{-# INLINE float64be #-}

float64le ::
  Get () Double
float64le =
  fmap toFloat word64le
{-# INLINE float64le #-}

int8 ::
  Get () Int8
int8 =
  fmap fromIntegral word8
{-# INLINE int8 #-}

int16be ::
  Get () Int16
int16be =
  fmap fromIntegral word16be
{-# INLINE int16be #-}

int16le ::
  Get () Int16
int16le =
  fmap fromIntegral word16le
{-# INLINE int16le #-}

int32be ::
  Get () Int32
int32be =
  fmap fromIntegral word32be
{-# INLINE int32be #-}

int32le ::
  Get () Int32
int32le =
  fmap fromIntegral word32le
{-# INLINE int32le #-}

int64be ::
  Get () Int64
int64be =
  fmap fromIntegral word64be
{-# INLINE int64be #-}

int64le ::
  Get () Int64
int64le =
  fmap fromIntegral word64le
{-# INLINE int64le #-}

data IntegerError =
  IntegerTagUnexpectedEof
  | Integer0TagUnexpectedEof Word8
  | Integer1TagUnexpectedEof 
  | IntegerListError ListError
  deriving (Eq, Ord, Show)

integerError :: 
  a
  -> (Word8 -> a)
  -> a
  -> (ListError -> a)
  -> IntegerError
  -> a
integerError u _ _ _ IntegerTagUnexpectedEof =
  u
integerError _ u _ _ (Integer0TagUnexpectedEof w) =
  u w
integerError _ _ u _ Integer1TagUnexpectedEof =
  u
integerError _ _ _ u (IntegerListError e) =
  u e

_IntegerTagUnexpectedEof ::
  Prism' IntegerError ()
_IntegerTagUnexpectedEof =
  prism'
    (\() -> IntegerTagUnexpectedEof)
    (\x -> case x of
             IntegerTagUnexpectedEof ->
               Just ()
             _ ->
               Nothing)

_Integer0TagUnexpectedEof ::
  Prism' IntegerError Word8
_Integer0TagUnexpectedEof =
  prism'
    Integer0TagUnexpectedEof
    (\x -> case x of
             Integer0TagUnexpectedEof w ->
               Just w
             _ ->
               Nothing)

_Integer1TagUnexpectedEof ::
  Prism' IntegerError ()
_Integer1TagUnexpectedEof =
  prism'
    (\() -> Integer1TagUnexpectedEof)
    (\x -> case x of
             Integer1TagUnexpectedEof ->
               Just ()
             _ ->
               Nothing)

_IntegerListError ::
  Prism' IntegerError ListError
_IntegerListError =
  prism'
    IntegerListError
    (\x -> case x of
             IntegerListError e ->
               Just e
             _ ->
               Nothing)

integer ::
  Get IntegerError Integer
integer =
  do t <- IntegerTagUnexpectedEof !- word8
     case t of
       0 ->
         Integer0TagUnexpectedEof t !- fmap fromIntegral int32be
       _ -> 
         do s <- Integer1TagUnexpectedEof !- word8
            y <- IntegerListError !!- list word8
            let v = foldr (\b a -> a `shiftL` 8 .|. fromIntegral b) 0 y
            return $! if s == (1 :: Word8) then v else - v

data ListError =
  ListUnexpectedEof
  | ListTagError
  deriving (Eq, Ord, Show)

listError ::
  a
  -> a
  -> ListError
  -> a
listError u _ ListUnexpectedEof =
  u
listError _ e ListTagError =
  e

listErrorIso ::
  Iso' Bool ListError
listErrorIso =
  iso
    (\p -> if p then ListUnexpectedEof else ListTagError)
    (== ListUnexpectedEof)

list ::
  Get e a
  -> Get ListError [a]
list q =
  do n <- ListTagError !- int64be
     ListUnexpectedEof !- many q n

many ::
  Get e a
  -> Int64
  -> Get e [a]
many g n =
  let go x 0 =
        return $! reverse x
      go x i =
        do a <- g
           x `seq` go (a:x) (i - 1)
  in go [] n

unsafeReadN ::
  Int 
  -> (B.ByteString -> a)
  -> Get e a
unsafeReadN !n f = 
  Get (\b ks -> ks (BU.unsafeDrop n b) $! f b)

readNWith ::
  Int
  -> (Ptr a -> IO a)
  -> Get () a
readNWith n f =
  readN n (\s -> BI.inlinePerformIO (BU.unsafeUseAsCString s (f . castPtr)))
{-# INLINE readNWith #-}

data Decoder e a =
  Fail !B.ByteString {-# UNPACK #-} !Int64 e
  | Partial (Maybe B.ByteString -> Decoder e a)
  | Done !B.ByteString {-# UNPACK #-} !Int64 a

-- |
--
-- >>> decoder (\b i n -> B.length b + fromIntegral i + n) (\_ -> 99) (\b n a -> B.length b + fromIntegral n + a) (_Fail # (BC.pack "abc", 12, 19))
-- 34
--
-- >>> decoder (\b i n -> B.length b + fromIntegral i + n) (\_ -> 99) (\b n a -> B.length b + fromIntegral n + a) (_Partial # (\b -> _Fail # (fromMaybe (BC.pack "abc") b, 12, 19)))
-- 99
--
-- >>> decoder (\b i n -> B.length b + fromIntegral i + n) (\_ -> 99) (\b n a -> B.length b + fromIntegral n + a) (_Done # (BC.pack "abc", 12, 19))
-- 34
decoder :: 
  (B.ByteString -> Int64 -> e -> x)
  -> ((Maybe B.ByteString -> Decoder e a) -> x)
  -> (B.ByteString -> Int64 -> a -> x)
  -> Decoder e a
  -> x
decoder f _ _ (Fail b i e) =
  f b i e
decoder _ p _ (Partial k) =
  p k
decoder _ _ d (Done b i a) =
  d b i a

-- |
--
-- >>> (_Fail # (BC.pack "abc", 19, 31)) ^? _Fail
-- Just ("abc",19,31)
--
-- >>> isNothing ((_Fail # (BC.pack "abc", 19, 31)) ^? _Partial)
-- True
--
-- >>> (_Fail # (BC.pack "abc", 19, 31)) ^? _Done
-- Nothing
_Fail ::
  Prism' (Decoder e a) (B.ByteString, Int64, e)
_Fail =
  prism'
    (\(b, i, e) -> Fail b i e)
    (\x -> case x of
             Fail b i e -> Just (b, i, e)
             _ -> Nothing)

-- |
--
-- >>> (_Partial # (\b -> _Fail # (fromMaybe (BC.pack "abc") b, 12, 19))) ^? _Fail
-- Nothing
--
-- >>> isJust ((_Partial # (\b -> _Fail # (fromMaybe (BC.pack "abc") b, 12, 19))) ^? _Partial)
-- True
--
-- >>> (_Partial # (\b -> _Fail # (fromMaybe (BC.pack "abc") b, 12, 19))) ^? _Done
-- Nothing
_Partial ::
  Prism' (Decoder e a) (Maybe B.ByteString -> Decoder e a)
_Partial =
  prism'
    Partial
    (\x -> case x of
             Partial k -> Just k
             _ -> Nothing)

-- |
--
-- >>> (_Done # (BC.pack "abc", 19, 31)) ^? _Fail
-- Nothing
--
-- >>> isNothing ((_Done # (BC.pack "abc", 19, 31)) ^? _Partial)
-- True
--
-- >>> (_Done # (BC.pack "abc", 19, 31)) ^? _Done
-- Just ("abc",19,31)
_Done ::
  Prism' (Decoder e a) (B.ByteString, Int64, a)
_Done =
  prism'
    (\(b, i, a) -> Done b i a)
    (\x -> case x of
             Done b i a -> Just (b, i, a)
             _ -> Nothing)

bimapD ::
  (e -> f)
  -> (a -> b)
  -> Decoder e a
  -> Decoder f b
bimapD f _ (Fail b i e) =
  Fail b i (f e)
bimapD f g (Partial k) =
  Partial (bimapD f g . k)
bimapD _ g (Done b i a) =
  Done b i (g a)
{-# INLINE bimapD #-}

-- |
--
-- >>> (bimap (+10) (*20) (_Fail # (BC.pack "abc", 19, 31))) ^? _Fail
-- Just ("abc",19,41)
--
-- >>> (bimap (+10) (*20) (_Done # (BC.pack "abc", 19, 31))) ^? _Done
-- Just ("abc",19,620)
instance Bifunctor Decoder where
  bimap =
    bimapD

fmapD ::
  (a -> b)
  -> Decoder e a
  -> Decoder e b
fmapD =
  bimapD id
{-# INLINE fmapD #-}

-- |
--
-- >>> (fmap (+10) (_Fail # (BC.pack "abc", 19, 31))) ^? _Fail
-- Just ("abc",19,31)
--
-- >>> (fmap (+10) (_Done # (BC.pack "abc", 19, 31))) ^? _Done
-- Just ("abc",19,41)
instance Functor (Decoder e) where
  fmap =
    fmapD

calculateOffset :: 
  XDecoder e a
  -> Decoder e a
calculateOffset s =
  let go r !acc =
        case r of
          XDone i a ->
            Done i (acc - fromIntegral (B.length i)) a
          XFail i e ->
            Fail i (acc - fromIntegral (B.length i)) e
          XPartial k ->
            Partial (\b ->
              case b of
                Nothing -> go (k Nothing) acc
                Just j -> go (k b) (acc + fromIntegral (B.length j)))
          XBytesRead i k ->
            go (k $! (acc - i)) acc
  in go s 0

runGetIncremental ::
  Get e a
  -> Decoder e a
runGetIncremental =
  calculateOffset . xrunGetIncremental

takeHeadChunk ::
  L.ByteString
  -> Maybe B.ByteString
takeHeadChunk lbs =
  case lbs of
    (LI.Chunk bs _) ->
      Just bs
    _ ->
      Nothing

dropHeadChunk ::
  L.ByteString
  -> L.ByteString
dropHeadChunk lbs =
  case lbs of
    (LI.Chunk _ lbs') ->
      lbs'
    _ ->
      LI.Empty

-- | An alias for @runGet@.
--
-- >>> (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2)) .>> BLC.pack "abc"
-- RunGet 195
(.>>) ::
  Get e a
  -> L.ByteString
  -> RunGetResult e a
(.>>) =
  runGet 

infixl 2 .>>

-- | An alias for @runGet@ with the arguments flipped.
--
-- >>> BLC.pack "abc" <<. (word8 >>= \c1 -> word8 >>= \c2 -> return (c1 + c2))
-- RunGet 195
(<<.) ::
  L.ByteString
  -> Get e a
  -> RunGetResult e a
(<<.) b =
  (`runGet` b)

infixl 2 <<.

runGet ::
  Get e a
  -> L.ByteString
  -> RunGetResult e a
runGet g b =
  let feedAll (Done _ _ x) _ =
        _RunGet # x
      feedAll (Partial k) c =
        feedAll (k (takeHeadChunk c)) (dropHeadChunk c)
      feedAll (Fail _ p e) _ =
        _RunGetFail # (p, e)
  in feedAll (runGetIncremental g) b
    
pushChunk ::
  Decoder e a
  -> B.ByteString
  -> Decoder e a
pushChunk r i =
  case r of
    Done j p a ->
      Done (j `B.append` i) p a
    Partial k ->
      k (Just i)
    Fail j p s ->
      Fail (j `B.append` i) p s

pushChunks ::
  Decoder e a
  -> L.ByteString
  -> Decoder e a
pushChunks r0 =
  let go r [] =
        r
      go (Done i p a) xs =
        Done (B.concat (i:xs)) p a
      go (Fail i p s) xs =
        Fail (B.concat (i:xs)) p s
      go (Partial k) (x:xs) =
        go (k (Just x)) xs
  in go r0 . L.toChunks
    
pushEndOfInput ::
  Decoder e a
  -> Decoder e a
pushEndOfInput r =
  case r of
    Done {} -> r
    Partial k -> k Nothing
    Fail {} -> r

-- |
--
-- >>> runGet (lazyByteString 5) (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet (lazyByteString 5) (BLC.pack "abc")
-- RunGetFail 3 ()
--
-- >>> runGet (lazyByteString 5) (BLC.pack "abcdefg")
-- RunGet "abcde"
lazyByteString ::
  Int64
  -> Get () L.ByteString
lazyByteString =
  let consume n s =
        if fromIntegral (B.length s) >= n
          then
            Right (B.splitAt (fromIntegral n) s)
          else
            Left (fromIntegral (B.length s))
      go n =
        do s <- get
           case consume n s of
             Left u ->
               do put B.empty
                  demandInput
                  fmap (s:) (go (n - u))
             Right (w, r) ->
               do put r
                  return [w]
  in fmap L.fromChunks . go

-- |
--
-- >>> runGet lazyByteStringNul (BLC.pack "")
-- RunGetFail 0 ()
--
-- >>> runGet lazyByteStringNul (BLC.pack "abc")
-- RunGetFail 3 ()
--
-- >>> runGet lazyByteStringNul (BLC.pack "abc\0")
-- RunGet "abc"
--
-- >>> runGet lazyByteStringNul (BLC.pack "abc\0def")
-- RunGet "abc"
lazyByteStringNul ::
  Get () L.ByteString
lazyByteStringNul =
  let findNull s =
        case B.break (==0) s of
          (w, r) ->
            if B.null r 
              then
                Nothing
              else
                Just (w, B.drop 1 r)
      go =
        do s <- get 
           case findNull s of
             Nothing ->
               do put B.empty
                  demandInput
                  fmap (s:) go
             Just (w, r) -> 
               do put r
                  return [w]
  in fmap L.fromChunks go

-- |
--
-- >>> runGet remainingLazyByteString  (BLC.pack "")
-- RunGet ""
--
-- >>> runGet remainingLazyByteString  (BLC.pack "abc")
-- RunGet "abc"
remainingLazyByteString ::
  Get e L.ByteString
remainingLazyByteString =
  let go =
        do s <- get
           put B.empty
           d <- isEmpty
           if d
             then 
               return [s]
             else
               fmap (s:) go
  in fmap L.fromChunks go

ptr ::
  Storable a =>
  Int
  -> Get () a
ptr n =
  readNWith n peek
{-# INLINE ptr #-}

{-# RULES

"word8/readN" word8 =
  readN 1 BU.unsafeHead
  
"word16be/readN" word16be =
  readN 2 word16be'

"word16le/readN" word16le =
  readN 2 word16le'

"word32be/readN" word32be =
  readN 4 word32be'

"word32le/readN" word32le =
  readN 4 word32le'

"word64be/readN" word64be =
  readN 8 word64be'

"word64le/readN" word64le =
  readN 8 word64le'

  #-}

-- |
--
-- >>> runGet word8 (BLC.pack "abc")
-- RunGet 97
--
-- >>> runGet word8 (BLC.pack "123")
-- RunGet 49
word8 ::
  Get () Word8
word8 =
  readN 1 BU.unsafeHead
{-# INLINE [0] word8 #-}

word16be' ::
  B.ByteString
  -> Word16
word16be' s =
    (fromIntegral (s `BU.unsafeIndex` 0) `shiftlW16` 8) .|.
    fromIntegral (s `BU.unsafeIndex` 1)
{-# INLINE word16be' #-}

-- |
--
-- >>> runGet word16be (BLC.pack "abc")
-- RunGet 24930
--
-- >>> runGet word16be (BLC.pack "123")
-- RunGet 12594
word16be ::
  Get () Word16
word16be =
  readN 2 word16be'
{-# INLINE [0] word16be #-}

word16le' ::
  B.ByteString
  -> Word16
word16le' s =
    (fromIntegral (s `BU.unsafeIndex` 1) `shiftlW16` 8) .|.
    fromIntegral (s `BU.unsafeIndex` 0)
{-# INLINE word16le' #-}

-- |
--
-- >>> runGet word16le (BLC.pack "abc")
-- RunGet 25185
--
-- >>> runGet word16le (BLC.pack "123")
-- RunGet 12849
word16le ::
  Get () Word16
word16le =
  readN 2 word16le'
{-# INLINE [0] word16le #-}

word32be' ::
  B.ByteString
  -> Word32
word32be' s =
    (fromIntegral (s `BU.unsafeIndex` 0) `shiftlW32` 24) .|.
    (fromIntegral (s `BU.unsafeIndex` 1) `shiftlW32` 16) .|.
    (fromIntegral (s `BU.unsafeIndex` 2) `shiftlW32`  8) .|.
    fromIntegral (s `BU.unsafeIndex` 3)
{-# INLINE word32be' #-}

-- |
--
-- >>> runGet word32be (BLC.pack "abcdef")
-- RunGet 1633837924
--
-- >>> runGet word32be (BLC.pack "123456")
-- RunGet 825373492
word32be ::
  Get () Word32
word32be =
  readN 4 word32be'
{-# INLINE [0] word32be #-}

word32le' ::
  B.ByteString
  -> Word32
word32le' s =
    (fromIntegral (s `BU.unsafeIndex` 3) `shiftlW32` 24) .|.
    (fromIntegral (s `BU.unsafeIndex` 2) `shiftlW32` 16) .|.
    (fromIntegral (s `BU.unsafeIndex` 1) `shiftlW32`  8) .|.
    fromIntegral (s `BU.unsafeIndex` 0)
{-# INLINE word32le' #-}

-- |
--
-- -- >>> runGet word32le (BLC.pack "abcdef")
-- RunGet 1684234849
--
-- >>> runGet word32le (BLC.pack "123456")
-- RunGet 875770417
word32le ::
  Get () Word32
word32le =
  readN 4 word32le'
{-# INLINE [0] word32le #-}

word64be' ::
  B.ByteString
  -> Word64
word64be' s =
    (fromIntegral (s `BU.unsafeIndex` 0) `shiftlW64` 56) .|.
    (fromIntegral (s `BU.unsafeIndex` 1) `shiftlW64` 48) .|.
    (fromIntegral (s `BU.unsafeIndex` 2) `shiftlW64` 40) .|.
    (fromIntegral (s `BU.unsafeIndex` 3) `shiftlW64` 32) .|.
    (fromIntegral (s `BU.unsafeIndex` 4) `shiftlW64` 24) .|.
    (fromIntegral (s `BU.unsafeIndex` 5) `shiftlW64` 16) .|.
    (fromIntegral (s `BU.unsafeIndex` 6) `shiftlW64`  8) .|.
    fromIntegral (s `BU.unsafeIndex` 7)
{-# INLINE word64be' #-}

-- |
--
-- >>> runGet word64be (BLC.pack "abcdefghi")
-- RunGet 7017280452245743464
--
-- >>> runGet word64be (BLC.pack "123456789")
-- RunGet 3544952156018063160
word64be ::
  Get () Word64
word64be =
  readN 8 word64be'
{-# INLINE [0] word64be #-}

word64le' ::
  B.ByteString
  -> Word64
word64le' s =
    (fromIntegral (s `BU.unsafeIndex` 7) `shiftlW64` 56) .|.
    (fromIntegral (s `BU.unsafeIndex` 6) `shiftlW64` 48) .|.
    (fromIntegral (s `BU.unsafeIndex` 5) `shiftlW64` 40) .|.
    (fromIntegral (s `BU.unsafeIndex` 4) `shiftlW64` 32) .|.
    (fromIntegral (s `BU.unsafeIndex` 3) `shiftlW64` 24) .|.
    (fromIntegral (s `BU.unsafeIndex` 2) `shiftlW64` 16) .|.
    (fromIntegral (s `BU.unsafeIndex` 1) `shiftlW64`  8) .|.
    fromIntegral (s `BU.unsafeIndex` 0) 
{-# INLINE word64le' #-}

-- |
--
-- >>> runGet word64le (BLC.pack "abcdefghi")
-- RunGet 7523094288207667809
--
-- >>> runGet word64le (BLC.pack "123456789")
-- RunGet 4050765991979987505
word64le ::
  Get () Word64
word64le =
  readN 8 word64le'
{-# INLINE [0] word64le #-}

-- |
--
-- >>> runGet wordhost (BLC.pack "abcdefghi")
-- RunGet 7523094288207667809
--
-- >>> runGet wordhost (BLC.pack "123456789")
-- RunGet 4050765991979987505
wordhost ::
  Get () Word
wordhost =
  ptr (sizeOf (undefined :: Word))
{-# INLINE wordhost #-}

-- |
--
-- >>> runGet word16host (BLC.pack "abcde")
-- RunGet 25185
--
-- >>> runGet word16host (BLC.pack "12345")
-- RunGet 12849
word16host ::
  Get () Word16
word16host =
  ptr (sizeOf (undefined :: Word16))
{-# INLINE word16host #-}

-- |
--
-- >>> runGet word32host (BLC.pack "abcde")
-- RunGet 1684234849
--
-- >>> runGet word32host (BLC.pack "12345")
-- RunGet 875770417
word32host ::
  Get () Word32
word32host =
  ptr (sizeOf (undefined :: Word32))
{-# INLINE word32host #-}

-- |
--
-- >>> runGet word64host (BLC.pack "abcdeghi")
-- RunGet 7595434456733934177
--
-- >>> runGet word64host (BLC.pack "123456789")
-- RunGet 4050765991979987505
word64host ::
  Get () Word64
word64host =
  ptr (sizeOf (undefined :: Word64))
{-# INLINE word64host #-}

------------------------------------------------------------------------
-- Unchecked shifts

shiftlW16 ::
  Word16
  -> Int
  -> Word16
shiftlW32 ::
  Word32
  -> Int
  -> Word32
shiftlW64 ::
  Word64
  -> Int
  -> Word64

#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
shiftlW16 (W16# w) (I# i) =
  W16# (w `uncheckedShiftL#`   i)
shiftlW32 (W32# w) (I# i) =
  W32# (w `uncheckedShiftL#`   i)

#if WORD_SIZE_IN_BITS < 64
shiftlW64 (W64# w) (I# i) =
  W64# (w `uncheckedShiftL64#` i)

#if __GLASGOW_HASKELL__ <= 606
-- Exported by GHC.Word in GHC 6.8 and higher
foreign import ccall unsafe "stg_uncheckedShiftL64"
  uncheckedShiftL64# ::
    Word64#
    -> Int#
    -> Word64#
#endif

#else
shiftlW64 (W64# w) (I# i) =
  W64# (w `uncheckedShiftL#` i)
#endif

#else
shiftlW16 =
  shiftL
shiftlW32 =
  shiftL
shiftlW64 =
  shiftL
#endif

----

data XDecoder e a =
  XFail !B.ByteString e
  | XPartial (Maybe B.ByteString -> XDecoder e a)
  | XDone !B.ByteString a
  | XBytesRead {-# UNPACK #-} !Int64 (Int64 -> XDecoder e a)

xdecoder :: 
  (B.ByteString -> e -> x)
  -> ((Maybe B.ByteString -> XDecoder e a) -> x)
  -> (B.ByteString -> a -> x)
  -> (Int64 -> (Int64 -> XDecoder e a) -> x)
  -> XDecoder e a
  -> x
xdecoder f _ _ _ (XFail b e) =
  f b e
xdecoder _ p _ _ (XPartial k) =
  p k
xdecoder _ _ d _ (XDone b a) =
  d b a
xdecoder _ _ _ r (XBytesRead i k) =
  r i k

_XFail ::
  Prism' (XDecoder e a) (B.ByteString, e)
_XFail =
  prism'
    (uncurry XFail)
    (\x -> case x of
             XFail b e -> Just (b, e)
             _ -> Nothing)

_XPartial ::
  Prism' (XDecoder e a) (Maybe B.ByteString -> XDecoder e a)
_XPartial =
  prism'
    XPartial
    (\x -> case x of
             XPartial k -> Just k
             _ -> Nothing)

_XDone ::
  Prism' (XDecoder e a) (B.ByteString, a)
_XDone =
  prism'
    (uncurry XDone)
    (\x -> case x of
             XDone b a -> Just (b, a)
             _ -> Nothing)
_XBytesRead ::
  Prism' (XDecoder e a) (Int64, Int64 -> XDecoder e a)
_XBytesRead =
  prism'
    (uncurry XBytesRead)
    (\x -> case x of
             XBytesRead i k -> Just (i, k)
             _ -> Nothing)

{-# INLINE _XFail #-}
{-# INLINE _XPartial #-}
{-# INLINE _XDone #-}
{-# INLINE _XBytesRead #-}

instance Functor (XDecoder e) where
  fmap =
    bimap id

instance Bifunctor XDecoder where
  bimap f _ (XFail b e) =
    XFail b (f e)
  bimap f g (XPartial k) =
    XPartial (bimap f g . k)
  bimap _ g (XDone b a) =
    XDone b (g a)
  bimap f g (XBytesRead i k) =
    XBytesRead i (bimap f g . k)
    
instance Bifoldable XDecoder where
  bifoldMap f _ (XFail _ e) =
    f e
  bifoldMap _ _ (XPartial _) =
    mempty
  bifoldMap _ g (XDone _ a) =
    g a
  bifoldMap _ _ (XBytesRead _ _) =
    mempty

instance Foldable (XDecoder e) where
  foldMap _ (XFail _ _) =
    mempty
  foldMap _ (XPartial _) =
    mempty
  foldMap f (XDone _ a) =
    f a
  foldMap _ (XBytesRead _ _) =
    mempty

data CompletedXDecoder e a =
  CompletedFail !B.ByteString e
  | CompletedDone !B.ByteString a
  deriving (Eq, Ord, Show)

completedXDecoder ::
  (B.ByteString -> e -> x)
  -> (B.ByteString -> a -> x)
  -> CompletedXDecoder e a
  -> x
completedXDecoder f _ (CompletedFail b e) =
  f b e
completedXDecoder _ d (CompletedDone b a) =
  d b a

_CompletedFail ::
  Prism' (CompletedXDecoder e a) (B.ByteString, e)
_CompletedFail =
  prism'
    (uncurry CompletedFail)
    (\x -> case x of
             CompletedFail b e -> Just (b, e)
             _ -> Nothing)

_CompletedDone ::
  Prism' (CompletedXDecoder e a) (B.ByteString, a)
_CompletedDone =
  prism'
    (uncurry CompletedDone)
    (\x -> case x of
             CompletedDone b a -> Just (b, a)
             _ -> Nothing)

completedIso ::
  Iso (CompletedXDecoder e a) (CompletedXDecoder f b) (Either e a, B.ByteString) (Either f b, B.ByteString)
completedIso =
  iso
    (\d -> case d of
             CompletedFail b e -> (Left e, b)
             CompletedDone b a -> (Right a, b))
    (\z -> case z of
             (Left e, b) -> CompletedFail b e
             (Right a, b) -> CompletedDone b a)
{-# INLINE completedIso #-}

completedByteString ::
  Lens' (CompletedXDecoder e a) B.ByteString
completedByteString =
  lens
    (\d -> case d of
             CompletedFail b _ -> b
             CompletedDone b _ -> b)
    (\d b -> case d of
               CompletedFail _ e -> CompletedFail b e
               CompletedDone _ a -> CompletedDone b a)
{-# INLINE completedByteString #-}

completedValue ::
  Lens' (CompletedXDecoder e a) (Either e a)
completedValue =
  lens
    (\d -> case d of
             CompletedFail _ e -> Left e
             CompletedDone _ a -> Right a)
    (\d z -> case d of
               CompletedFail b e -> CompletedFail b (either id (pure e) z)
               CompletedDone b a -> CompletedDone b (either (pure a) id z))
{-# INLINE completedValue #-}

instance Functor (CompletedXDecoder e) where
  fmap =
    bimap id

instance Bifunctor CompletedXDecoder where
  bimap f _ (CompletedFail b e) =
    CompletedFail b (f e)
  bimap _ g (CompletedDone b a) =
    CompletedDone b (g a)

instance Foldable (CompletedXDecoder e) where
  foldMap _ (CompletedFail _ _) =
    mempty
  foldMap f (CompletedDone _ a) =
    f a

instance Traversable (CompletedXDecoder e) where
  traverse = 
    bitraverse pure

instance Bifoldable CompletedXDecoder where
  bifoldMap f _ (CompletedFail _ e) = 
    f e
  bifoldMap _ g (CompletedDone _ a) =
    g a

instance Bitraversable CompletedXDecoder where
  bitraverse f _ (CompletedFail b e) =
    fmap (CompletedFail b) (f e)
  bitraverse _ g (CompletedDone b a) =
    fmap (CompletedDone b) (g a)

uncomplete ::
  Prism' (XDecoder e a) (CompletedXDecoder e a)
uncomplete =
  prism'
    (\d -> case d of
             CompletedFail b e -> XFail b e
             CompletedDone b a -> XDone b a)
    (\d -> case d of
             XFail b e -> Just (CompletedFail b e)
             XPartial _ -> Nothing
             XDone b a -> Just (CompletedDone b a)
             XBytesRead _ _ -> Nothing)
{-# INLINE uncomplete #-}

uncompletedByteString ::
  Traversal' (XDecoder e a) B.ByteString
uncompletedByteString = 
  uncomplete . completedByteString
{-# INLINE uncompletedByteString #-}

uncompletedValue ::
  Traversal' (XDecoder e a) (Either e a)
uncompletedValue = 
  uncomplete . completedValue
{-# INLINE uncompletedValue #-}