    module Database.TokyoCabinet.Map
        (
          new
        , new2
        , dup
        , delete
        , put
        , putkeep
        , putcat
        , out
        , get
        , move
        , iterinit
        , iternext
        , rnum
        , msiz
        , keys
        , vals
        , addint
        , adddouble
        , clear
        , cutfront
        , dump
        , load
        , Map
        ) where

    import Database.TokyoCabinet.Map.C
    import Database.TokyoCabinet.Storable
    import Database.TokyoCabinet.Sequence
    import Database.TokyoCabinet.Internal
    import Database.TokyoCabinet.Error (cINT_MIN)

    import Data.Word

    import Foreign.Ptr
    import Foreign.ForeignPtr
    import Foreign.Storable (peek)
    import Foreign.Marshal (alloca, mallocBytes, free)
    import Foreign.Marshal.Utils (maybePeek, copyBytes)

    import Data.ByteString (ByteString)
    import Data.ByteString.Unsafe
        (
          unsafeUseAsCStringLen
        , unsafePackCStringFinalizer
        )

    new :: IO (Map k v)
    new = Map `fmap` (c_tcmapnew >>= newForeignPtr tcmapFinalizer)

    new2 :: Word32 -> IO (Map k v)
    new2 num = Map `fmap` (c_tcmapnew2 num >>= newForeignPtr tcmapFinalizer)

    dup :: Map k v -> IO (Map k v)
    dup m =
        withForeignPtr (unMap m) $ \m' ->
            Map `fmap` (c_tcmapdup m' >>= newForeignPtr tcmapFinalizer)

    delete :: Map k v -> IO ()
    delete m = finalizeForeignPtr (unMap m)

    put :: (Storable k, Storable v) => Map k v -> k -> v -> IO ()
    put = putHelper c_tcmapput unMap

    putkeep :: (Storable k, Storable v) => Map k v -> k -> v -> IO Bool
    putkeep = putHelper c_tcmapputkeep unMap

    putcat :: (Storable k, Storable v) => Map k v -> k -> v -> IO ()
    putcat = putHelper c_tcmapputcat unMap

    out :: (Storable k) => Map k v -> k -> IO Bool
    out = outHelper c_tcmapout unMap

    get :: (Storable k, Storable v) => Map k v -> k -> IO (Maybe v)
    get = getHelper' c_tcmapget unMap

    move :: (Storable k) => Map k v -> k -> Bool -> IO Bool
    move m key hd =
        withForeignPtr (unMap m) $ \m' ->
            withPtrLen key $ \(kbuf, ksiz) ->
                c_tcmapmove m' kbuf ksiz hd

    iterinit :: Map k v -> IO ()
    iterinit m = withForeignPtr (unMap m) c_tcmapiterinit

    iternext :: (Storable k) => Map k v -> IO (Maybe k)
    iternext m =
        withForeignPtr (unMap m) $ \p ->
            alloca $ \sizbuf -> do
                vbuf <- c_tcmapiternext p sizbuf
                flip maybePeek vbuf $ \vp ->
                    do siz <- peek sizbuf
                       buf <- mallocBytes (fromIntegral siz)
                       copyBytes buf vp (fromIntegral siz)
                       peekPtrLen (buf, siz)

    rnum :: Map k v -> IO Word64
    rnum m = withForeignPtr (unMap m) c_tcmaprnum

    msiz :: Map k v -> IO Word64
    msiz m = withForeignPtr (unMap m) c_tcmapmsiz

    keys :: (Storable k) => Map k v -> IO [k]
    keys m = withForeignPtr (unMap m) $ (>>= peekList') . c_tcmapkeys

    vals :: (Storable v) => Map k v -> IO [v]
    vals m = withForeignPtr (unMap m) $ (>>= peekList') . c_tcmapvals

    addint :: (Storable k) => Map k v -> k -> Int -> IO (Maybe Int)
    addint = addHelper c_tcmapaddint unMap fromIntegral fromIntegral (== cINT_MIN)

    adddouble :: (Storable k) => Map k v -> k -> Double -> IO (Maybe Double)
    adddouble = addHelper c_tcmapadddouble unMap realToFrac realToFrac isNaN

    clear :: Map k v -> IO ()
    clear m = withForeignPtr (unMap m) c_tcmapclear

    cutfront :: Map k v -> Int -> IO ()
    cutfront m num = withForeignPtr (unMap m) $ flip c_tcmapcutfront (fromIntegral num)

    dump :: Map k v -> IO ByteString
    dump m =
        withForeignPtr (unMap m) $ \m' ->
            alloca $ \sizbuf -> do
                buf <- c_tcmapdump m' sizbuf
                size <- fromIntegral `fmap` peek sizbuf
                unsafePackCStringFinalizer (castPtr buf) size (free buf)

    load :: ByteString -> IO (Map k v)
    load bytes =
        unsafeUseAsCStringLen bytes $ \(buf, siz) -> do
          m <- c_tcmapload (castPtr buf) (fromIntegral siz)
          Map `fmap` newForeignPtr tcmapFinalizer m