libmdbx-0.2.0.0: src/Mdbx/FFI.chs
{-|
Module : Mdbx.FFI
Copyright : (c) 2021 Francisco Vallarino
License : BSD-3-Clause (see the LICENSE file)
Maintainer : fjvallarino@gmail.com
Stability : experimental
Portability : non-portable
Low level bindings to libmdbx functions.
-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Mdbx.FFI (
-- * Environment
MdbxEnvFlags(..),
MdbxEnv(..),
MdbxEnvMode(..),
mdbx_env_create,
mdbx_env_set_geometry,
mdbx_env_open,
mdbx_env_close,
-- * Transaction
MdbxTxnFlags(..),
MdbxTxn(..),
mdbx_txn_begin,
mdbx_txn_commit,
mdbx_txn_abort,
mdbx_txn_env,
-- * Database
MdbxDbFlags(..),
MdbxDbi(..),
mdbx_dbi_open,
mdbx_dbi_close,
-- * Data
MdbxVal(..),
emptyMdbxVal,
-- * CRUD
MdbxPutFlags(..),
mdbx_put,
mdbx_get,
mdbx_del,
-- * Cursor
MdbxCursorOp(..),
MdbxCursor(..),
mdbx_cursor_open,
mdbx_cursor_close,
mdbx_cursor_del,
mdbx_cursor_get,
mdbx_cursor_put,
mdbx_cursor_count,
mdbx_strerror,
mdbx_cmp,
-- * Error
MdbxError(..),
-- * Conversion for types used in keys
mdbx_key_from_float,
mdbx_key_from_double,
mdbx_key_from_int32,
mdbx_key_from_int64,
mdbx_float_from_key,
mdbx_double_from_key,
mdbx_int32_from_key,
mdbx_int64_from_key
) where
import GHC.Float (castWord32ToFloat, castWord64ToDouble)
import Foreign
import Foreign.C
#include "mdbx.h"
-- Common
{# typedef size_t CSize #}
-- | Error codes for the different operations.
{# enum MDBX_error_t as MdbxError {underscoreToCase} deriving (Show, Eq, Ord) #}
-- Environment
-- | Environment object, needed for all the operations.
{# pointer *MDBX_env as MdbxEnv newtype #}
deriving instance Storable MdbxEnv
-- | Flags for opening an environment.
data MdbxEnvFlags
= MdbxEnvDefaults
| MdbxSyncDurable
| MdbxNosubdir
| MdbxSafeNosync
| MdbxMapasync
| MdbxRdonly
| MdbxNometasync
| MdbxWritemap
| MdbxUtterlyNosync
| MdbxNotls
| MdbxExclusive
| MdbxNordahead
| MdbxNomeminit
| MdbxCoalesce
| MdbxLiforeclaim
| MdbxPageperturb
| MdbxAccede
deriving (Show, Eq, Ord)
--{# enum MDBX_env_flags_t as MdbxEnvFlags {underscoreToCase} deriving (Show, Eq, Ord) #}
{# enum MDBX_env_flags_t as MdbxEnvFlags nocode {underscoreToCase} #}
-- | UNIX permissions to set on created files. Zero value means to open existing, but do not create.
type MdbxEnvMode = {# type mdbx_mode_t #}
{# typedef mdbx_mode_t MdbxEnvMode #}
-- Opening and closing.
-- | Creates an environment. Represents a database in the file system.
{# fun unsafe mdbx_env_create {alloca- `MdbxEnv' peek*} -> `Int' #}
{--
Haddock ignores these:
- size_lower: Minimum size of the database in bytes.
- size_now: Current size of the database in bytes.
- size_upper: Maximum size of the database in bytes.
- growth_step: Step growth size of the database in bytes. Must be greater than
zero to allow for growth.
- shrink_threshold: Step shrink size of the database in bytes. Must be greater
than zero to allow for shrinkage and lower than growth_step to avoid shrinking
after growth.
- pagesize: Page size of the database in bytes.
--}
{-|
Sets geometry of an environment. All the parameters can receive -1 to keep the
current value. Receives (expressed in bytes): size_lower, size_now, size_upper,
growth_step, shrink_threshold, pagesize.
-}
{# fun unsafe mdbx_env_set_geometry {`MdbxEnv', `Int', `Int', `Int', `Int', `Int', `Int'} -> `Int' #}
-- | Opens an environment. Receives name, flags and mode.
{# fun unsafe mdbx_env_open {`MdbxEnv', `String', bitMask`[MdbxEnvFlags]', `MdbxEnvMode'} -> `Int' #}
-- | Closes an environment.
{# fun unsafe mdbx_env_close {`MdbxEnv'} -> `Int' #}
{-- Closes an env (env, dontSync) --}
-- Fails under ghci
-- {# fun unsafe mdbx_env_close_ex {`MdbxEnv', `Bool'} -> `Int' #}
-- Transaction
-- | Transaction instance. Needed for all operations with data, even reading.
{# pointer *MDBX_txn as MdbxTxn newtype #}
deriving instance Storable MdbxTxn
-- | Flags for a transaction.
data MdbxTxnFlags
= MdbxTxnReadwrite
| MdbxTxnNosync
| MdbxTxnRdonly
| MdbxTxnNometasync
| MdbxTxnRdonlyPrepare
| MdbxTxnTry
deriving (Show, Eq, Ord)
-- {# enum MDBX_txn_flags_t as MdbxTxnFlags {underscoreToCase} deriving (Show, Eq, Ord) #}
{# enum MDBX_txn_flags_t as MdbxTxnFlags nocode {underscoreToCase} #}
{-|
Begins a new transaction.
Arguments:
- Environment.
- Parent transaction, or Nothing.
- Flags.
-}
{# fun unsafe mdbx_txn_begin {`MdbxEnv', maybeTxn* `Maybe MdbxTxn', bitMask`[MdbxTxnFlags]', alloca- `MdbxTxn' peek*} -> `Int' #}
-- | Commits a transaction.
{# fun unsafe mdbx_txn_commit {`MdbxTxn'} -> `Int' #}
-- | Aborts a transaction.
{# fun unsafe mdbx_txn_abort {`MdbxTxn'} -> `Int' #}
-- | Gets the environment from a transaction.
{# fun unsafe mdbx_txn_env {`MdbxTxn'} -> `MdbxEnv' #}
-- MdbxEnv* mdbx_txn_env ( const MdbxTxn * txn )
-- Database
-- | Database instance. Represents a logical table in the database.
type MdbxDbi = {# type MDBX_dbi #}
{# typedef MDBX_dbi MdbxDbi #}
-- | Flags for a database.
data MdbxDbFlags
= MdbxDbDefaults
| MdbxReversekey
| MdbxDupsort
| MdbxIntegerkey
| MdbxDupfixed
| MdbxIntegerdup
| MdbxReversedup
| MdbxCreate
| MdbxDbAccede
deriving (Show, Eq, Ord)
--{# enum MDBX_db_flags_t as MdbxDbFlags {underscoreToCase} deriving (Show, Eq, Ord) #}
{# enum MDBX_db_flags_t as MdbxDbFlags nocode {underscoreToCase} #}
{-|
Opens a database.
Arguments:
- Transaction.
- Name.
- Flags.
-}
{# fun unsafe mdbx_dbi_open {`MdbxTxn', maybeString* `Maybe String', bitMask`[MdbxDbFlags]', alloca- `MdbxDbi' peek*} -> `Int' #}
-- | Closes a database.
{# fun unsafe mdbx_dbi_close {`MdbxEnv', `MdbxDbi'} -> `Int' #}
-- CRUD
-- | Binary blob representing a key or value in the database.
data MdbxVal = MdbxVal {
mvlSize :: {-# UNPACK #-} !{# type size_t #},
mvlData :: {-# UNPACK #-} !(Ptr ())
} deriving (Eq, Show)
--instance Show MdbxVal where
-- show (MdbxVal sz dt) = "MdbxVal { size = " ++ show sz ++ ", data = " ++ show dt ++ " }"
instance Storable MdbxVal where
sizeOf _ = {# sizeof MDBX_val #}
alignment _ = {# alignof MDBX_val #}
peek ptr = do
sz <- {# get MDBX_val->iov_len #} ptr
pd <- {# get MDBX_val->iov_base #} ptr
return $! MdbxVal sz pd
poke ptr (MdbxVal sz pd) = do
{# set MDBX_val->iov_len #} ptr sz
{# set MDBX_val->iov_base #} ptr pd
-- | Sample empty value
emptyMdbxVal :: MdbxVal
emptyMdbxVal = MdbxVal 0 nullPtr
{# pointer *MDBX_val as MdbxValPtr -> MdbxVal #}
-- | Flags for all data related operations.
data MdbxPutFlags
= MdbxUpsert
| MdbxNooverwrite
| MdbxNodupdata
| MdbxCurrent
| MdbxAlldups
| MdbxReserve
| MdbxAppend
| MdbxAppenddup
| MdbxMultiple
deriving (Show, Eq, Ord)
--{# enum MDBX_put_flags_t as MdbxPutFlags {underscoreToCase} deriving (Show, Eq, Ord) #}
{# enum MDBX_put_flags_t as MdbxPutFlags nocode {underscoreToCase} #}
{-|
Stores a key/value pair.
Arguments:
- Transaction.
- Database.
- Key.
- Value.
-}
{# fun unsafe mdbx_put {`MdbxTxn', `MdbxDbi', with* `MdbxVal', with* `MdbxVal', bitMask`[MdbxPutFlags]'} -> `Int' #}
{-|
Gets a value with the given key.
Arguments:
- Transaction.
- Database.
- Key.
-}
{# fun unsafe mdbx_get {`MdbxTxn', `MdbxDbi', with* `MdbxVal', alloca- `MdbxVal' peek*} -> `Int' #}
{-|
Gets a value with the given key.
Arguments:
- Transaction.
- Database.
- Key.
-}
{# fun unsafe mdbx_del {`MdbxTxn', `MdbxDbi', with* `MdbxVal', withMaybe* `Maybe MdbxVal'} -> `Int' #}
-- Cursor
-- | Cursor instance. Used for efficient navigation in a database.
{# pointer *MDBX_cursor as MdbxCursor newtype #}
deriving instance Storable MdbxCursor
-- | Flags for cursor operations.
data MdbxCursorOp
= MdbxFirst
| MdbxFirstDup
| MdbxGetBoth
| MdbxGetBothRange
| MdbxGetCurrent
| MdbxGetMultiple
| MdbxLast
| MdbxLastDup
| MdbxNext
| MdbxNextDup
| MdbxNextMultiple
| MdbxNextNodup
| MdbxPrev
| MdbxPrevDup
| MdbxPrevNodup
| MdbxSet
| MdbxSetKey
| MdbxSetRange
| MdbxPrevMultiple
| MdbxSetLowerbound
deriving (Show, Eq, Ord)
--{# enum MDBX_cursor_op as MdbxCursorOp {underscoreToCase} deriving (Show, Eq, Ord) #}
{# enum MDBX_cursor_op as MdbxCursorOp nocode {underscoreToCase} #}
{-|
Opens a new cursor.
Arguments:
- Transaction.
- Database.
-}
{# fun unsafe mdbx_cursor_open {`MdbxTxn', `MdbxDbi', alloca- `MdbxCursor' peek*} -> `Int' #}
-- | Closes a cursor.
{# fun unsafe mdbx_cursor_close {`MdbxCursor'} -> `()' #}
-- | Removes the current key/value pair.
{# fun unsafe mdbx_cursor_del {`MdbxCursor', bitMask`[MdbxPutFlags]'} -> `Int' #}
-- | Returns the current key/value pair.
{# fun unsafe mdbx_cursor_get {`MdbxCursor', with* `MdbxVal' peek*, alloca- `MdbxVal' peek*, `MdbxCursorOp'} -> `Int' #}
{-| Sets the value for the current key.
Arguments:
- Cursor.
- Key.
- Value.
- FLags.
-}
{# fun unsafe mdbx_cursor_put {`MdbxCursor', with* `MdbxVal', with* `MdbxVal', bitMask`[MdbxPutFlags]'} -> `Int' #}
-- | Returns the count of duplicates in the current key.
{# fun unsafe mdbx_cursor_count {`MdbxCursor', alloca- `CSize' peek*} -> `Int' #}
-- Helpers
-- | Returns the description of a given error number.
{# fun unsafe mdbx_strerror {`Int'} -> `String' #}
-- | Compares two values as a binary blob.
{# fun unsafe mdbx_cmp {`MdbxTxn', `MdbxDbi', with* `MdbxVal', with* `MdbxVal'} -> `Int' #}
bitMask :: Enum a => [a] -> C2HSImp.CInt
bitMask = foldl (.|.) 0 . fmap (fromIntegral . fromEnum)
maybeString :: Maybe String -> (Ptr CChar -> IO c) -> IO c
maybeString Nothing fn = fn nullPtr
maybeString (Just val) fn = withCString val fn
maybeTxn :: Maybe MdbxTxn -> (MdbxTxn -> IO c) -> IO c
maybeTxn Nothing fn = fn (MdbxTxn nullPtr)
maybeTxn (Just val) fn = fn val
withMaybe :: Storable a => Maybe a -> (Ptr a -> IO c) -> IO c
withMaybe Nothing fn = fn nullPtr
withMaybe (Just val) fn = with val fn
-- Conversion for types used in keys
-- | Converts a Float value to an unsigned Word that can be used by libmdbx's compare function.
{# fun pure mdbx_key_from_float {`Float'} -> `Word32' #}
-- | Converts a Double value to an unsigned Word that can be used by libmdbx's compare function.
{# fun pure mdbx_key_from_double {`Double'} -> `Word64' #}
-- | Converts a 32bits signed Int value to an unsigned Word that can be used by libmdbx's compare function.
{# fun pure mdbx_key_from_int32 {`Int32'} -> `Word32' #}
-- | Converts a 64bits signed Int value to an unsigned Word that can be used by libmdbx's compare function.
{# fun pure mdbx_key_from_int64 {`Int64'} -> `Word64' #}
-- These functions are re-implemented locally because they are not exported by libmdbx.
-- | Converts an unsigned Word to a Float value.
mdbx_float_from_key :: Word32 -> Float
mdbx_float_from_key val = value where
intVal :: Int32 = fromIntegral val
value
| intVal < 0 = castWord32ToFloat $ val + 0x80000000
| otherwise = castWord32ToFloat $ 0xffffFFFF - val
-- | Converts an unsigned Word to a Double value.
mdbx_double_from_key :: Word64 -> Double
mdbx_double_from_key val = value where
intVal :: Int64 = fromIntegral val
value
| intVal < 0 = castWord64ToDouble $ val + 0x8000000000000000
| otherwise = castWord64ToDouble $ 0xffffFFFFffffFFFF - val
-- | Converts an unsigned Word value to a 32bits signed Int.
mdbx_int32_from_key :: Word32 -> Int32
mdbx_int32_from_key val = fromIntegral $ val - 0x80000000;
-- | Converts an unsigned Word value to a 64bits signed Int.
mdbx_int64_from_key :: Word64 -> Int64
mdbx_int64_from_key val = fromIntegral $ val - 0x8000000000000000;