module Database.PureCDB.Internal where
import System.IO
import qualified Data.Vector as V
import Data.Word
import qualified Data.ByteString as B
import Data.Bits
import qualified Data.IntMap as I
import Data.List (foldl')
data TOCHash = TOCHash { hPosition :: Word32, hLength :: Word32 } deriving (Show)
data ReadCDB = ReadCDB { rHandle :: Handle, rTOC :: V.Vector TOCHash }
cdbHash :: B.ByteString -> Word32
cdbHash = B.foldl' go 5381 where go h c = ((h `shiftL` 5) + h) `xor` (fromIntegral c)
tocIndex :: Integral a => a -> Int
tocIndex h = fromIntegral $ h `mod` 256
tocFind :: ReadCDB -> B.ByteString -> (TOCHash, Word32)
tocFind (ReadCDB _ vec) bs = (vec V.! tocIndex h, h) where h = cdbHash bs
layoutHash :: Int -> I.IntMap [a] -> Maybe [(Int, a)]
layoutHash sz = test . concat . foldl' go [] . I.toList where
go l@(((li, _):_):_) p = (one li p):l
go [] p = [one (1) p]
go ([]:_) _ = error "layoutHash: found empty list"
one lastIdx (idx, as) = reverse $ zip [ (max idx $ lastIdx + 1) .. ] as
test [] = Just []
test l@(x:_) = if fst x >= sz then Nothing else Just l
coalesceHash :: Integral a => a -> [(a, b)] -> I.IntMap [(a, b)]
coalesceHash hlen = foldl' go I.empty where
go im (a, b) = I.insertWith (++) (fromIntegral $ hashSlot a hlen) [(a, b)] im
createHashVector :: Integral a => a -> (a, b) -> [(a, b)] -> V.Vector (a, b)
createHashVector sz start l = maybe (createHashVector next start l) vec hsh
where vec ups = V.replicate (fromIntegral sz) start V.// ups
hsh = layoutHash (fromIntegral sz) $ coalesceHash sz l
next = 1 + sz + (sz `div` 3)
hashSlot :: Integral a => a -> a -> a
hashSlot h hlen = (h `div` 256) `mod` hlen