HsHyperEstraier-0.3.2: Database/QDBM/Cabin/Map.hsc
module Database.QDBM.Cabin.Map
( Map
, CBMAP
, wrapMap
, unsafePeekMap
, withMapPtr
, newMap
, put
, get
, toList
, fromList
)
where
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
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 ()