packages feed

large-hashable-0.1.1.0: benchmark/Data/LargeHashable/Benchmarks/Text.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.LargeHashable.Benchmarks.Text (
    setup,
    benchTextUtf8,
    benchTextUtf16,
    benchTextUnicode,
    benchTextString
) where

import Data.Char (ord)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Foreign as TF
import qualified Data.Text.IO as T
import qualified Data.ByteString as BS
import Data.LargeHashable
import Data.LargeHashable.Intern
import Data.Word
import GHC.Ptr

setup :: IO (T.Text, String)
setup = do
#if MIN_VERSION_text(2,0,0)
  text <- T.readFile "test/bigfile.txt"
  let string = T.unpack text
  return (length string `seq` (text, string))
#else
  fail "benchmark for text only available with text >= 2"
#endif

-- Hash the native UTF8 encoding
benchTextUtf8 :: T.Text -> MD5Hash
benchTextUtf8 !t = runLH md5HashAlgorithm go
  where
    go = do
      updates <- hashUpdates
      ioInLH $ do
        TF.useAsPtr t $ \(valPtr :: Ptr Word8) (units :: TF.I8) ->
            hu_updatePtr updates (castPtr valPtr) (fromIntegral units)
        hu_updateULong updates (fromIntegral (T.length t))

-- Hash the UTF16 encoding for backwards compat with text-1.*
benchTextUtf16 :: T.Text -> MD5Hash
benchTextUtf16 !t =
  let b = TE.encodeUtf16BE t -- encoding of text-1.* (at least on mac with big endian)
  in runLH md5HashAlgorithm (go b)
  where
    go b = do
      updates <- hashUpdates
      ioInLH $ do
        ptr <- BS.useAsCString b return
        hu_updatePtr updates (castPtr ptr) (BS.length b)
        hu_updateULong updates (fromIntegral (T.length t))

benchTextUnicode :: T.Text -> MD5Hash
benchTextUnicode !t = runLH md5HashAlgorithm go
  where
    go = do
      updates <- hashUpdates
      ioInLH $ do
        hashStringNoLength updates (T.unpack t)
        hu_updateULong updates (fromIntegral (T.length t))

hashStringNoLength :: HashUpdates -> String -> IO ()
hashStringNoLength updates = loop
  where
    loop :: [Char] -> IO ()
    loop [] = pure ()
    loop (c:cs) = do
       _ <- hu_updateUInt updates (c2w c)
       loop cs

benchTextString:: String -> MD5Hash
benchTextString s = runLH md5HashAlgorithm $ do
  updates <- hashUpdates
  ioInLH $ hashString updates s

hashString :: HashUpdates -> String -> IO ()
hashString updates = loop 0
  where
    loop :: Int -> [Char] -> IO ()
    loop !n [] = hu_updateULong updates (fromIntegral n)
    loop !n (c:cs) = do
       _ <- hu_updateUInt updates (c2w c)
       loop (n+1) cs

{-# INLINE c2w #-}
c2w :: Char -> Word32
c2w c = toEnum (ord c :: Int)