+{-# OPTIONS -fno-implicit-prelude #-}
+
-----------------------------------------------------------------------------
-- |
-- Module : Data.HashTable
-- * Hash functions
-- $hash_functions
hashInt, hashString,
+ prime,
-- * Diagnostics
longestChain
) where
-import Data.Char ( ord )
-import Data.Int ( Int32 )
-import Data.Array.IO
-import Data.Array.Base
-import Data.List ( maximumBy )
-import Data.IORef
+-- This module is imported by Data.Dynamic, which is pretty low down in the
+-- module hierarchy, so don't import "high-level" modules
+
+#ifdef __GLASGOW_HASKELL__
+import GHC.Base
+#else
+import Prelude hiding ( lookup )
+#endif
+import Data.Tuple ( fst )
import Data.Bits
-import Control.Monad ( when )
-import Prelude hiding (lookup)
---import Debug.Trace
+import Data.Maybe
+import Data.List ( maximumBy, filter, length, concat, foldl )
+import Data.Int ( Int32 )
+
+#if defined(__GLASGOW_HASKELL__)
+import GHC.Num
+import GHC.Real ( Integral(..), fromIntegral )
+
+import GHC.IOBase ( IO, IOArray, newIOArray, readIOArray, writeIOArray,
+ unsafeReadIOArray, unsafeWriteIOArray,
+ IORef, newIORef, readIORef, writeIORef )
+import GHC.Err ( undefined )
+#else
+import Data.Char ( ord )
+import Data.IORef ( IORef, newIORef, readIORef, writeIORef )
+# if defined(__HUGS__)
+import Hugs.IOArray ( IOArray, newIOArray, readIOArray, writeIOArray,
+ unsafeReadIOArray, unsafeWriteIOArray )
+# elif defined(__NHC__)
+import NHC.IOExtras ( IOArray, newIOArray, readIOArray, writeIOArray)
+# endif
+#endif
+import Control.Monad ( when, mapM, sequence_ )
+
+-----------------------------------------------------------------------
myReadArray :: IOArray Int32 a -> Int32 -> IO a
myWriteArray :: IOArray Int32 a -> Int32 -> a -> IO ()
-#ifdef DEBUG
-myReadArray = readArray
-myWriteArray = writeArray
+#if defined(DEBUG) || defined(__NHC__)
+myReadArray = readIOArray
+myWriteArray = writeIOArray
#else
-myReadArray arr i = unsafeRead arr (fromIntegral i)
-myWriteArray arr i x = unsafeWrite arr (fromIntegral i) x
+myReadArray arr i = unsafeReadIOArray arr (fromIntegral i)
+myWriteArray arr i x = unsafeWriteIOArray arr (fromIntegral i) x
#endif
-- | A hash table mapping keys of type @key@ to values of type @val@.
-- which seems to give reasonable results.
--
hashString :: String -> Int32
-hashString = fromIntegral . foldr f 0
- where f c m = ord c + (m * 128) `rem` fromIntegral prime
+hashString = fromIntegral . foldl f 0
+ where f m c = ord c + (m * 128) `rem` fromIntegral prime
--- a prime larger than the maximum hash table size
-prime = 1500007 :: Int32
+-- | A prime larger than the maximum hash table size
+prime :: Int32
+prime = 1500007
-- -----------------------------------------------------------------------------
-- Parameters
-- -----------------------------------------------------------------------------
-- Creating a new hash table
--- | Creates a new hash table
+-- | Creates a new hash table. The following property should hold for the @eq@
+-- and @hash@ functions passed to 'new':
+--
+-- > eq A B => hash A == hash B
+--
new
- :: (key -> key -> Bool) -- ^ An equality comparison on keys
- -> (key -> Int32) -- ^ A hash function on keys
+ :: (key -> key -> Bool) -- ^ @eq@: An equality comparison on keys
+ -> (key -> Int32) -- ^ @hash@: A hash function on keys
-> IO (HashTable key val) -- ^ Returns: an empty hash table
new cmp hash_fn = do
-- make a new hash table with a single, empty, segment
- dir <- newArray (0,dIR_SIZE) undefined
- segment <- newArray (0,sEGMENT_SIZE-1) []
+ dir <- newIOArray (0,dIR_SIZE) undefined
+ segment <- newIOArray (0,sEGMENT_SIZE-1) []
myWriteArray dir 0 segment
let
then expandHashTable table1
else return table1
writeIORef ref table2
- (segment_index,segment_offset) <- tableLocation table key
+ (segment_index,segment_offset) <- tableLocation table2 key
segment <- myReadArray dir segment_index
bucket <- myReadArray segment segment_offset
myWriteArray segment segment_offset ((key,val):bucket)
table@HT{ dir=dir,
split=split,
max_bucket=max,
+ bcount=bcount,
mask2=mask2 } = do
let
oldsegment = split `shiftR` sEGMENT_SHIFT
newindex = newbucket .&. sEGMENT_MASK
--
when (newindex == 0) $
- do segment <- newArray (0,sEGMENT_SIZE-1) []
+ do segment <- newIOArray (0,sEGMENT_SIZE-1) []
myWriteArray dir newsegment segment
--
let table' =
if (split+1) < max
- then table{ split = split+1 }
+ then table{ split = split+1,
+ bcount = bcount+1 }
-- we've expanded all the buckets in this table, so start from
-- the beginning again.
else table{ split = 0,
+ bcount = bcount+1,
max_bucket = max * 2,
mask1 = mask2,
mask2 = mask2 `shiftL` 1 .|. 1 }