HsHyperEstraier-0.4: Database/QDBM/Cabin/Map.hsc
{-# LANGUAGE
EmptyDataDecls
, ForeignFunctionInterface
, UnicodeSyntax
#-}
module Database.QDBM.Cabin.Map
( Map
, CBMAP
, wrapMap
, unsafePeekMap
, withMapPtr
, newMap
, put
, get
, toList
, fromList
)
where
import Control.Monad.Unicode
import qualified Data.ByteString as Strict (ByteString)
import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc
import Prelude.Unicode
newtype Map = Map (ForeignPtr CBMAP)
data CBMAP
foreign import ccall unsafe "cabin.h cbmapopen"
_open ∷ IO (Ptr CBMAP)
foreign import ccall unsafe "cabin.h &cbmapclose"
_close ∷ FunPtr (Ptr CBMAP → IO ())
foreign import ccall unsafe "cabin.h cbmapput"
_put ∷ Ptr CBMAP → Ptr CChar → CInt → Ptr CChar → CInt → CInt → IO CInt
foreign import ccall unsafe "cabin.h cbmapget"
_get ∷ Ptr CBMAP → Ptr CChar → CInt → Ptr CInt → IO (Ptr CChar)
foreign import ccall unsafe "cabin.h cbmapiterinit"
_iterinit ∷ Ptr CBMAP → IO ()
foreign import ccall unsafe "cabin.h cbmapiternext"
_iternext ∷ Ptr CBMAP → Ptr CInt → IO (Ptr CChar)
foreign import ccall unsafe "cabin.h cbmapiterval"
_iterval ∷ Ptr CChar → Ptr CInt → IO (Ptr CChar)
wrapMap ∷ Ptr CBMAP → IO Map
wrapMap = fmap Map ∘ newForeignPtr _close
unsafePeekMap ∷ Ptr CBMAP → IO Map
unsafePeekMap = fmap Map ∘ newForeignPtr_
withMapPtr ∷ Map → (Ptr CBMAP → IO a) → IO a
withMapPtr (Map m) = withForeignPtr m
newMap ∷ IO Map
newMap = _open ≫= wrapMap
put ∷ Map → Strict.ByteString → Strict.ByteString → Bool → IO Bool
put m key value overwrite
= withMapPtr m $ \ mapPtr →
C8.useAsCStringLen key $ \ (keyPtr , keyLen ) →
C8.useAsCStringLen value $ \ (valuePtr, valueLen) →
fmap (/= 0) (_put mapPtr
keyPtr (fromIntegral keyLen )
valuePtr (fromIntegral valueLen)
(fromIntegral $ fromEnum overwrite))
get ∷ Map → Strict.ByteString → IO (Maybe Strict.ByteString)
get m key
= withMapPtr m $ \ mapPtr →
C8.useAsCStringLen key $ \ (keyPtr, keyLen) →
alloca $ \ valLenPtr →
do valPtr ← _get mapPtr keyPtr (fromIntegral keyLen) valLenPtr
if valPtr ≡ nullPtr then
return Nothing
else
do valLen ← peek valLenPtr
value ← C8.packCStringLen (valPtr, fromIntegral valLen)
return $ Just value
initIterator ∷ Map → IO ()
initIterator m
= withMapPtr m _iterinit
iterateNext ∷ Map → IO (Maybe (Strict.ByteString, Strict.ByteString))
iterateNext m
= withMapPtr m $ \ mapPtr →
alloca $ \ keyLenPtr →
alloca $ \ valLenPtr →
do keyPtr ← _iternext mapPtr keyLenPtr
if keyPtr ≡ nullPtr then
return Nothing
else
do keyLen ← peek keyLenPtr
key ← C8.packCStringLen (keyPtr, fromIntegral keyLen)
valPtr ← _iterval keyPtr valLenPtr
valLen ← peek valLenPtr
value ← C8.packCStringLen (valPtr, fromIntegral valLen)
return $ Just (key, value)
-- Internal state of the iterator is stored in the Map itself. That's
-- not thread-safe. So we can't iterate it lazily.
toList ∷ Map → IO [(Strict.ByteString, Strict.ByteString)]
toList m = initIterator m≫ loop
where
loop ∷ IO [(Strict.ByteString, Strict.ByteString)]
loop = do next ← iterateNext m
case next of
Nothing → return []
Just pair → do -- We want to do unsafeInterleaveIO
-- here, but we can't.
rest ← loop
return $ pair : rest
fromList ∷ [(Strict.ByteString, Strict.ByteString)] → IO Map
fromList pairs
= do m ← newMap
mapM_ (putPair m) pairs
return m
where
putPair ∷ Map → (Strict.ByteString, Strict.ByteString) → IO ()
putPair m (key, value) = put m key value True ≫ return ()