    {-# LANGUAGE BangPatterns #-}
    {-# LANGUAGE CPP #-}
    {-# LANGUAGE MagicHash #-}
    {-# LANGUAGE TemplateHaskellQuotes #-}
    module Data.Aeson.Internal.ByteString (
        mkBS,
        withBS,
    #ifdef MIN_VERSION_template_haskell
        liftSBS,
    #endif
    ) where

    import Data.ByteString.Internal (ByteString (..))
    import Data.Word (Word8)
    import Foreign.ForeignPtr (ForeignPtr)

    #if !MIN_VERSION_bytestring(0,11,0)
    import GHC.ForeignPtr (plusForeignPtr)
    #endif

    #ifdef MIN_VERSION_template_haskell
    import Data.ByteString.Short (ShortByteString, fromShort)
    import Data.ByteString.Short.Internal (createFromPtr)
    import GHC.Exts (Addr#, Ptr (Ptr))
    import System.IO.Unsafe (unsafeDupablePerformIO)

    import qualified Language.Haskell.TH.Lib as TH
    import qualified Language.Haskell.TH.Syntax as TH

    #if !MIN_VERSION_template_haskell(2,16,0)
    import qualified Data.ByteString as BS
    #endif
    #endif

    -------------------------------------------------------------------------------
    -- bytestring-0.11 compat
    -------------------------------------------------------------------------------

    mkBS :: ForeignPtr Word8 -> Int -> ByteString
    #if MIN_VERSION_bytestring(0,11,0)
    mkBS :: ForeignPtr Word8 -> Int -> ByteString
    mkBS ForeignPtr Word8
    dfp Int
    n = ForeignPtr Word8 -> Int -> ByteString
    BS ForeignPtr Word8
    dfp Int
    n
    #else
    mkBS dfp n = PS dfp 0 n
    #endif
    {-# INLINE mkBS #-}

    withBS :: ByteString -> (ForeignPtr Word8 -> Int -> r) -> r
    #if MIN_VERSION_bytestring(0,11,0)
    withBS :: forall r. ByteString -> (ForeignPtr Word8 -> Int -> r) -> r
    withBS (BS !ForeignPtr Word8
    sfp !Int
    slen)       ForeignPtr Word8 -> Int -> r
    kont = ForeignPtr Word8 -> Int -> r
    kont ForeignPtr Word8
    sfp Int
    slen
    #else
    withBS (PS !sfp !soff !slen) kont = kont (plusForeignPtr sfp soff) slen
    #endif
    {-# INLINE withBS #-}

    -------------------------------------------------------------------------------
    -- Template Haskell
    -------------------------------------------------------------------------------

    #ifdef MIN_VERSION_template_haskell
    liftSBS :: ShortByteString -> TH.ExpQ
    #if MIN_VERSION_template_haskell(2,16,0)
    liftSBS :: ShortByteString -> ExpQ
    liftSBS ShortByteString
    sbs = ByteString -> (ForeignPtr Word8 -> Int -> ExpQ) -> ExpQ
    forall r. ByteString -> (ForeignPtr Word8 -> Int -> r) -> r
    withBS ByteString
    bs ((ForeignPtr Word8 -> Int -> ExpQ) -> ExpQ)
    -> (ForeignPtr Word8 -> Int -> ExpQ) -> ExpQ
    forall a b. (a -> b) -> a -> b
    $ \ForeignPtr Word8
    ptr Int
    len -> [| unsafePackLenLiteral |]
        ExpQ -> ExpQ -> ExpQ
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
    `TH.appE` Lit -> ExpQ
    forall (m :: * -> *). Quote m => Lit -> m Exp
    TH.litE (Integer -> Lit
    TH.integerL (Int -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len))
        ExpQ -> ExpQ -> ExpQ
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
    `TH.appE` Lit -> ExpQ
    forall (m :: * -> *). Quote m => Lit -> m Exp
    TH.litE (Bytes -> Lit
    TH.BytesPrimL (Bytes -> Lit) -> Bytes -> Lit
    forall a b. (a -> b) -> a -> b
    $ ForeignPtr Word8 -> Word -> Word -> Bytes
    TH.Bytes ForeignPtr Word8
    ptr Word
    0 (Int -> Word
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len))
        where
          bs :: ByteString
    bs = ShortByteString -> ByteString
    fromShort ShortByteString
    sbs
    #else
    liftSBS sbs = withBS bs $ \_ len -> [| unsafePackLenLiteral |]
        `TH.appE` TH.litE (TH.integerL (fromIntegral len))
        `TH.appE` TH.litE (TH.StringPrimL $ BS.unpack bs)
        where
          bs = fromShort sbs
    #endif

    -- this is copied verbatim from @bytestring@, but only in recent versions.
    unsafePackLenLiteral :: Int -> Addr# -> ShortByteString
    unsafePackLenLiteral :: Int -> Addr# -> ShortByteString
    unsafePackLenLiteral Int
    len Addr#
    addr# =
        IO ShortByteString -> ShortByteString
    forall a. IO a -> a
    unsafeDupablePerformIO (IO ShortByteString -> ShortByteString)
    -> IO ShortByteString -> ShortByteString
    forall a b. (a -> b) -> a -> b
    $ Ptr (ZonkAny 0) -> Int -> IO ShortByteString
    forall a. Ptr a -> Int -> IO ShortByteString
    createFromPtr (Addr# -> Ptr (ZonkAny 0)
    forall a. Addr# -> Ptr a
    Ptr Addr#
    addr#) Int
    len
    #endif