packages feed

hashes-0.3.0.1: test/Test/Data/Hash/Class/Pure.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}

-- |
-- Module: Test.Data.Hash.Class.Pure
-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
-- License: MIT
-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
-- Stability: experimental
--
module Test.Data.Hash.Class.Pure
( tests
, run
) where

import Data.Bits
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 Foreign.Marshal
import Foreign.Ptr

import GHC.Exts
import GHC.IO
import GHC.Word

import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

-- internal modules

import Data.Hash.Class.Pure.Salted

-- -------------------------------------------------------------------------- --
--

tests :: Spec
tests = do
    prop "prop_hashByteString" prop_hashByteString
    prop "prop_hashByteStringLazy" prop_hashByteStringLazy
    prop "prop_hashShortByteString" prop_hashShortByteString
    prop "prop_hashStorable" prop_hashStorable
    prop "prop_hashPtr" prop_hashPtr
    prop "prop_hashByteArray" prop_hashByteArray

run :: IO ()
run = do
    putStrLn "prop_hashByteString"
    quickCheck prop_hashByteString
    putStrLn "prop_hashByteStringLazy"
    quickCheck prop_hashByteStringLazy
    putStrLn "prop_hashShortByteString"
    quickCheck prop_hashShortByteString
    putStrLn "prop_hashStorable"
    quickCheck prop_hashStorable
    putStrLn "prop_hashPtr"
    quickCheck prop_hashPtr
    putStrLn "prop_hashByteArray"
    quickCheck prop_hashByteArray

word8sToWord64 :: [Word8] -> Word64
word8sToWord64 = foldr (\b c -> fromIntegral b + shiftL c 8) 0

newtype TestHash = TestHash { _getTestHash :: [Word8] }
    deriving (Eq, Ord, Show)

instance IncrementalHash TestHash where
    type Context TestHash = [Word8]
    update ctx p l = (ctx ++) <$> peekArray l p
    finalize = TestHash

instance Hash TestHash where
    type Salt TestHash = ()
    initialize _ = []

prop_hashStorable :: Word64 -> Property
prop_hashStorable b = word8sToWord64 (_getTestHash $ hashStorable @TestHash () b) === b

prop_hashPtr :: [Word8] -> Property
prop_hashPtr b = ioProperty $
    B.unsafeUseAsCStringLen (B.pack b) $ \(!ptr, !len) -> do
        !r <- hashPtr @TestHash () (castPtr ptr) len
        return $! r === TestHash b

prop_hashByteString :: [Word8] -> Property
prop_hashByteString b = hashByteString @TestHash () (B.pack b) === TestHash b

prop_hashByteStringLazy :: [Word8] -> Property
prop_hashByteStringLazy b = hashByteStringLazy @TestHash () (BL.pack b) === TestHash b

prop_hashShortByteString :: [Word8] -> Property
prop_hashShortByteString b = hashShortByteString @TestHash () (BS.pack b) === TestHash b

prop_hashByteArray :: [Word8] -> Property
prop_hashByteArray bytes = unsafeDupablePerformIO $ IO $ \s0 ->
    case newPinnedByteArray# size s0 of
        (# s1, a# #) ->
            case copyToArray 0# bytes a# s1 of
                s2 -> case unsafeFreezeByteArray# a# s2 of
                    (# s3, b# #) ->
                        let r = hashByteArray @TestHash () b# === TestHash bytes
                        in (# s3, r #)
  where
    !(I# size) = length bytes

    copyToArray _ [] _ s = s
    copyToArray i ((W8# h):t) a s = case writeWord8Array# a i h s of
        s' -> copyToArray (i +# 1#) t a s'