packages feed

croaring-ffi-1.0: src/Data/Bitmap/Roaring/FFI/Memory.hsc

{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE NoFieldSelectors #-}

{-| Raw bindings to @roaring/memory.h@.
It sets the allocator that CRoaring uses for every bitmap, container, iterator and bitset.

The hook is process-wide.
Install it before CRoaring allocates anything.
Never change it while any CRoaring structure is alive.
Otherwise a different allocator frees memory that the old one allocated.

Every hook must be a C function, not a Haskell function wrapped with @foreign import ccall "wrapper"@.
CRoaring allocates inside @unsafe@ foreign calls, which must not re-enter Haskell.
-}
module Data.Bitmap.Roaring.FFI.Memory
  ( Hooks (..)
  , Malloc
  , Realloc
  , Calloc
  , Free
  , AlignedMalloc
  , AlignedFree
  , initMemoryHook
  ) where

#include <roaring/memory.h>

import Foreign.C.Types (CSize)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (FunPtr, Ptr)
import Foreign.Storable (Storable (..))

-- | @roaring_malloc_p@
type Malloc = CSize -> IO (Ptr ())

-- | @roaring_realloc_p@
type Realloc = Ptr () -> CSize -> IO (Ptr ())

-- | @roaring_calloc_p@
type Calloc = CSize -> CSize -> IO (Ptr ())

-- | @roaring_free_p@: must accept and ignore NULL.
type Free = Ptr () -> IO ()

-- | @roaring_aligned_malloc_p@: alignment first, size second.
type AlignedMalloc = CSize -> CSize -> IO (Ptr ())

-- | @roaring_aligned_free_p@: frees blocks from 'AlignedMalloc'. It must accept and ignore NULL.
type AlignedFree = Ptr () -> IO ()

-- | @roaring_memory_t@
data Hooks = Hooks
  { malloc :: FunPtr Malloc
  , realloc :: FunPtr Realloc
  , calloc :: FunPtr Calloc
  , free :: FunPtr Free
  , alignedMalloc :: FunPtr AlignedMalloc
  , alignedFree :: FunPtr AlignedFree
  }
  deriving (Eq, Show)

instance Storable Hooks where
  sizeOf _ = #{size roaring_memory_t}
  {-# INLINE sizeOf #-}
  alignment _ = #{alignment roaring_memory_t}
  {-# INLINE alignment #-}
  peek ptr =
    Hooks
      <$> #{peek roaring_memory_t, malloc} ptr
      <*> #{peek roaring_memory_t, realloc} ptr
      <*> #{peek roaring_memory_t, calloc} ptr
      <*> #{peek roaring_memory_t, free} ptr
      <*> #{peek roaring_memory_t, aligned_malloc} ptr
      <*> #{peek roaring_memory_t, aligned_free} ptr
  {-# INLINE peek #-}
  poke ptr hooks = do
    #{poke roaring_memory_t, malloc} ptr hooks.malloc
    #{poke roaring_memory_t, realloc} ptr hooks.realloc
    #{poke roaring_memory_t, calloc} ptr hooks.calloc
    #{poke roaring_memory_t, free} ptr hooks.free
    #{poke roaring_memory_t, aligned_malloc} ptr hooks.alignedMalloc
    #{poke roaring_memory_t, aligned_free} ptr hooks.alignedFree
  {-# INLINE poke #-}

-- | @roaring_init_memory_hook@: see the module header for when it is safe to call.
initMemoryHook :: Hooks -> IO ()
initMemoryHook hooks =
  alloca $ \ptr -> do
    poke ptr hooks
    initMemoryHookFrom ptr

foreign import capi unsafe "croaring-ffi.h croaring_ffi_init_memory_hook"
  initMemoryHookFrom :: Ptr Hooks -> IO ()