Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No long in C-FFI #213

Merged
merged 3 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
See also https://pvp.haskell.org/faq

## Version 1.3.3.0

* `Text` hashing uses 64-bit FNV prime
* Don't truncate Text hashvalues on 64bit Windows:
https://github.com/haskell-unordered-containers/hashable/pull/211

## Version 1.3.2.0

* Add `Hashable (Fixed a)` for `base <4.7` versions.
Expand Down
16 changes: 5 additions & 11 deletions cbits/fnv.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "MachDeps.h"

#if WORD_SIZE_IN_BITS == 64
#define FNV_PRIME 1099511628211
#else
#define FNV_PRIME 16777619
#endif
#include "HsHashable.h"

/* FNV-1 hash
*
* The FNV-1 hash description: http://isthe.com/chongo/tech/comp/fnv/
* The FNV-1 hash is public domain: http://isthe.com/chongo/tech/comp/fnv/#public_domain
*/
long hashable_fnv_hash(const unsigned char* str, long len, long salt) {
FNV_UNSIGNED hashable_fnv_hash(const unsigned char* str, FNV_SIGNED len, FNV_UNSIGNED salt) {

unsigned long hash = salt;
FNV_UNSIGNED hash = salt;
while (len--) {
hash = (hash * 16777619) ^ *str++;
hash = (hash * FNV_PRIME) ^ *str++;
}

return hash;
Expand All @@ -57,6 +51,6 @@ long hashable_fnv_hash(const unsigned char* str, long len, long salt) {
/* Used for ByteArray#s. We can't treat them like pointers in
native Haskell, but we can in unsafe FFI calls.
*/
long hashable_fnv_hash_offset(const unsigned char* str, long offset, long len, long salt) {
FNV_UNSIGNED hashable_fnv_hash_offset(const unsigned char* str, FNV_SIGNED offset, FNV_SIGNED len, FNV_UNSIGNED salt) {
return hashable_fnv_hash(str + offset, len, salt);
}
9 changes: 6 additions & 3 deletions hashable-bench/hashable-bench.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ tested-with:
|| ==8.10.4
|| ==9.0.1

extra-source-files: benchmarks/cbits/*.h
extra-source-files:
benchmarks/cbits/*.h
include/HsHashable.h

flag integer-gmp
description:
Expand Down Expand Up @@ -61,6 +63,7 @@ library
Data.Hashable.Generic.Instances

c-sources: cbits/fnv.c
include-dirs: include
hs-source-dirs: src
build-depends:
base >=4.5 && <4.16
Expand Down Expand Up @@ -113,12 +116,12 @@ benchmark hashable-benchmark
, siphash
, text

if impl(ghc -any)
if impl(ghc)
build-depends:
ghc-prim
, text >=0.11.0.5

if (impl(ghc -any) && flag(integer-gmp))
if (impl(ghc) && flag(integer-gmp))
build-depends: integer-gmp >=0.2

if impl(ghc >=7.2.1)
Expand Down
1 change: 1 addition & 0 deletions hashable-bench/include
5 changes: 3 additions & 2 deletions hashable.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
cabal-version: 1.12
name: hashable
version: 1.3.2.0
x-revision: 1
version: 1.3.3.0
synopsis: A class for types that can be converted to a hash value
description:
This package defines a class, 'Hashable', for types that
Expand Down Expand Up @@ -43,6 +42,7 @@ tested-with:
extra-source-files:
CHANGES.md
README.md
include/HsHashable.h

flag integer-gmp
description:
Expand Down Expand Up @@ -71,6 +71,7 @@ library
Data.Hashable.Generic.Instances

c-sources: cbits/fnv.c
include-dirs: include
hs-source-dirs: src
build-depends:
base >=4.5 && <4.17
Expand Down
22 changes: 22 additions & 0 deletions include/HsHashable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef HS_HASHABLE_H
#define HS_HASHABLE_H

#include "MachDeps.h"
#include <stdint.h>

#if WORD_SIZE_IN_BITS == 64
#define FNV_PRIME 1099511628211
#define FNV_SIGNED int64_t
#define FNV_UNSIGNED uint64_t
#else
#define FNV_PRIME 16777619
#define FNV_SIGNED int32_t
#define FNV_UNSIGNED uint32_t
#endif

uint64_t hs_hashable_init();

FNV_UNSIGNED hashable_fnv_hash(const unsigned char* str, FNV_SIGNED len, FNV_UNSIGNED salt);
FNV_UNSIGNED hashable_fnv_hash_offset(const unsigned char* str, FNV_SIGNED offset, FNV_SIGNED len, FNV_UNSIGNED salt);

#endif
29 changes: 18 additions & 11 deletions src/Data/Hashable/Class.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{-# LANGUAGE BangPatterns, CPP, MagicHash,
ScopedTypeVariables, UnliftedFFITypes, DeriveDataTypeable,
DefaultSignatures, FlexibleContexts, TypeFamilies,
MultiParamTypeClasses #-}
MultiParamTypeClasses, CApiFFI #-}

{-# LANGUAGE Trustworthy #-}

Expand Down Expand Up @@ -121,13 +121,7 @@ import Data.Typeable.Internal (Typeable, TypeRep (..))
import GHC.Fingerprint.Type(Fingerprint(..))
#endif

#if MIN_VERSION_base(4,5,0)
import Foreign.C (CLong(..))
import Foreign.C.Types (CInt(..))
#else
import Foreign.C (CLong)
import Foreign.C.Types (CInt)
#endif

#if !(MIN_VERSION_base(4,8,0))
import Data.Word (Word)
Expand Down Expand Up @@ -210,7 +204,7 @@ initialSeed :: Word64
initialSeed = unsafePerformIO initialSeedC
{-# NOINLINE initialSeed #-}

foreign import ccall "hs_hashable_init" initialSeedC :: IO Word64
foreign import capi "HsHashable.h hs_hashable_init" initialSeedC :: IO Word64
#endif

-- | A default salt used in the implementation of 'hash'.
Expand Down Expand Up @@ -723,6 +717,7 @@ instance Hashable TL.Text where
hashThreadId :: ThreadId -> Int
hashThreadId (ThreadId t) = hash (fromIntegral (getThreadId t) :: Int)

-- this cannot be capi, as GHC panics.
foreign import ccall unsafe "rts_getThreadId" getThreadId
:: ThreadId# -> CInt

Expand Down Expand Up @@ -816,8 +811,12 @@ hashPtrWithSalt p len salt =
fromIntegral `fmap` c_hashCString (castPtr p) (fromIntegral len)
(fromIntegral salt)

foreign import ccall unsafe "hashable_fnv_hash" c_hashCString
:: CString -> CLong -> CLong -> IO CLong
foreign import capi unsafe "HsHashable.h hashable_fnv_hash" c_hashCString
#if WORD_SIZE_IN_BITS == 64
:: CString -> Int64 -> Int64 -> IO Word64
#else
:: CString -> Int32 -> Int32 -> IO Word32
#endif

-- | Compute a hash value for the content of this 'ByteArray#',
-- beginning at the specified offset, using specified number of bytes.
Expand All @@ -844,8 +843,16 @@ hashByteArrayWithSalt ba !off !len !h =
fromIntegral $ c_hashByteArray ba (fromIntegral off) (fromIntegral len)
(fromIntegral h)

#if __GLASGOW_HASKELL__ >= 802
foreign import capi unsafe "HsHashable.h hashable_fnv_hash_offset" c_hashByteArray
#else
foreign import ccall unsafe "hashable_fnv_hash_offset" c_hashByteArray
:: ByteArray# -> CLong -> CLong -> CLong -> CLong
#endif
#if WORD_SIZE_IN_BITS == 64
:: ByteArray# -> Int64 -> Int64 -> Int64 -> Word64
#else
:: ByteArray# -> Int32 -> Int32 -> Int32 -> Word32
#endif

-- | Combine two given hash values. 'combine' has zero as a left
-- identity.
Expand Down
8 changes: 8 additions & 0 deletions tests/Regress.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

module Regress (regressions) where

Expand All @@ -9,13 +10,16 @@ import Test.HUnit ((@=?))
import GHC.Generics (Generic)
import Data.List (nub)
import Data.Fixed (Pico)
import Data.Text (Text)

#ifdef HAVE_MMAP
import qualified Regress.Mmap as Mmap
#endif

import Data.Hashable

#include "MachDeps.h"

regressions :: [F.Test]
regressions = [] ++
#ifdef HAVE_MMAP
Expand All @@ -35,6 +39,10 @@ regressions = [] ++
let ns = take 20 $ iterate S Z
let hs = map hash ns
hs @=? nub hs
#if WORD_SIZE_IN_BITS == 64
, testCase "64 bit Text" $ do
hash ("hello world" :: Text) @=? 2668910425102664189
#endif
]
where
nullaryCase :: Int -> SumOfNullary -> IO ()
Expand Down