module Database.LevelDB.Iterator
( Iterator
, createIter
, iterEntry
, iterFirst
, iterGetError
, iterItems
, iterKey
, iterKeys
, iterLast
, iterNext
, iterPrev
, iterSeek
, iterValid
, iterValue
, iterValues
, mapIter
, releaseIter
, withIter
)
where
import Control.Applicative ((<$>), (<*>))
import Control.Exception (bracket, finally, onException)
import Control.Monad (when)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Data.ByteString (ByteString)
import Data.Maybe (catMaybes)
import Foreign
import Foreign.C.Error (throwErrnoIfNull)
import Foreign.C.String (CString, peekCString)
import Foreign.C.Types (CSize)
import Database.LevelDB.C
import Database.LevelDB.Internal
import Database.LevelDB.Types
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Unsafe as BU
data Iterator = Iterator !IteratorPtr !ReadOptionsPtr deriving (Eq)
createIter :: MonadIO m => DB -> ReadOptions -> m Iterator
createIter (DB db_ptr _) opts = liftIO $ do
opts_ptr <- mkCReadOpts opts
flip onException (freeCReadOpts opts_ptr) $ do
iter_ptr <- throwErrnoIfNull "create_iterator" $
c_leveldb_create_iterator db_ptr opts_ptr
return $ Iterator iter_ptr opts_ptr
releaseIter :: MonadIO m => Iterator -> m ()
releaseIter (Iterator iter_ptr opts) = liftIO $
c_leveldb_iter_destroy iter_ptr `finally` freeCReadOpts opts
withIter :: MonadIO m => DB -> ReadOptions -> (Iterator -> IO a) -> m a
withIter db opts = liftIO . bracket (createIter db opts) releaseIter
iterValid :: MonadIO m => Iterator -> m Bool
iterValid (Iterator iter_ptr _) = liftIO $ do
x <- c_leveldb_iter_valid iter_ptr
return (x /= 0)
iterSeek :: MonadIO m => Iterator -> ByteString -> m ()
iterSeek (Iterator iter_ptr _) key = liftIO $
BU.unsafeUseAsCStringLen key $ \(key_ptr, klen) ->
c_leveldb_iter_seek iter_ptr key_ptr (intToCSize klen)
iterFirst :: MonadIO m => Iterator -> m ()
iterFirst (Iterator iter_ptr _) = liftIO $ c_leveldb_iter_seek_to_first iter_ptr
iterLast :: MonadIO m => Iterator -> m ()
iterLast (Iterator iter_ptr _) = liftIO $ c_leveldb_iter_seek_to_last iter_ptr
iterNext :: MonadIO m => Iterator -> m ()
iterNext (Iterator iter_ptr _) = liftIO $ do
valid <- c_leveldb_iter_valid iter_ptr
when (valid /= 0) $ c_leveldb_iter_next iter_ptr
iterPrev :: MonadIO m => Iterator -> m ()
iterPrev (Iterator iter_ptr _) = liftIO $ do
valid <- c_leveldb_iter_valid iter_ptr
when (valid /= 0) $ c_leveldb_iter_prev iter_ptr
iterKey :: MonadIO m => Iterator -> m (Maybe ByteString)
iterKey = liftIO . flip iterString c_leveldb_iter_key
iterValue :: MonadIO m => Iterator -> m (Maybe ByteString)
iterValue = liftIO . flip iterString c_leveldb_iter_value
iterEntry :: MonadIO m => Iterator -> m (Maybe (ByteString, ByteString))
iterEntry iter = liftIO $ do
mkey <- iterKey iter
mval <- iterValue iter
return $ (,) <$> mkey <*> mval
iterGetError :: MonadIO m => Iterator -> m (Maybe ByteString)
iterGetError (Iterator iter_ptr _) = liftIO $
alloca $ \err_ptr -> do
poke err_ptr nullPtr
c_leveldb_iter_get_error iter_ptr err_ptr
erra <- peek err_ptr
if erra == nullPtr
then return Nothing
else do
err <- peekCString erra
return . Just . BC.pack $ err
mapIter :: MonadIO m => (Iterator -> m a) -> Iterator -> m [a]
mapIter f iter@(Iterator iter_ptr _) = go []
where
go acc = do
valid <- liftIO $ c_leveldb_iter_valid iter_ptr
if valid == 0
then return acc
else do
val <- f iter
() <- liftIO $ c_leveldb_iter_next iter_ptr
go (val : acc)
iterItems :: (Functor m, MonadIO m) => Iterator -> m [(ByteString, ByteString)]
iterItems iter = catMaybes <$> mapIter iterEntry iter
iterKeys :: (Functor m, MonadIO m) => Iterator -> m [ByteString]
iterKeys iter = catMaybes <$> mapIter iterKey iter
iterValues :: (Functor m, MonadIO m) => Iterator -> m [ByteString]
iterValues iter = catMaybes <$> mapIter iterValue iter
iterString :: Iterator
-> (IteratorPtr -> Ptr CSize -> IO CString)
-> IO (Maybe ByteString)
iterString (Iterator iter_ptr _) f = do
valid <- c_leveldb_iter_valid iter_ptr
if valid == 0
then return Nothing
else alloca $ \len_ptr -> do
ptr <- f iter_ptr len_ptr
if ptr == nullPtr
then return Nothing
else do
len <- peek len_ptr
Just <$> BS.packCStringLen (ptr, cSizeToInt len)