    {-# LANGUAGE BangPatterns #-}
    {-# LANGUAGE MultiWayIf #-}
    {-# LANGUAGE TypeApplications #-}
    -- |
    -- Module       : Data.ByteString.Base64.Internal.W32.Loop
    -- Copyright    : (c) 2019-2020 Emily Pillmore
    -- License      : BSD-style
    --
    -- Maintainer   : Emily Pillmore <emilypi@cohomolo.gy>
    -- Stability    : Experimental
    -- Portability  : portable
    --
    -- 'Word32'-optimized inner loop
    --
    module Data.ByteString.Base64.Internal.W32.Loop
    ( innerLoop
    , decodeLoop
    , lenientLoop
    ) where

    import Data.Bits
    import Data.ByteString.Internal
    import Data.ByteString.Base64.Internal.Utils
    import qualified Data.ByteString.Base64.Internal.W16.Loop as W16
    import Data.Text (Text)

    import Foreign.ForeignPtr
    import Foreign.Ptr
    import Foreign.Storable

    import GHC.Word

    -- | Encoding inner loop. Packs 3 bytes from src pointer into
    -- the first 6 bytes of 4 'Word8''s (using the encoding table,
    -- as 2 'Word12''s ), writing these to the dst pointer.
    --
    innerLoop
        :: Ptr Word16
        -> Ptr Word32
        -> Ptr Word32
        -> Ptr Word32
        -> (Ptr Word8 -> Ptr Word8 -> IO ByteString)
        -> IO ByteString
    innerLoop :: Ptr Word16
    -> Ptr Word32
    -> Ptr Word32
    -> Ptr Word32
    -> (Ptr Word8 -> Ptr Word8 -> IO ByteString)
    -> IO ByteString
    innerLoop !Ptr Word16
    etable !Ptr Word32
    sptr !Ptr Word32
    dptr !Ptr Word32
    end Ptr Word8 -> Ptr Word8 -> IO ByteString
    finish = Ptr Word32 -> Ptr Word32 -> IO ByteString
    forall {b}.
    (Storable b, Num b) =>
    Ptr Word32 -> Ptr b -> IO ByteString
    go Ptr Word32
    sptr Ptr Word32
    dptr
      where
        go :: Ptr Word32 -> Ptr b -> IO ByteString
    go !Ptr Word32
    src !Ptr b
    dst
          | Ptr Word32 -> Int -> Ptr Word32
    forall a b. Ptr a -> Int -> Ptr b
    plusPtr Ptr Word32
    src Int
    3 Ptr Word32 -> Ptr Word32 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Ptr Word32
    end =
            Ptr Word16
    -> Ptr Word8
    -> Ptr Word16
    -> Ptr Word8
    -> (Ptr Word8 -> Ptr Word8 -> IO ByteString)
    -> IO ByteString
    W16.innerLoop Ptr Word16
    etable (Ptr Word32 -> Ptr Word8
    forall a b. Ptr a -> Ptr b
    castPtr Ptr Word32
    src) (Ptr b -> Ptr Word16
    forall a b. Ptr a -> Ptr b
    castPtr Ptr b
    dst) (Ptr Word32 -> Ptr Word8
    forall a b. Ptr a -> Ptr b
    castPtr Ptr Word32
    end) Ptr Word8 -> Ptr Word8 -> IO ByteString
    finish
          | Bool
    otherwise = do
            !Word32
    w <- Ptr Word32 -> IO Word32
    peekWord32BE Ptr Word32
    src

            let !a :: Word32
    a = Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    unsafeShiftR Word32
    w Int
    20
                !b :: Word32
    b = Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    unsafeShiftR Word32
    w Int
    8

            !Word32
    x <- Word16 -> Word32
    w32_16 (Word16 -> Word32) -> IO Word16 -> IO Word32
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Ptr Word16 -> Int -> IO Word16
    forall a. Storable a => Ptr a -> Int -> IO a
    peekElemOff Ptr Word16
    etable (Word32 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    a)
            !Word32
    y <- Word16 -> Word32
    w32_16 (Word16 -> Word32) -> IO Word16 -> IO Word32
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Ptr Word16 -> Int -> IO Word16
    forall a. Storable a => Ptr a -> Int -> IO a
    peekElemOff Ptr Word16
    etable (Word32 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    b)

            let !z :: Word32
    z = Word32
    x Word32 -> Word32 -> Word32
    forall a. Bits a => a -> a -> a
    .|. (Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    unsafeShiftL Word32
    y Int
    16)
            Ptr b -> b -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke Ptr b
    dst (Word32 -> b
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    z)

            Ptr Word32 -> Ptr b -> IO ByteString
    go (Ptr Word32 -> Int -> Ptr Word32
    forall a b. Ptr a -> Int -> Ptr b
    plusPtr Ptr Word32
    src Int
    3) (Ptr b -> Int -> Ptr b
    forall a b. Ptr a -> Int -> Ptr b
    plusPtr Ptr b
    dst Int
    4)
        {-# INLINE go #-}
    {-# INLINE innerLoop #-}

    decodeLoop
        :: Ptr Word8
        -> Ptr Word8
            -- ^ src pointer
        -> Ptr Word8
            -- ^ dst pointer
        -> Ptr Word8
            -- ^ end of src ptr
        -> ForeignPtr Word8
        -> IO (Either Text ByteString)
    decodeLoop :: Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> ForeignPtr Word8
    -> IO (Either Text ByteString)
    decodeLoop = Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> ForeignPtr Word8
    -> IO (Either Text ByteString)
    W16.decodeLoop
    {-# INLINE decodeLoop #-}

    lenientLoop
        :: Ptr Word8
            -- ^ decode lookup table
        -> Ptr Word8
            -- ^ src pointer
        -> Ptr Word8
            -- ^ dst pointer
        -> Ptr Word8
            -- ^ end of src ptr
        -> ForeignPtr Word8
            -- ^ dst foreign ptr (for consing bs)
        -> IO ByteString
    lenientLoop :: Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> ForeignPtr Word8
    -> IO ByteString
    lenientLoop = Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> Ptr Word8
    -> ForeignPtr Word8
    -> IO ByteString
    W16.lenientLoop