I'm starting to learn how to meld the worlds of C and Haskell. Looking for any feedback on this first function.
The function takes in a pointer to an array of unsigned chars and returns a pointer to 32 unsigned shorts. Or at least I would like it too :).
I am unsure about when the memory used to return the result is cleaned up. Is it possible that it will be garbage collected before I can use it on the C side?
Anyway here's the code.
{-# LANGUAGE ForeignFunctionInterface #-}
module JSFLPlugin where
import Foreign.C.Types (CInt(..))
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Ptr (Ptr)
import Data.Serialize (encode)
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import Data.Digest.Pure.MD5 (md5)
import Data.ByteString (unpack)
import Data.ByteString.Internal (toForeignPtr, fromForeignPtr)
import Data.ByteString.Lazy.Internal (chunk)
import Data.ByteString.Lazy (empty)
import Data.Word (Word8, Word16)
import Data.Text.Format (hex)
import Control.Applicative ((<$>))
import Data.Text.Lazy.Builder (toLazyText)
import Data.Text.Lazy (toStrict)
import Data.Text.Foreign (asForeignPtr)
import Data.Monoid ((<>), mempty)
-- | Hash pointer to array of unsigned chars
hash :: CInt -> Ptr Word8 -> IO (Ptr Word16)
hash count addr = do
-- cast to ForeignPtr
fptr <- newForeignPtr_ addr
-- Make a strict ByteString
let sbyte = fromForeignPtr fptr 0 (fromIntegral count)
-- Make a lazy ByteString from the strict one
lbyte = chunk sbyte empty
-- Hash with md5 and encode as a strict ByteString
digest = encode . md5 $ lbyte
-- Convert the each digest byte to a UTF-16 encoded hexdecimal character
hexBytes = toLazyText . foldr (\x y -> y <> hex x) mempty . unpack $ digest
-- Convert to a strict Text and get the pointer to chars
fmap (unsafeForeignPtrToPtr . fst) . asForeignPtr . toStrict $ hexBytes
foreign export ccall hash :: CInt -> Ptr Word8 -> IO (Ptr Word16)
EDIT: New version per Joey's suggestions (compiled but not tested).
{-# LANGUAGE ForeignFunctionInterface #-}
module JSFLPlugin where
import Foreign.C.Types (CInt(..))
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Ptr (Ptr)
import qualified Data.Serialize as Serialize
import Data.Digest.Pure.MD5 (MD5Digest)
import Data.ByteString (ByteString)
import Data.ByteString.Internal (fromForeignPtr)
import Data.Word (Word8, Word16)
import Data.Text (Text)
import Data.Text.Foreign (unsafeCopyToPtr)
import qualified Data.ByteString.Base16 as Base16
import Data.Text.Encoding (decodeUtf8)
import Crypto.Classes (hash')
pureHash :: ByteString -> Text
pureHash = decodeUtf8 . Base16.encode . Serialize.encode . md5 where
md5 s = hash' s :: MD5Digest
hash :: CInt -> Ptr Word8 -> Ptr Word16 -> IO ()
hash count input output = do
fptr <- newForeignPtr_ input
let sbyte = fromForeignPtr fptr 0 $ fromIntegral count
unsafeCopyToPtr (pureHash sbyte) output
foreign export ccall hash :: CInt -> Ptr Word8 -> Ptr Word16 -> IO ()