module Botan.Low.Internal.ByteString (
    peekCString
  , withCString
  , withCBytes
  , withCBytesLen
  , allocBytes
  , allocBytesWith
  , asCString
  , asCStringLen
  , asBytes
  , unsafeAsBytes
  , asBytesLen
  , unsafeAsBytesLen
  , showBytes
  ) where

import           Control.DeepSeq
import           Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as Char8
import qualified Data.ByteString.Internal as ByteString
import qualified Data.ByteString.Unsafe as ByteString
import           Data.Word
import           Foreign.C.String hiding (peekCString, peekCStringLen,
                     withCString, withCStringLen)
import           Foreign.C.Types
import           Foreign.ForeignPtr
import           Foreign.Ptr

{-
Small rant: CString is a bit of a mess

- CString doesn't work with const
- There is no CBytes
- Doesn't work with ConstPtr
- Different names for different types (peek vs pack, useAs vs with)
    - Data.ByteString
        - packCString :: CString -> IO ByteString
        - useAsCString :: ByteString -> (CString -> IO a) -> IO a
    - Text
    - Foreign.C.String
        - peekCString :: CString -> IO String
        - withCString :: String -> (CString -> IO a) -> IO a
-}

peekCString :: CString -> IO ByteString
peekCString :: CString -> IO ByteString
peekCString = CString -> IO ByteString
ByteString.packCString

withCString :: ByteString -> (CString -> IO a) -> IO a
withCString :: forall a. ByteString -> (CString -> IO a) -> IO a
withCString = ByteString -> (CString -> IO a) -> IO a
forall a. ByteString -> (CString -> IO a) -> IO a
ByteString.useAsCString

type CBytes = Ptr Word8

withCBytes :: ByteString -> (CBytes -> IO a) -> IO a
withCBytes :: forall a. ByteString -> (CBytes -> IO a) -> IO a
withCBytes ByteString
bs CBytes -> IO a
act = ByteString -> (CStringLen -> IO a) -> IO a
forall a. ByteString -> (CStringLen -> IO a) -> IO a
ByteString.useAsCStringLen ByteString
bs (\ (CString
ptr,Int
_) -> CBytes -> IO a
act (CString -> CBytes
forall a b. Ptr a -> Ptr b
castPtr CString
ptr))

type CBytesLen = (Ptr Word8, Int)

withCBytesLen :: ByteString -> (CBytesLen -> IO a) -> IO a
withCBytesLen :: forall a. ByteString -> (CBytesLen -> IO a) -> IO a
withCBytesLen ByteString
bs CBytesLen -> IO a
act = ByteString -> (CStringLen -> IO a) -> IO a
forall a. ByteString -> (CStringLen -> IO a) -> IO a
ByteString.useAsCStringLen ByteString
bs (\ (CString
ptr,Int
len) -> CBytesLen -> IO a
act (CString -> CBytes
forall a b. Ptr a -> Ptr b
castPtr CString
ptr, Int
len))

-- A cheap knockoff of ByteArray.alloc / allocRet
-- We'll make this safer in the future
-- NOTE: THIS IS NOT LIKE Foriegn.Marshal.Alloc.allocaBytes, though it is close
--  Instead of returning the thing, we always return a bytestring.
--  Also, allocaBytes frees the memory after, but this is a malloc freed on garbage collect.
-- I basically ripped the relevant bits from ByteArray for ease of continuity
allocBytes :: Int -> (Ptr byte -> IO ()) -> IO ByteString
-- allocBytes sz f = snd <$> allocBytesWith sz f
-- NOTE: This is probably better than mallocByteString withForeignPtr
--  Use of mallocByteString without mkDeferredByteString / deferForeignPtrAvailability
--  is possibly a factor in our InsufficientBufferSpaceException issues
-- NOTE: Most of the comments are rendered moot now :) this needs cleanup
allocBytes :: forall byte. Int -> (Ptr byte -> IO ()) -> IO ByteString
allocBytes Int
sz Ptr byte -> IO ()
f = Int -> (CBytes -> IO ()) -> IO ByteString
ByteString.create Int
sz (Ptr byte -> IO ()
f (Ptr byte -> IO ()) -> (CBytes -> Ptr byte) -> CBytes -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CBytes -> Ptr byte
forall a b. Ptr a -> Ptr b
castPtr)

allocBytesWith :: Int -> (Ptr byte -> IO a) -> IO (a, ByteString)
allocBytesWith :: forall byte a. Int -> (Ptr byte -> IO a) -> IO (a, ByteString)
allocBytesWith Int
sz Ptr byte -> IO a
f
    | Int
sz Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0    = Int -> (Ptr byte -> IO a) -> IO (a, ByteString)
forall byte a. Int -> (Ptr byte -> IO a) -> IO (a, ByteString)
allocBytesWith Int
0 Ptr byte -> IO a
f
    | Bool
otherwise = do
        ForeignPtr Word8
fptr <- Int -> IO (ForeignPtr Word8)
forall a. Int -> IO (ForeignPtr a)
ByteString.mallocByteString Int
sz
        a
a <- ForeignPtr Word8 -> (CBytes -> IO a) -> IO a
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
fptr (Ptr byte -> IO a
f (Ptr byte -> IO a) -> (CBytes -> Ptr byte) -> CBytes -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CBytes -> Ptr byte
forall a b. Ptr a -> Ptr b
castPtr)
        -- return (a, ByteString.PS fptr 0 sz)
        -- NOTE: The safety of this function is suspect, may require deepseq
        let bs :: ByteString
bs = ForeignPtr Word8 -> Int -> Int -> ByteString
ByteString.PS ForeignPtr Word8
fptr Int
0 Int
sz
            in ByteString
bs ByteString -> IO (a, ByteString) -> IO (a, ByteString)
forall a b. NFData a => a -> b -> b
`deepseq` (a, ByteString) -> IO (a, ByteString)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (a
a,ByteString
bs)

asCString :: ByteString -> (Ptr CChar -> IO a) -> IO a
asCString :: forall a. ByteString -> (CString -> IO a) -> IO a
asCString = ByteString -> (CString -> IO a) -> IO a
forall a. ByteString -> (CString -> IO a) -> IO a
ByteString.useAsCString

asCStringLen :: ByteString -> (Ptr CChar -> CSize -> IO a) -> IO a
asCStringLen :: forall a. ByteString -> (CString -> CSize -> IO a) -> IO a
asCStringLen ByteString
bs CString -> CSize -> IO a
f = ByteString -> (CStringLen -> IO a) -> IO a
forall a. ByteString -> (CStringLen -> IO a) -> IO a
ByteString.useAsCStringLen ByteString
bs (\ (CString
ptr,Int
len) -> CString -> CSize -> IO a
f CString
ptr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))

asBytes :: ByteString -> (Ptr byte -> IO a) -> IO a
asBytes :: forall byte a. ByteString -> (Ptr byte -> IO a) -> IO a
asBytes ByteString
bs Ptr byte -> IO a
f = ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
forall byte a. ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen ByteString
bs (\ Ptr byte
ptr CSize
_ -> Ptr byte -> IO a
f Ptr byte
ptr)

unsafeAsBytes :: ByteString -> (Ptr byte -> IO a) -> IO a
unsafeAsBytes :: forall byte a. ByteString -> (Ptr byte -> IO a) -> IO a
unsafeAsBytes ByteString
bs Ptr byte -> IO a
f = ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
forall byte a. ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
unsafeAsBytesLen ByteString
bs (\ Ptr byte
ptr CSize
_ -> Ptr byte -> IO a
f Ptr byte
ptr)

-- WARNING: This should not be using `useAsCStringLen`
asBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen :: forall byte a. ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen ByteString
bs Ptr byte -> CSize -> IO a
f = ByteString -> (CStringLen -> IO a) -> IO a
forall a. ByteString -> (CStringLen -> IO a) -> IO a
ByteString.useAsCStringLen ByteString
bs (\ (CString
ptr,Int
len) -> Ptr byte -> CSize -> IO a
f (CString -> Ptr byte
forall a b. Ptr a -> Ptr b
castPtr CString
ptr) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))

unsafeAsBytesLen :: ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
unsafeAsBytesLen :: forall byte a. ByteString -> (Ptr byte -> CSize -> IO a) -> IO a
unsafeAsBytesLen ByteString
bs Ptr byte -> CSize -> IO a
f = ByteString -> (CStringLen -> IO a) -> IO a
forall a. ByteString -> (CStringLen -> IO a) -> IO a
ByteString.unsafeUseAsCStringLen ByteString
bs (\ (CString
ptr,Int
len) -> Ptr byte -> CSize -> IO a
f (CString -> Ptr byte
forall a b. Ptr a -> Ptr b
castPtr CString
ptr) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))

showBytes :: (Show a) => a -> ByteString
showBytes :: forall a. Show a => a -> ByteString
showBytes = String -> ByteString
Char8.pack (String -> ByteString) -> (a -> String) -> a -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show