{-# LANGUAGE Unsafe #-}

module Data.Hash.SL2.Unsafe (unsafeUseAsPtr, unsafeUseAsPtr2, unsafeWithNew, unsafePack, unsafeUnpack) where

import Foreign
import System.IO.Unsafe

import Data.Hash.SL2.Internal (Hash(H), hashSize)

instance Storable Hash where
  sizeOf = const hashSize
  alignment = const 16
  peek p = fmap fst $ unsafeWithNew $ \hp -> copyBytes hp (castPtr p) hashSize
  poke p h = unsafeUseAsPtr h $ \hp -> copyBytes (castPtr p) hp hashSize

unsafeUseAsPtr :: Hash -> (Ptr Hash -> IO a) -> IO a
{-# INLINE unsafeUseAsPtr #-}
unsafeUseAsPtr (H fp) f = withForeignPtr fp (f . castPtr)

unsafeUseAsPtr2 :: Hash -> Hash -> (Ptr Hash -> Ptr Hash -> IO a) -> IO a
{-# INLINE unsafeUseAsPtr2 #-}
unsafeUseAsPtr2 a b f = unsafeUseAsPtr a (unsafeUseAsPtr b . f)

unsafeWithNew :: (Ptr Hash -> IO a) -> IO (Hash, a)
{-# INLINE unsafeWithNew #-}
unsafeWithNew f = mallocForeignPtr >>= \fp -> (\r -> (H (castForeignPtr fp), r)) `fmap` withForeignPtr fp f

unsafePack :: Storable a => [a] -> Hash
unsafePack as@(a:_) = H $ unsafePerformIO $ do
  let len = hashSize `div` sizeOf a
  fp <- mallocForeignPtrArray0 len
  withForeignPtr fp $ \p ->
    mapM_ (\(a, off) -> pokeElemOff p off a) (zip as [0..len-1])
  return (castForeignPtr fp)

unsafeUnpack :: Storable a => Hash -> [a]
unsafeUnpack h = unsafePerformIO $ unsafeUseAsPtr h $ rec 0
  where rec off _ | off >= hashSize = return []
        rec off p = do
          a <- peekByteOff (castPtr p) off
          r <- rec (off + sizeOf a) p
          return (a : r)