{-# 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 = forall r. ByteString -> (ForeignPtr Word8 -> Int -> r) -> r
withBS ByteString
bs forall a b. (a -> b) -> a -> b
$ \ForeignPtr Word8
ptr Int
len -> [| unsafePackLenLiteral |]
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
`TH.appE` forall (m :: * -> *). Quote m => Lit -> m Exp
TH.litE (Integer -> Lit
TH.integerL (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
`TH.appE` forall (m :: * -> *). Quote m => Lit -> m Exp
TH.litE (Bytes -> Lit
TH.BytesPrimL forall a b. (a -> b) -> a -> b
$ ForeignPtr Word8 -> Word -> Word -> Bytes
TH.Bytes ForeignPtr Word8
ptr Word
0 (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# =
    forall a. IO a -> a
unsafeDupablePerformIO forall a b. (a -> b) -> a -> b
$ forall a. Ptr a -> Int -> IO ShortByteString
createFromPtr (forall a. Addr# -> Ptr a
Ptr Addr#
addr#) Int
len
#endif