packages feed

hashes-0.2.2.1: src/Data/Hash/FNV1.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}

-- |
-- Module: Data.Hash.FNV1
-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
-- License: MIT
-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
-- Stability: experimental
--
-- The primitive versions are usually not more efficient than the version with
-- explicit word sizes for the respective host architecture.
--
module Data.Hash.FNV1
(
-- * Fnv1 64 bit
  Fnv164Hash(..)
, Fnv164Context
, fnv164Initialize
, fnv164Update
, fnv164Finalize
, fnv164


-- * Fnv1a 64 bit
, Fnv1a64Hash(..)
, Fnv1a64Context
, fnv1a64Initialize
, fnv1a64Update
, fnv1a64Finalize
, fnv1a64

-- * Fnv1 32 bit
, Fnv132Hash(..)
, Fnv132Context
, fnv132Initialize
, fnv132Update
, fnv132Finalize
, fnv132

-- * Fnv1a 32 bit
, Fnv1a32Hash(..)
, Fnv1a32Context
, fnv1a32Initialize
, fnv1a32Update
, fnv1a32Finalize
, fnv1a32

-- * Fnv1 Host Wordsize
, Fnv1Hash(..)
, Fnv1Context
, fnv1Initialize
, fnv1Update
, fnv1Finalize
, fnv1

-- * Fnv1a Host Wordsize
, Fnv1aHash(..)
, Fnv1aContext
, fnv1aInitialize
, fnv1aUpdate
, fnv1aFinalize
, fnv1a

-- * Utils
, module Data.Hash.Class.Pure

-- * Low-Level
-- ** 64 bit

, fnv1_64
, fnv1_64_
, fnv1a_64
, fnv1a_64_

-- ** 32 bit
, fnv1_32
, fnv1_32_
, fnv1a_32
, fnv1a_32_

-- ** Host word size
, fnv1_host
, fnv1_host_
, fnv1Primitive
, fnv1Primitive_

, fnv1a_host
, fnv1a_host_
, fnv1aPrimitive
, fnv1aPrimitive_

-- ** Internal Constants
, fnvPrime
, fnvPrime32
, fnvPrime64

, fnvOffsetBasis
, fnvOffsetBasis32
, fnvOffsetBasis64

) where

import Control.Monad

import Data.Bits
import Data.Word

import Foreign.Ptr
import Foreign.Storable

import GHC.Exts
import GHC.IO

-- internal modules

import Data.Hash.Class.Pure

-- -------------------------------------------------------------------------- --
-- Fnv1 64 bit

newtype Fnv164Context = Fnv164Context Word64

newtype Fnv164Hash = Fnv164Hash Word64
    deriving (Show, Eq, Ord)

fnv164Initialize :: Fnv164Context
fnv164Initialize = Fnv164Context fnvOffsetBasis64
{-# INLINE fnv164Initialize #-}

fnv164Update :: Fnv164Context -> Ptr Word8 -> Int -> IO Fnv164Context
fnv164Update (Fnv164Context !ctx) !ptr !n =
    Fnv164Context <$!> fnv1_64_ ptr n ctx
{-# INLINE fnv164Update #-}

fnv164Finalize :: Fnv164Context -> Fnv164Hash
fnv164Finalize (Fnv164Context !ctx) = Fnv164Hash ctx
{-# INLINE fnv164Finalize #-}

fnv164 :: Ptr Word8 -> Int -> IO Fnv164Hash
fnv164 !ptr !n = fnv164Finalize <$!> fnv164Update fnv164Initialize ptr n
{-# INLINE fnv164 #-}

instance IncrementalHash Fnv164Hash where
    type Context Fnv164Hash = Fnv164Context
    update = fnv164Update
    finalize = fnv164Finalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv164Hash where
    initialize = fnv164Initialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Fnv1a 64 bit

newtype Fnv1a64Context = Fnv1a64Context Word64

newtype Fnv1a64Hash = Fnv1a64Hash Word64
    deriving (Show, Eq, Ord)

fnv1a64Initialize :: Fnv1a64Context
fnv1a64Initialize = Fnv1a64Context fnvOffsetBasis64
{-# INLINE fnv1a64Initialize #-}

fnv1a64Update :: Fnv1a64Context -> Ptr Word8 -> Int -> IO Fnv1a64Context
fnv1a64Update (Fnv1a64Context !ctx) !ptr !n =
    Fnv1a64Context <$!> fnv1a_64_ ptr n ctx
{-# INLINE fnv1a64Update #-}

fnv1a64Finalize :: Fnv1a64Context -> Fnv1a64Hash
fnv1a64Finalize (Fnv1a64Context !ctx) = Fnv1a64Hash ctx
{-# INLINE fnv1a64Finalize #-}

fnv1a64 :: Ptr Word8 -> Int -> IO Fnv1a64Hash
fnv1a64 !ptr !n = fnv1a64Finalize <$!> fnv1a64Update fnv1a64Initialize ptr n
{-# INLINE fnv1a64 #-}

instance IncrementalHash Fnv1a64Hash where
    type Context Fnv1a64Hash = Fnv1a64Context
    update = fnv1a64Update
    finalize = fnv1a64Finalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv1a64Hash where
    initialize = fnv1a64Initialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Fnv1 32 bit

newtype Fnv132Context = Fnv132Context Word32

newtype Fnv132Hash = Fnv132Hash Word32
    deriving (Show, Eq, Ord)

fnv132Initialize :: Fnv132Context
fnv132Initialize = Fnv132Context fnvOffsetBasis32
{-# INLINE fnv132Initialize #-}

fnv132Update :: Fnv132Context -> Ptr Word8 -> Int -> IO Fnv132Context
fnv132Update (Fnv132Context !ctx) !ptr !n =
    Fnv132Context <$!> fnv1_32_ ptr n ctx
{-# INLINE fnv132Update #-}

fnv132Finalize :: Fnv132Context -> Fnv132Hash
fnv132Finalize (Fnv132Context !ctx) = Fnv132Hash ctx
{-# INLINE fnv132Finalize #-}

fnv132 :: Ptr Word8 -> Int -> IO Fnv132Hash
fnv132 !ptr !n = fnv132Finalize <$!> fnv132Update fnv132Initialize ptr n
{-# INLINE fnv132 #-}

instance IncrementalHash Fnv132Hash where
    type Context Fnv132Hash = Fnv132Context
    update = fnv132Update
    finalize = fnv132Finalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv132Hash where
    initialize = fnv132Initialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Fnv1a 32 bit

newtype Fnv1a32Context = Fnv1a32Context Word32

newtype Fnv1a32Hash = Fnv1a32Hash Word32
    deriving (Show, Eq, Ord)

fnv1a32Initialize :: Fnv1a32Context
fnv1a32Initialize = Fnv1a32Context fnvOffsetBasis32
{-# INLINE fnv1a32Initialize #-}

fnv1a32Update :: Fnv1a32Context -> Ptr Word8 -> Int -> IO Fnv1a32Context
fnv1a32Update (Fnv1a32Context !ctx) !ptr !n =
    Fnv1a32Context <$!> fnv1a_32_ ptr n ctx
{-# INLINE fnv1a32Update #-}

fnv1a32Finalize :: Fnv1a32Context -> Fnv1a32Hash
fnv1a32Finalize (Fnv1a32Context !ctx) = Fnv1a32Hash ctx
{-# INLINE fnv1a32Finalize #-}

fnv1a32 :: Ptr Word8 -> Int -> IO Fnv1a32Hash
fnv1a32 !ptr !n = fnv1a32Finalize <$!> fnv1a32Update fnv1a32Initialize ptr n
{-# INLINE fnv1a32 #-}

instance IncrementalHash Fnv1a32Hash where
    type Context Fnv1a32Hash = Fnv1a32Context
    update = fnv1a32Update
    finalize = fnv1a32Finalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv1a32Hash where
    initialize = fnv1a32Initialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Fnv1 Host Wordsize

newtype Fnv1Context = Fnv1Context Word

newtype Fnv1Hash = Fnv1Hash Word
    deriving (Show, Eq, Ord)

fnv1Initialize :: Fnv1Context
fnv1Initialize = Fnv1Context fnvOffsetBasis
{-# INLINE fnv1Initialize #-}

fnv1Update :: Fnv1Context -> Ptr Word8 -> Int -> IO Fnv1Context
fnv1Update (Fnv1Context !ctx) !ptr !n =
    Fnv1Context <$!> fnv1_host_ ptr n ctx
{-# INLINE fnv1Update #-}

fnv1Finalize :: Fnv1Context -> Fnv1Hash
fnv1Finalize (Fnv1Context !ctx) = Fnv1Hash ctx
{-# INLINE fnv1Finalize #-}

fnv1 :: Ptr Word8 -> Int -> IO Fnv1Hash
fnv1 !ptr !n = fnv1Finalize <$!> fnv1Update fnv1Initialize ptr n
{-# INLINE fnv1 #-}

instance IncrementalHash Fnv1Hash where
    type Context Fnv1Hash = Fnv1Context
    update = fnv1Update
    finalize = fnv1Finalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv1Hash where
    initialize = fnv1Initialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Fnv1a Host Wordsize

newtype Fnv1aContext = Fnv1aContext Word

newtype Fnv1aHash = Fnv1aHash Word
    deriving (Show, Eq, Ord)

fnv1aInitialize :: Fnv1aContext
fnv1aInitialize = Fnv1aContext fnvOffsetBasis
{-# INLINE fnv1aInitialize #-}

fnv1aUpdate :: Fnv1aContext -> Ptr Word8 -> Int -> IO Fnv1aContext
fnv1aUpdate (Fnv1aContext !ctx) !ptr !n =
    Fnv1aContext <$!> fnv1a_host_ ptr n ctx
{-# INLINE fnv1aUpdate #-}

fnv1aFinalize :: Fnv1aContext -> Fnv1aHash
fnv1aFinalize (Fnv1aContext !ctx) = Fnv1aHash ctx
{-# INLINE fnv1aFinalize #-}

fnv1a :: Ptr Word8 -> Int -> IO Fnv1aHash
fnv1a !ptr !n = fnv1aFinalize <$!> fnv1aUpdate fnv1aInitialize ptr n
{-# INLINE fnv1a #-}

instance IncrementalHash Fnv1aHash where
    type Context Fnv1aHash = Fnv1aContext
    update = fnv1aUpdate
    finalize = fnv1aFinalize

    {-# INLINE update #-}
    {-# INLINE finalize #-}

instance Hash Fnv1aHash where
    initialize = fnv1aInitialize
    {-# INLINE initialize #-}

-- -------------------------------------------------------------------------- --
-- Low Level
-- -------------------------------------------------------------------------- --

-- -------------------------------------------------------------------------- --
-- Constants

fnvPrime32 :: Word32
fnvPrime32 = 0x01000193
{-# INLINE fnvPrime32 #-}

fnvPrime64 :: Word64
fnvPrime64 = 0x100000001b3
{-# INLINE fnvPrime64 #-}

fnvOffsetBasis32 :: Word32
fnvOffsetBasis32 = 0x811c9dc5
{-# INLINE fnvOffsetBasis32 #-}

fnvOffsetBasis64 :: Word64
fnvOffsetBasis64 = 0xcbf29ce484222325
{-# INLINE fnvOffsetBasis64 #-}

fnvPrime :: Word
#if defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
fnvPrime = fromIntegral fnvPrime64
#elif defined(i386_HOST_ARCH)
fnvPrime = fromIntegral fvnPrime32
#else
fnvPrime = error "fnvPrime: unsupported hardware platform"
#endif
{-# INLINE fnvPrime #-}

fnvOffsetBasis :: Word
#if defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
fnvOffsetBasis = fromIntegral fnvOffsetBasis64
#elif defined(i386_HOST_ARCH)
fnvOffsetBasis = fromIntegral fnvOffsetBasis32
#else
fnvOffsetBasis = error "fnvOffsetBasis: unsupported hardware platform"
#endif
{-# INLINE fnvOffsetBasis #-}

-- -------------------------------------------------------------------------- --
-- FNV1 64 bit

fnv1_64 :: Ptr Word8 -> Int -> IO Word64
fnv1_64 !ptr !n = fnv1_64_ ptr n fnvOffsetBasis64
{-# INLINE fnv1_64 #-}

fnv1_64_ :: Ptr Word8 -> Int -> Word64 -> IO Word64
fnv1_64_ !ptr !n !a = loop a 0
  where
    loop !acc !i
        | i == n = return acc
        | otherwise = do
            !x <- peekByteOff @Word8 ptr i
            loop ((fnvPrime64 * acc) `xor` fromIntegral x) (i + 1)
{-# INLINE fnv1_64_ #-}

-- -------------------------------------------------------------------------- --
-- FNV1a 64 bit

fnv1a_64 :: Ptr Word8 -> Int -> IO Word64
fnv1a_64 !ptr !n = fnv1a_64_ ptr n fnvOffsetBasis64
{-# INLINE fnv1a_64 #-}

fnv1a_64_ :: Ptr Word8 -> Int -> Word64 -> IO Word64
fnv1a_64_ !ptr !n !a = loop a 0
  where
    loop !acc !i
        | i == n = return acc
        | otherwise = do
            !x <- peekByteOff @Word8 ptr i
            loop (fnvPrime64 * (acc `xor` fromIntegral x)) (i + 1)
{-# INLINE fnv1a_64_ #-}

-- -------------------------------------------------------------------------- --
-- FNV1 32 bit

fnv1_32 :: Ptr Word8 -> Int -> IO Word32
fnv1_32 !ptr !n = fnv1_32_ ptr n fnvOffsetBasis32
{-# INLINE fnv1_32 #-}

fnv1_32_ :: Ptr Word8 -> Int -> Word32 -> IO Word32
fnv1_32_ !ptr !n !a = loop a 0
  where
    loop !acc !i
        | i == n = return acc
        | otherwise = do
            !x <- peekByteOff @Word8 ptr i
            loop ((fnvPrime32 * acc) `xor` fromIntegral x) (i + 1)
{-# INLINE fnv1_32_ #-}

-- FNV1a 32 bit

fnv1a_32 :: Ptr Word8 -> Int -> IO Word32
fnv1a_32 !ptr !n = fnv1a_32_ ptr n fnvOffsetBasis32
{-# INLINE fnv1a_32 #-}

fnv1a_32_ :: Ptr Word8 -> Int -> Word32 -> IO Word32
fnv1a_32_ !ptr !n a = loop a 0
  where
    loop !acc !i
        | i == n = return acc
        | otherwise = do
            !x <- peekByteOff @Word8 ptr i
            loop (fnvPrime32 * (acc `xor` fromIntegral x)) (i + 1)
{-# INLINE fnv1a_32_ #-}

-- -------------------------------------------------------------------------- --
-- Host architecture words

-- FNV1

fnv1_host :: Ptr Word8 -> Int -> IO Word
fnv1_host (Ptr addr) (I# n) = IO $ \s -> case fnv1Primitive addr n s of
    (# s1, w #) -> (# s1, W# w #)
{-# INlINE fnv1_host #-}

fnv1_host_ :: Ptr Word8 -> Int -> Word -> IO Word
fnv1_host_ (Ptr addr) (I# n) (W# a) = IO $ \s -> case fnv1Primitive_ addr n a s of
    (# s1, w #) -> (# s1, W# w #)
{-# INlINE fnv1_host_ #-}

fnv1Primitive :: Addr# -> Int# -> State# tok -> (# State# tok, Word# #)
fnv1Primitive !addr !n !tok = fnv1Primitive_ addr n o tok
  where
    !(W# o) = fnvOffsetBasis
{-# INLINE fnv1Primitive #-}

fnv1Primitive_ :: Addr# -> Int# -> Word# -> State# tok -> (# State# tok, Word# #)
fnv1Primitive_ !addr !n !a tok = case loop a 0# tok of
    (# tok1, w #) -> (# tok1, w #)
  where
    loop !acc !i !s = case i ==# n of
        1# -> (# s, acc #)
        _ -> case readWord8OffAddr# addr i s of
            (# s1, w #) -> loop
                ((p `timesWord#` acc) `xor#` word8ToWord# w)
                (i +# 1#)
                s1

    !(W# p) = fnvPrime
{-# INLINE fnv1Primitive_ #-}

-- -------------------------------------------------------------------------- --
-- Host Wordsize FNV1a

fnv1a_host :: Ptr Word8 -> Int -> IO Word
fnv1a_host (Ptr addr) (I# n) = IO $ \s -> case fnv1aPrimitive addr n s of
    (# s1, w #) -> (# s1, W# w #)
{-# INlINE fnv1a_host #-}

fnv1a_host_ :: Ptr Word8 -> Int -> Word -> IO Word
fnv1a_host_ (Ptr addr) (I# n) (W# a) = IO $ \s -> case fnv1aPrimitive_ addr n a s of
    (# s1, w #) -> (# s1, W# w #)
{-# INlINE fnv1a_host_ #-}

fnv1aPrimitive :: Addr# -> Int# -> State# tok -> (# State# tok, Word# #)
fnv1aPrimitive !addr !n !tok = fnv1aPrimitive_ addr n o tok
  where
    !(W# o) = fnvOffsetBasis
{-# INLINE fnv1aPrimitive #-}

fnv1aPrimitive_ :: Addr# -> Int# -> Word# -> State# tok -> (# State# tok, Word# #)
fnv1aPrimitive_ !addr !n !a tok = case loop a 0# tok of
    (# tok1, w #) -> (# tok1, w #)
  where
    loop !acc !i !s = case i ==# n of
        1# -> (# s, acc #)
        _ -> case readWord8OffAddr# addr i s of
            (# s1, w #) ->
                let !acc' = p `timesWord#` (acc `xor#` word8ToWord# w)
                    !n' = i +# 1#
                in loop acc' n' s1

    !(W# p) = fnvPrime
{-# INLINE fnv1aPrimitive_ #-}

-- -------------------------------------------------------------------------- --
-- Backward compatibility

#if !MIN_VERSION_base(4,16,0)
-- | 'readWord8OffAddr#' returns 'Word#' for base < 4.16.0. So, there's no
-- need to convert it to 'Word#' down the road.
--
word8ToWord# :: Word# -> Word#
word8ToWord# a = a
{-# INLINE word8ToWord# #-}
#endif