packages feed

hashes-0.2.0.0: src/Data/Hash/Class/Pure/Internal.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

-- |
-- Module: Data.Hash.Class.Pure.Internal
-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
-- License: MIT
-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
-- Stability: experimental
--
-- Incremental Pure Hashes
--
module Data.Hash.Class.Pure.Internal
( IncrementalHash(..)
, updateByteString
, updateByteStringLazy
, updateShortByteString
, updateStorable
, updateByteArray
) where

import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Short as BS
import qualified Data.ByteString.Unsafe as B
import Data.Kind
import Data.Word

import Foreign.Marshal.Alloc
import Foreign.Marshal.Utils
import Foreign.Ptr
import Foreign.Storable

import GHC.Exts
import GHC.IO

-- -------------------------------------------------------------------------- --
-- Incremental Pure Hashes

class IncrementalHash a where
    type Context a :: Type
    update :: Context a -> Ptr Word8 -> Int -> IO (Context a)
    finalize :: Context a -> a

updateByteString :: forall a . IncrementalHash a => Context a -> B.ByteString -> Context a
updateByteString !ctx !b = unsafeDupablePerformIO $!
    B.unsafeUseAsCStringLen b $ \(!p, !l) -> update @a ctx (castPtr p) l
{-# INLINE updateByteString #-}

updateByteStringLazy
    :: forall a
    . IncrementalHash a
    => Context a
    -> BL.ByteString
    -> Context a
updateByteStringLazy = BL.foldlChunks (updateByteString @a)
{-# INLINE updateByteStringLazy #-}

updateShortByteString
    :: forall a
    . IncrementalHash a
    => Context a
    -> BS.ShortByteString
    -> Context a
updateShortByteString !ctx b = unsafeDupablePerformIO $!
    BS.useAsCStringLen b $ \(!p, !l) -> update @a ctx (castPtr p) l
{-# INLINE updateShortByteString #-}

updateStorable
    :: forall a b
    . IncrementalHash a
    => Storable b
    => Context a
    -> b
    -> Context a
updateStorable !ctx b = unsafeDupablePerformIO $!
    with b $ \p -> update @a ctx (castPtr p) (sizeOf b)
{-# INLINE updateStorable #-}

updateByteArray
    :: forall a
    . IncrementalHash a
    => Context a
    -> ByteArray#
    -> Context a
updateByteArray ctx a# = unsafeDupablePerformIO $!
    case isByteArrayPinned# a# of
        -- Pinned ByteArray
        1# -> update @a ctx (Ptr (byteArrayContents# a#)) (I# size#)

        -- Unpinned ByteArray, copy content to newly allocated pinned ByteArray
        _ -> allocaBytes (I# size#) $ \ptr@(Ptr addr#) -> IO $ \s0 ->
            case copyByteArrayToAddr# a# 0# addr# size# s0 of
                s1 -> case update @a ctx ptr (I# size#) of
                    IO run -> run s1
  where
    size# = sizeofByteArray# a#
{-# INLINE updateByteArray #-}