{-# LANGUAGE ForeignFunctionInterface #-}
-- |
-- Module      : Data.Digest.CityHash
-- Copyright   : (c) Austin Seipp 2011
-- License     : BSD3
-- 
-- Maintainer  : as@hacks.yi.org
-- Stability   : experimental
-- Portability : portable (FFI)
-- 
-- This module implements a binding to the CityHash family of hashing
-- functions. You can find more information here:
-- <http://code.google.com/p/cityhash/>. It implements both the 64-bit
-- and 128-bit interfaces.
-- 
-- Note that CityHash was designed to work on architectures where
-- unaligned reads do not have a large penalty. In practice, it is
-- only used at Google on little-endian Intel/AMD CPUs, and has not
-- been tested on big-endian architectures.
-- 
module Data.Digest.CityHash
( -- * Hashing values 
  cityHash64  -- :: ByteString -> Word64
, cityHash128 -- :: ByteString -> Word128
) where
import Foreign
import Foreign.C
import Control.Monad (liftM)

import Data.LargeWord

import qualified Data.ByteString as S
import qualified Data.ByteString.Unsafe as U

-- | Hash a value into a 64bit result.
cityHash64 :: S.ByteString -> Word64
cityHash64 bs
  = unsafePerformIO . U.unsafeUseAsCStringLen bs $ \(cstr,clen) -> do
      return $ c_CityHash64 cstr clen
{-# INLINEABLE cityHash64 #-}

-- | Hash a value into a 128bit result. Per the documentation, this is
-- probably only faster for inputs with a length greater than
-- approximately 200 bytes. Alternatively, use this when you wish
-- to have minimal collisions.
cityHash128 :: S.ByteString -> Word128
cityHash128 bs
  = unsafePerformIO . U.unsafeUseAsCStringLen bs $ \(cstr,clen) ->
      allocaBytes w64s $ \lo -> 
        allocaBytes w64s $ \hi -> do
          c_CityHash128 cstr clen lo hi
          lo' <- fromIntegral `liftM` peek lo
          hi' <- fromIntegral `liftM` peek hi
          return $ (hi' `shiftL` 64) .|. lo'
  where w64s = sizeOf (undefined :: Word64)
{-# INLINEABLE cityHash128 #-}

--
-- FFI bindings
-- 

foreign import ccall unsafe "hs_city.h hs_CityHash64"
  c_CityHash64 :: CString -> Int -> Word64

foreign import ccall unsafe "hs_city.h hs_CityHash128"
  c_CityHash128 :: CString -> Int -> Ptr Word64 -> Ptr Word64 -> IO ()