packages feed

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 ()