packages feed

mini-1.7.0.0: src/Mini/Hash/Class.hs

{-# LANGUAGE FunctionalDependencies #-}

-- | Hash functions and hashable types
module Mini.Hash.Class (
  -- * Classes
  Hash (
    hash,
    digest
  ),
  Hashable (
    toBytes
  ),
) where

import Data.Bits (
  FiniteBits,
  finiteBitSize,
  shiftR,
 )
import Data.Int (
  Int,
  Int16,
  Int32,
  Int64,
  Int8,
 )
import Data.Word (
  Word,
  Word16,
  Word32,
  Word64,
  Word8,
 )
import Prelude (
  Bool,
  Char,
  Enum,
  Integral,
  concatMap,
  div,
  fmap,
  fromEnum,
  fromIntegral,
  iterate,
  pure,
  take,
  ($),
  (.),
 )

-- Classes

-- | The class of hash functions
class Hash h s d | h -> s d where
  -- | Make a hash value from a hashable value and a seed
  hash :: (Hashable a) => a -> s -> h
  -- | Extract the digest of a hash value
  digest :: h -> d

-- | Instances should use little-endian byte order
class Hashable a where
  -- | Convert a hashable type to a sequence of bytes
  toBytes :: a -> [Word8]

instance Hashable Bool where
  toBytes = enumToBytes

instance Hashable Char where
  toBytes = enumToBytes

instance Hashable Int where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Int8 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Int16 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Int32 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Int64 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Word where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Word8 where
  toBytes = pure

instance Hashable Word16 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Word32 where
  toBytes = finiteBitsIntegralToBytes

instance Hashable Word64 where
  toBytes = finiteBitsIntegralToBytes

instance (Hashable a) => Hashable [a] where
  toBytes = concatMap toBytes

-- Helpers

enumToBytes :: (Enum a) => a -> [Word8]
enumToBytes = pure . fromIntegral . fromEnum

finiteBitsIntegralToBytes :: (FiniteBits a, Integral a) => a -> [Word8]
finiteBitsIntegralToBytes w =
  take (finiteBitSize w `div` 8)
    . fmap fromIntegral
    $ iterate (`shiftR` 8) w